web-dev-qa-db-fra.com

Express - Envoyer une page ET des données personnalisées au navigateur en une seule demande?

Comment simultanément rendre une page et transmettre mes données personnalisées au navigateur. Si j'ai bien compris, il faut envoyer deux couches: une avec un modèle et une autre avec des données JSON. Je veux gérer ces données par colonne vertébrale.

D'après ce que j'ai compris des tutoriels express et bb app, les interactions sont les suivantes:

  1. res.render envoyer une page au navigateur
  2. lorsque document.ready déclenche jQuery.get pour app.get('/post') 
  3. app.get('/post', post.allPosts) envoyer les données à la page

Ceci est trois étapes et comment le faire par un?

var visitCard = {
  name: 'John Smit',
  phone: '+78503569987'
};

exports.index = function(req, res, next){
  res.render('index');
  res.send({data: visitCard}); 
};

Et comment je devrais attraper cette variable sur la page- document.card?

15
khex

J'ai créé ma propre petite fonction middleware qui ajoute une méthode d'assistance appelée renderWithData à l'objet res.

app.use(function (req, res, next) {
    res.renderWithData = function (view, model, data) {
        res.render(view, model, function (err, viewString) {
            data.view = viewString;
            res.json(data);
        }); 
    };
    next();
});

Il prend en compte le nom de la vue, le modèle de la vue et les données personnalisées que vous voulez envoyer au navigateur. Il appelle res.render mais transmet une fonction de rappel. Ceci indique à express de passer le balisage de la vue compilée au rappel sous forme de chaîne au lieu de le canaliser immédiatement dans la réponse. Une fois que j'ai la chaîne de vue, je l'ajoute à l'objet de données en tant que data.view. J'utilise ensuite res.json pour envoyer l'objet de données au navigateur complet avec la vue compilée :)

Modifier:

Un inconvénient avec ce qui précède est que la demande doit être faite avec javascript afin qu'il ne puisse pas s'agir d'une demande de page complète. Vous avez besoin d’une demande initiale pour extraire la page principale qui contient le javascript qui fera la demande ajax.

Ceci est idéal pour les situations dans lesquelles vous essayez de modifier l'URL du navigateur et le titre lorsque l'utilisateur navigue vers une nouvelle page via AJAX. Vous pouvez renvoyer la vue partielle de la nouvelle page au navigateur avec des données pour le titre de la page. Ensuite, votre script côté client peut placer la vue partielle à laquelle il appartient sur la page, mettre à jour la barre de titre de la page et mettre à jour l'URL si nécessaire.

Si vous souhaitez envoyer un document HTML complet au navigateur avec quelques données JavaScript initiales, vous devez compiler ce code JavaScript dans la vue elle-même. C'est certainement possible, mais je n'ai jamais trouvé de solution qui n'implique pas de magie des cordes.

Par exemple:

// controller.js
var someData = { message: 'hi' };
res.render('someView', { data: JSON.stringify(someData) });

// someView.jade
script.
    var someData = !{data};

Remarque: !{data} est utilisé à la place de #{data} car jade remplace HTML par défaut, ce qui transformerait tous les guillemets en espaces réservés ".

Cela a l'air vraiment étrange au début, mais ça marche. En gros, vous prenez un objet JS sur le serveur, vous le transformez en chaîne, vous la restituez dans la vue compilée, puis vous l'envoyez au navigateur. Lorsque le document atteint enfin le navigateur, il devrait ressembler à ceci:

// someSite.com/someView
<script type="text/javascript">
    var someData = { "message": "hi" };
</script>

Espérons que cela a du sens. Si je devais recréer ma méthode d'assistance d'origine pour atténuer les inconvénients de ce second scénario, il se présenterait ainsi:

app.use(function (req, res, next) {
    res.renderWithData = function (view, model, data) {
        model.data = JSON.stringify(data);
        res.render(view, model);
    };
    next();
});

Tout ce que cela fait est de prendre votre objet de données personnalisé, de le structurer pour vous, de l'ajouter au modèle pour la vue, puis de rendre la vue comme d'habitude. Maintenant, vous pouvez appeler res.renderWithData('someView', {}, { message: 'hi' });; vous devez simplement vous assurer que, quelque part dans votre vue, vous récupérez cette chaîne de données et la restituez dans une instruction d'affectation de variable.

html
    head
        title Some Page
        script.
            var data = !{data};

Je ne vais pas vous mentir, tout cela vous semble assez dégoûtant, mais si cela vous évite une visite supplémentaire sur le serveur et que c'est ce que vous recherchez, alors vous devrez le faire. Quelqu'un peut peut-être penser à quelque chose d'un peu plus intelligent, mais je ne vois pas comment vous pouvez obtenir des données déjà présentes dans un document HTML complet rendu pour la première fois.

Edit2:

Voici un exemple de travail: https://c9.io/chevex/test

Vous devez disposer d'un compte Cloud9 (gratuit) pour pouvoir exécuter le projet. Connectez-vous, ouvrez app.js et cliquez sur le bouton d'exécution vert en haut.

9
Chev

Mon approche consiste à envoyer un cookie avec les informations, puis à les utiliser à partir du client.

server.js

const visitCard = {
  name: 'John Smit',
  phone: '+78503569987'
};

router.get('/route', (req, res) => {
  res.cookie('data', JSON.stringify(pollsObj));
  res.render('index');
});

client.js

const getCookie = (name) => {
  const value = "; " + document.cookie;
  const parts = value.split("; " + name + "=");
  if (parts.length === 2) return parts.pop().split(";").shift();
};

const deleteCookie = (name) => {
  document.cookie = name + '=; max-age=0;';
};

const parseObjectFromCookie = (cookie) => {
  const decodedCookie = decodeURIComponent(cookie);
  return JSON.parse(decodedCookie);
};

window.onload = () => {
  let dataCookie = getCookie('data');
  deleteCookie('data');

  if (dataCookie) {
    const data = parseObjectFromCookie(dataCookie);
    // work with data. `data` is equal to `visitCard` from the server

  } else {
    // handle data not found
  }


Procédure pas à pas

À partir du serveur, vous envoyez le cookie avant le rendu de la page. Le cookie est donc disponible lors du chargement de la page.

Ensuite, le client récupère le cookie avec la solution que j'ai trouvée ici et le supprime. Le contenu du cookie est stocké dans notre constante. Si le cookie existe, vous l'analysez en tant qu'objet et vous l'utilisez. Notez qu'à l'intérieur de la parseObjectFromCookie, vous devez d'abord décoder le contenu, puis analyser le JSON avec un objet.

Remarques:

  • Si vous obtenez les données de manière asynchrone, veillez à envoyer le cookie avant le rendu. Sinon, vous obtiendrez une erreur car le res.render() met fin à la réponse. Si la récupération des données prend trop de temps, vous pouvez utiliser une autre solution qui ne tient pas le rendu aussi longtemps. Une alternative pourrait être d'ouvrir un socket à partir du client et d'envoyer les informations que vous déteniez sur le serveur. Voir ici pour cette approche.

  • data n'est probablement pas le meilleur nom pour un cookie, car vous pourriez écraser quelque chose. Utilisez quelque chose de plus significatif pour votre objectif.

  • Je n'ai trouvé cette solution nulle part ailleurs. Je ne sais pas si l'utilisation de cookies n'est pas recommandée pour une raison que je ne connais pas. Je pensais juste que cela pourrait fonctionner et j'ai vérifié, mais je ne l'ai pas utilisé en production.

4
Daniel Reina

Utilisez res.send au lieu de res.render. Il accepte les données brutes sous n'importe quelle forme: une chaîne, un tableau, un ancien objet simple, etc. S'il s'agit d'un objet ou d'un tableau d'objets, il sera sérialisé au format JSON pour vous.

var visitCard = {
  name: 'John Smit',
  phone: '+78503569987'
};

exports.index = function(req, res, next){
  res.send(visitCard}; 
};
3
Brandon

Découvrez Steamer, un module minuscule conçu à cet effet.

https://github.com/rotundasoftware/steamer

2
Brave Dave

Le moyen le plus élégant et le plus simple de le faire est d'utiliser un moteur de rendu (au moins pour la page concernée). Par exemple, utilisez le moteur ejs

node install ejs -s

Sur server.js:

let ejs = require('ejs');    
app.set('view engine', 'ejs');

puis renommez la page index.html souhaitée en index.ejs et déplacez-la dans le répertoire/views. Après cela, vous pouvez créer une API endpoit pour cette page (en utilisant le module mysql):

app.get('/index/:id', function(req, res) { 
    db.query("SELECT * FROM products WHERE id = ?", [req.params.id], (error, results) => {
    if (error) throw error;
        res.render('index', { title: results[0] }); 
    });
});  

Sur le front-end, vous devrez faire une demande GET, par exemple avec Axios ou directement en cliquant sur un lien de la page de modèle index.ejs qui envoie la demande:

<a v-bind:href="'/index/' + co.id">Click</a>

où co.id est la valeur du paramètre de données Vue 'co' que vous souhaitez envoyer avec la demande

0
Danny