2012-12-09 2 views
42

Występuje problem z kontekstem szablonu, który trudno jest znaleźć.Uzyskiwanie dostępu do kontekstu nadrzędnego w szablonach meteorytów i szablonach pomocników


Oto szablon w pytaniu:

{{#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}} 


A oto przykład dokumentu głosowanie:

{ 
    _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 
     } 
    ] 
} 


I tu jest problem .. .

Kiedy szablon wpada do uczestników #each, nie ma już dostępu do kontekstu vote i dlatego nie ma dostępu do dostępnych opcji dla każdego głosowania.

mogę nieco to obejść za pomocą ścieżki ../options kierownicy, aby przejść z powrotem do macierzystego kontekstu, ale nie wpływa to kontekst pomocnika szablonu, więc this w Template.vote.is_selected_option odnosi się do aktualnej participant, nie do bieżącego vote lub option i nie ma możliwości sprawdzenia, który option jesteśmy obecnie iteracyjne.

Wszelkie sugestie, jak obejść ten problem, bez uciekania się do manipulacji DOM i shenanigans jQuery?

Jest to kwestia szablonowa, która pojawiła się wielokrotnie dla mnie. Potrzebujemy formalnego sposobu dotarcia do hierarchii kontekstu szablonu, szablonów, szablonów i zdarzeń szablonu.

Odpowiedz

4

To niezbyt ładna, ale zrobiłem coś takiego:

<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; 
     }); 
    } 
}); 

Jest podobny do podejścia, które avgp sugerowane, ale zwiększa danych na poziomie pomocniczym zamiast poziomu dB, co ja myślę, że jest trochę lżejsza.

Jeśli masz ochotę, możesz spróbować napisać pomocnik blokujący kierownicę eachWithParent, który rozwiąże tę funkcję.Rozszerzenia Meteor do kierownicy są udokumentowane tutaj: https://github.com/meteor/meteor/wiki/Handlebars

+0

zorlak: To, co zrobiłem, jest bardzo podobne do twojego przykładu: stworzyłem rozszerzoną kolekcję 'members_with_vote_info' zawierającą wszystko, czego potrzebowałem, i iterowałem przez to z' # each', dzięki czemu nie musiałem wskakiwać z powrotem do rodzic. Dzięki za pomysł! – cmal

+3

Dobra odpowiedź - ale to jest do bani. To powinno być naprawione – Chet

+0

Dla układu zagnieżdżonych szablonów jest to dość bolesne w dupie –

2

nie wiem formalny sposób (jeśli istnieje), ale aby rozwiązać swój problem, chciałbym połączyć uczestników z identyfikatorem dominującej jak ten:

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

i używać tej parent_id w pomocników , wydarzenia itp., aby powrócić do rodzica za pomocą findOne. Jest to oczywiście coś, co nie jest optymalne, ale jest to najłatwiejszy sposób, jaki przychodzi mi do głowy, o ile nie można odwołać się do kontekstu nadrzędnego. Może jest sposób, ale jest bardzo dobrze ukryty w wewnętrznych działaniach Meteorytów bez wzmianki w dokumentach, jeśli tak, to: proszę zaktualizować to pytanie, jeśli je znajdziesz.

+0

Masz rację, byłoby to nieoptymalne, ale zadziałałoby. Podobną alternatywą byłoby użycie osobnego zbioru Użytkownicy, przy czym każdy użytkownik zapisał swój powiązany identyfikator głosowania. Jeśli ktokolwiek inny ma inne sposoby robienia tego, proszę, daj mi znać. – cmal

+0

Powinienem dodać, że żadne z tych rozwiązań nie ominą problemu _biggest_, że każde 'Template.votes.option' w' {{#each ../ options}} 'nie wie, której opcji dotyczy! – cmal

2

To długi strzał, ale może to może działać:

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

ta powinna ułatwić życie.

// 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; 
}); 

Śmiało i zmienić

p.parent = self._id; 

aby cokolwiek chcesz mieć dostęp w związku macierzystego.

Naprawiono go:

// 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); 
}); 

To działa :) bez błędu

+0

Otrzymuję ten błąd 'Wyjątek z funkcji Deps afterFlush: Błąd: Nie mogę utworzyć drugiego punktu orientacyjnego w tej samej gałęzi' kiedy próbuję zmienić coś, co jest renderowane za pomocą eachWithParent – Chet

82

Wygląda na to, ponieważ Spacebars (Meteor's new template engine), masz dostęp do kontekstu macierzystego w {{#each}} bloków wykorzystujących ../.

W Meteorcie 0.9.1 można również napisać pomocnika i użyć Template.parentData() w swojej implementacji.

+8

To jest poprawna odpowiedź dla Meteor 0.8. – JJJ

+3

@opyh, czy dodasz odwołanie do [Template.parentData (numLevels)] (http://docs.meteor.com/#template_parentdata)? Myślę, że jeszcze bardziej poprawiłoby to tę odpowiedź. –

+4

Tak więc zrobiłbyś " Pytanie: {{../przyp.}} ' to działało dla mnie. (Meteor 1.0+) – Michael

2

Wystarczy zarejestrować szablon globalny pomocnika:

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

i używać go w szablonach HTML jako:

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

======= EDIT

Dla Meteor 1.2+ , użyłeś:

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

, ale co to jest "someRecords" w twoim przykładzie? na pewno musi to być z kontekstu parentData - ale twoje rozwiązanie nie działa, gdy próbujesz uzyskać dostęp do parentData w klauzuli #each –

0

Utknąłem w podobny sposób i odkryłem, że metoda Template.parentData() sugerowana w innych odpowiedziach obecnie nie działa w ramach procedur obsługi zdarzeń (patrz https://github.com/meteor/meteor/issues/5491).Użytkownik Lirbank pisał to proste rozwiązania:

przekazać dane z zewnętrznego kontekstu do elementu html w wewnętrznym kontekście, w tym samym szablonie:

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

Teraz ID firma może uzyskać dostęp z obsługi zdarzeń z czymś

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

"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

{{#each parent}}

{{#each child}}

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

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

{{/each}}

{{/each}}

_id NIE jest _did z rzeczy, ja t jest identyfikatorem rodzica!