web-dev-qa-db-fra.com

Node.js avec Handlebars.js sur le serveur et le client

J'ai une application dans Node.js utilisant Expressjs et Handlebars comme moteur de modèle.

Expressjs utilise des dispositions et rend ensuite les vues. La disposition (layout.hbs) ressemble à ceci:

<!doctype html>
<html lang="en">
    <head>
    </head>
  <body>
    {{{body}}}
  </body>
</html>

Le {{{body}}} est remplacé côté serveur, dans node.js lorsque vous accédez à un itinéraire. Par exemple:

app.get('/', function(req, res){
   res.render('index'})
})

Remplacera le {{{body}}} tag avec le contenu de index.hbs.

Maintenant, côté client, j'utilise Backbone.js et je veux utiliser le guidon pour les vues contrôlées via Backbone. Le problème est que parce que ces pages sont déjà rendues via les guidons, lorsque je tente d'utiliser des guidons (ou des guidons dans les guidons), cela ne fonctionne pas. Il n'y a aucune erreur, il ne remplace simplement pas les balises par des données.

Quelqu'un a-t-il déjà rencontré cela ou a-t-il une idée de solution?

Je vous remercie!

34
dzm

Oui, c'est un problème épineux --- un peu comme les problèmes de citation dans les scripts Shell qui deviennent un nid de rats entre guillemets.

Ma solution consiste à utiliser du jade (à la haml) dans expressjs (côté serveur) pour générer des modèles basés sur le guidon pour le client. De cette façon, le serveur utilise une syntaxe (jade) et le client en utilise une autre (guidon). Je suis au même carrefour que vous, j'ai donc le même défi.

Bien sûr, le jade n'est pas essentiel (bien qu'il soit prêt à l'emploi pour expressjs). Vous pouvez choisir n'importe quel moteur de modèle (non-guidon) pour le serveur, et/ou vous pouvez utiliser des guidons sur le serveur avec vos modèles non-guidons sur le client --- tant que les deux syntaxes des moteurs de modèles choisis ne le font pas. entrer en collision. Puisque j'utilise emberjs sur le client et qu'il utilise la syntaxe du guidon (par défaut), je préfère utiliser la syntaxe emberjs + guidon sur le client. Ainsi, expressjs + jade est devenu un choix naturel pour le serveur.

14
occam

Vous devez utiliser des modèles de client précompilés. Ils s'exécutent plus rapidement et vous permettent d'utiliser le même langage de modèle sur le serveur et le client.

  1. Installez le guidon à l'échelle mondiale npm install handlebars -g
  2. Précompilez vos modèles handlebars client-template1.handlebars -f templates.js
  3. Incluez templates.js <script src="templates.js"></script>
  4. Exécutez le modèle var html = Handlebars.templates["client-template1"](context);

https://stackoverflow.com/a/13884587/836

73
Zachary Yates

Un moyen simple de le faire consiste à simplement ajouter un \ avant le {{ dans un fichier de guidon. Par exemple:

<script type="text/x-template" id="todo-item-template">
<div class="todo-view">
    <input type="checkbox" class="todo-checkbox" \{{checked}}>
    <span class="todo-content" tabindex="0">\{{text}}</span>
</div>

<div class="todo-edit">
    <input type="text" class="todo-input" value="\{{text}}">
</div>

<a href="#" class="todo-remove" title="Remove this task">
    <span class="todo-remove-icon"></span>
</a>

Le code ci-dessus sera rendu sur le client avec les balises {{..}} préservées.

30
Tilo Mitra

Auto-promotion sans vergogne!

Je voulais faire cette même chose de partage client/serveur, j'ai donc écrit un petit paquet npm pour aider:

nœud-guidon-précompilateur

Je l'ai fouetté en quelques heures sur la base du compilateur en ligne de commande dans le référentiel de guidons de wycats. Ce n'est pas le meilleur code au monde, mais il fait très bien le travail pour moi.

EDIT: Je ne maintiens plus ce package. Si vous souhaitez prendre le relais, veuillez me contacter via Github. J'utilise principalement des modèles Jade maintenant, donc cela n'a pas de sens pour moi de continuer en tant que mainteneur.

11
Joel Wietelmann

J'ai travaillé autour de cela en passant des modèles côté client via des modèles côté serveur.

Donc, côté serveur, lisez tous vos modèles côté client dans un tableau et passez-le à votre fonction de rendu côté serveur

Dans votre gestionnaire d'itinéraire, faites quelque chose comme:

readTemplates(function(err, clientTemplates) {
  res.render("page", {
    clientTemplates: clientTemplates;   
  });
});

Et puis dans layout.hbs:

{{#each clientTemplates}}
<script type="text/handlebars id="{{this.filename}}" >
{{{this.template}}}
</script>
{{/each}}

Ici, j'utilise des noms de fichiers sans extensions comme identifiant de modèle afin qu'ils puissent être référencés à partir des vues Backbone. Oh, et n'oubliez pas d'implémenter la mise en cache pour le mode production.

Ouais, ça craint.

Je pense que nous devrions écrire un assistant Handlebars/Express/Connect pour cela.

4
Epeli

Vous avez 2 options. La seconde est LA meilleure façon de procéder:

1) Échapper aux moustaches

<script type="text/x-handlebars" data-hbs="example">
  <p>\{{name}}</p>
</script>

2) Précompilation

Cela compilera le modèle sur le serveur avant qu'il ne soit envoyé au client. Cela rendra le modèle prêt à l'emploi et réduira la charge du navigateur.

1
jordanb

Je n'ai pas aimé la solution de précompilation (car je veux définir des modèles dans le même fichier où je vais les utiliser) ni la naïve \{{ solution d'échappement (car elle a besoin du compilateur Handlebars complet et de plus de code javascript), donc j'ai trouvé une solution hybride qui utilise les aides de Handlebars:

1) Enregistrez un nouvel assistant appelé "modèle" sur la configuration du serveur

var hbs = require('hbs');
hbs.registerHelper("template", function(key, options){
    var source = options.fn().replace("\\{{", "{{");
    var ret =
    '<script>\n' + 
        key + ' = function(opt){\n' +
            'return Handlebars.template(' + hbs.handlebars.precompile(source) + ')(opt);\n' +
        '}\n' + 
    '</script>';
    return ret;
});


2) Utilisez-le n'importe où dans votre page Web côté client (avec \{{ escape pour les paramètres côté client)

{{#template "myTemplate"}}
    <div>
        <p>Hello \{{this.name}}!</p>
    </div>
{{/template}}

(le serveur le précompilera dans quelque chose comme ça)

<script>
    myTemplate = function(opt){
        return Handlebars.template(/* HBS PRECOMPILATED FUNCTION */)(opt);
    }
</script>


3) Appelez simplement la fonction là où vous en avez besoin en javascript côté client

var generatedHtml = myTemplate("world");   // = <div><p>Hello world!</p></div>
$("#myDiv").html(generatedHtml);           // or whatever
1
Oneiros