web-dev-qa-db-fra.com

Accès au contexte parent dans les modèles Meteor et les aides de modèles

Je suis dans une situation de modèle que j'ai du mal à trouver un moyen de contourner.


Voici le modèle en question:

{{#each votes}}
    <h3>{{question}}</h3>

    <ul>
        {{#each participants}}
            <li>
                <p>{{email}}</p>
                <select name="option-select">
                    {{#each ../options}}
                    <option value="{{option}}" class="{{is_selected_option}}">{{option}}</option>
                    {{/each}}
                </select>
            </li>
        {{/each}}
    </ul>
</div>
{{/each}}


Et voici un exemple de document de vote:

{
    _id: '32093ufsdj90j234',
    question: 'What is the best food of all time?'
    options: [
        'Pizza',
        'Tacos',
        'Salad',
        'Thai'
    ],
    participants: [
        {
            id: '2f537a74-3ce0-47b3-80fc-97a4189b2c15'
            vote: 0
        },
        {
            id: '8bffafa7-8736-4c4b-968e-82900b82c266'
            vote: 1
        }
    ]
}


Et voici le problème ...

Lorsque le modèle passe dans le #each pour les participants, il n'a plus accès au contexte vote et n'a donc pas accès aux options disponibles pour chaque vote.

Je peux un peu contourner ce problème en utilisant le chemin du guidon ../options pour revenir au contexte parent, mais cela n'affecte pas le contexte de l'aide modèle, donc this dans Template.vote.is_selected_option fait référence au courant participant, pas au courant vote ou option, et n'a aucun moyen de savoir lequel option nous parcourons actuellement.

Des suggestions sur la façon de contourner cela, sans recourir à la manipulation du DOM et à des manigances jQuery? 

Il s’agit d’un problème de modèle qui a été soulevé à plusieurs reprises pour moi. Nous avons besoin d’un moyen formel d’atteindre la hiérarchie de contexte de modèle, dans les modèles, les aides de modèle et les événements de modèle.

43
cmal

Ce n'est pas particulièrement joli, mais j'ai fait quelque chose comme ça:

<template name='forLoop'>
{{#each augmentedParticipants}}
{{> participant }}
{{/each}}
</template>

<template name='participant'>
...
Question: {{this.parent.question}}
...
</template>


// and in the js:
Template.forLoop.helpers({
    augmentedParticipants: function() {
        var self = this;
        return _.map(self.participants,function(p) {
            p.parent = self;
            return p;
        });
    }
});

C'est similaire à l'approche proposée par AVGP, mais elle augmente les données au niveau de l'aide au lieu du niveau de la base de données, ce qui, je pense, est un peu plus léger.

Si vous en avez envie, vous pouvez essayer d’écrire un assistant de bloc Handlebars eachWithParent qui résumerait cette fonctionnalité. Les extensions de Meteor au guidon sont documentées ici: https://github.com/meteor/meteor/wiki/Handlebars

4
zorlak

Il semble que depuis Spacebars (le nouveau moteur de template de Meteor) , vous avez accès au contexte parent à l’intérieur de {{#each}} blocs en utilisant ../.

Dans Meteor 0.9.1, vous pouvez également écrire une aide et utiliser Template.parentData() dans son implémentation.

83
opyh

Cela devrait rendre la vie plus facile.

// use #eachWithParent instead of #each and the parent._id will be passed into the context as parent.
Handlebars.registerHelper('eachWithParent', function(context, options) {

    var self = this;
    var contextWithParent = _.map(context,function(p) {
        p.parent = self._id;
        return p;
    });

    var ret = "";

    for(var i=0, j=contextWithParent.length; i<j; i++) {
        ret = ret + options.fn( contextWithParent[i] );
    }
    return ret;
});

Allez-y et changez 

p.parent = self._id;

à tout ce que vous voulez accéder dans le contexte parent. 

Fixe le:

// https://github.com/meteor/handlebars.js/blob/master/lib/handlebars/base.js

// use #eachWithParent instead of #each and the parent._id will be passed into the context as parent.
Handlebars.registerHelper('eachWithParent', function(context, options) {
    var self = this;
    var contextWithParent = _.map(context,function(p) {
        p.parent = self._id;
        return p;
    });

    return Handlebars._default_helpers.each(contextWithParent, options);
});

Cela fonctionne :) sans erreur

2
Chet

C'est un long coup, mais peut-être que cela pourrait fonctionner:

{{#with ../}}
  {{#each options}}
    {{this}}
  {{/each}}
{{/with}}
2
danielsvane

Enregistrez simplement un assistant de modèle global:

Template.registerHelper('parentData',
    function () {
        return Template.parentData(1);
    }
);

et l'utiliser dans vos templates HTML comme:

{{#each someRecords}}
  {{parentData.someValue}}
{{/each}}

======= EDIT

Pour Meteor 1.2+, vous devez utiliser:

UI.registerHelper('parentData', function() {
  return Template.parentData(1);
});
2
Jan Curn

Je ne connais pas la méthode officielle (s'il y en a une), mais pour résoudre votre problème, je relierais les participants avec l'ID parent de la manière suivante:

{
    _id: "1234",
    question: "Whats up?",
    ...
    participants: [
      {
        _id: "abcd",
        parent_id: "1234",
        vote: 0
      }
    ]
}

et utilisez ce parent_id dans les assistants, les événements, etc. pour revenir au parent à l'aide de findOne . C'est évidemment une chose sous-optimale à faire, mais c'est le moyen le plus simple qui me vienne à l'esprit tant qu'il est impossible de référencer le contexte parent .. Il existe peut-être un moyen mais il est très bien caché dans le fonctionnement interne de Meteor sans mention dans la documentation, le cas échéant: mettez à jour cette question si vous en trouvez un.

2
geekonaut

J'étais coincé de la même manière et j'ai constaté que l'approche Template.parentData () suggérée dans d'autres réponses ne fonctionne actuellement pas dans les gestionnaires d'événements (voir https://github.com/meteor/meteor/issues/5491 ). L'utilisateur Lirbank a posté cette solution de contournement simple:

Transmettez les données du contexte externe à un élément HTML du contexte interne, dans le même modèle:

{{#each companies}}
  {{#each employees}}
    <a href="" companyId="{{../id}}">Do something</a>
  {{/each}}
{{/each}}

Maintenant, l’identifiant de la société est accessible depuis le gestionnaire d’événements avec quelque chose comme:

$(event.currentTarget).attr('companyId')
0
oskare

"click .selected":function(e){
    var parent_id = $(e.currentTarget).parent().attr("uid");
    return parent_id
  },
<td id="" class="staff_docs" uid="{{_id}}">
                                        {{#each all_req_doc}}
                                                <div class="icheckbox selected "></div>
  {{/each}}
  </td>

0
BGbhavesh

{{#each parent}}

{{#each child}}

<input type="hidden" name="child_id" value="{{_id}}" />

<input type="hidden" name="parent_id" value="{{../_id}}" />

{{/each}}

{{/each}}

Le _id n'est PAS le _did de la chose, c'est l'identifiant du parent!

0
Andy