2014-09-10 20 views
6

Ich habe folgende Template-CodeMeteor: warten, bis alle Vorlagen

<template name="home"> 
    <div class="mainBox"> 
     <ul class="itemList"> 
      {{#each this}} 
       {{> listItem}} 
      {{/each}} 
     </ul> 
    </div> 
</template> 

<template name="listItem"> 
    <li class="item"> 
     {{username}} 
    </li> 
</template> 

geleistet werden und ich möchte einen Code einmal alle „listItem“ gerendert werden auszuführen. Es gibt ungefähr 100 von ihnen. Ich habe folgendes versucht

Template.home.rendered = function() { 
    // is this called once all of its 'subviews' are rendered? 
}; 

Aber es wartet nicht, bis alle Ansichten geladen sind.

Was ist der beste Weg, zu wissen, wann alle Untersicht Vorlagen geladen werden?

+0

Sie irgendwo die Daten abonnieren? Verwenden Sie Eisenrouter? –

+0

'listItem'-Vorlagen erhalten Daten aus einer vermutlich reaktiven Sammlung. In diesem Fall können Sie nie sicher sein, dass * alle * von ihnen gerendert werden. Sie können jedoch ein paar Tricks verwenden, um eine Benachrichtigung zu erhalten, wenn das erste Set gezeichnet wird. Trotzdem versuchst du wahrscheinlich etwas nicht im "Meteor Way" zu machen, also würde ich nach einer besseren Lösung suchen. Was versuchst du zu erreichen? –

Antwort

3

Dies ist, wie ich vorgehen:

client/views/home/home.html

<template name="home"> 
    {{#if itemsReady}} 
    {{> itemsList}} 
    {{/if}} 
</template> 

<template name="itemsList"> 
    <ul> 
    {{#each items}} 
     {{> item}} 
    {{/each}} 
    </ul> 
</template> 

<template name="item"> 
    <li>{{value}}</li> 
</template> 

client/views/home/home.js

Template.home.helpers({ 
    itemsReady:function(){ 
    return Meteor.subscribe("items").ready(); 
    } 
}); 

Template.itemsList.helpers({ 
    items:function(){ 
    return Items.find(); 
    } 
}); 

Template.itemsList.rendered=function(){ 
    // will output 100, once 
    console.log(this.$("li").length); 
}; 

lib/collections/items.js

Items=new Mongo.Collection("items"); 

server/collections/items.js

insertItems=function(){ 
    var range=_.range(100); 
    _.each(range,function(index){ 
    Items.insert({value:"Item "+index}); 
    }); 
}; 

Meteor.publish("items",function(){ 
    return Items.find(); 
}); 

server/startup.js

Meteor.startup(function(){ 
    Items.remove({}); 
    if(Items.find().count()===0){ 
    insertItems(); 
    } 
}); 

Wir spezifizieren, dass wir unsere Liste der Elemente nur machen möchten, wenn die Veröffentlichung bereit ist, so durch, dass Zeitdaten zur Verfügung steht und die richtige Anzahl von li Elemente erhalten in der Liste angezeigt Callback angezeigt.

nun das gleiche mit iron:routerwaitOn Feature:

client/views/home/controller.js

HomeController=RouteController.extend({ 
    template:"home", 
    waitOn:function(){ 
    return Meteor.subscribe("items"); 
    } 
}); 

client/lib/router.js

Router.configure({ 
    loadingTemplate:"loading" 
}); 

Router.onBeforeAction("loading"); 

Router.map(function(){ 
    this.route("home",{ 
    path:"/", 
    controller:"HomeController" 
    }); 
}); 

client/views/loading/loading.html

<template name="loading"> 
    <p>LOADING...</p> 
</template> 

Die Verwendung von iron:router ist wahrscheinlich besser, weil es ein gemeinsames Muster elegant löst: Wir brauchen den itemsReady-Helfer nicht mehr, die Home-Vorlage wird nur gerendert, wenn die von waitOn zurückgegebene WaitList global bereit ist.

Man darf nicht gleichzeitig Lade Vorlage und Einstellungen als Standard „Laden“ Haken sonst wird es nicht funktionieren hinzuzufügen vergessen.

+0

Nur @ saimeunt Antwort hinzufügen - in meiner Route hatte ich "Daten:" -Funktion, aber die Datenfunktion selbst läuft vor "Abonnement" abgeschlossen ist, so müssen Sie "this.ready()" Conditional-Check hinzufügen zu machen sicher, dass alle Daten zurückgegeben werden - https://github.com/EventedMind/iron-router/issues/265 – ericbae

0

das Verfahren gemacht Werke dieser Art und Weise

Dieser Callback wird einmal aufgerufen, wenn eine Instanz von Template.myTemplate in DOM-Knoten gemacht wird und in das Dokument zum ersten Mal gestellt.

so, wenn gemacht wird, Sie hat keine Variable in diesem Fall reaktiv.

// this would sufficient 
Template.listItem.helpers = function() { 
    username:function(){ 
     return ... 
    } 
}; 
0

Ich würde vorschlagen, so etwas wie:

var unrendered = []; 

Template.listItem.created = function() { 
    var newId = Random.id(); 
    this._id = newId; 
    unrendered.push(newId); 
}; 

Template.listItem.rendered = function() { 
    unrendered = _.without(unrendered, this._id); 
    if (!unrendered.length) { 
    // WHATEVER NEEDS DOING WHEN THEY'VE ALL RENDERED 
    } 
}; 

CAVEAT

Dies unter der Annahme arbeitet, die im Wesentlichen alle Template-Instanzen erstellt werden, bevor sie ersten, die gemacht worden sind, andernfalls Ihr Code wird ausgeführt, bevor es sollte. I denke, Dies sollte der Fall sein, aber Sie müssen es ausprobieren, da ich nicht wirklich Zeit habe, einen 100+ Unter-Vorlage-Test zu laufen. Wenn es nicht der Fall ist, dann kann ich nicht sehen, wie Sie dieses Verhalten erreichen können, ohne im Voraus genau zu wissen, wie viele Untervorlagen erstellt werden.

Wenn Sie wissen, wie viele es sein wird, dann wird der Code über einen Zähler vereinfacht werden kann, dass jedes Mal rendered läuft dekrementiert, und es ist einfach.

unrendered = [number of listitems]; 

Template.listItem.rendered = function() { 
    unrendered--; 
    if (!unrendered) { 
    // WHATEVER NEEDS DOING WHEN THEY'VE ALL RENDERED 
    } 
}; 

Auch können Sie meteor add random brauchen, aber ich denke, dass dieses Paket jetzt in Kern enthalten ist.

1

Ich hatte das gleiche Problem mit auf alle meinem Subtemplates zu warten, um zu laden, bevor eine glatte JavaScript Karussell-Plugin aufrufe (oder jede kühle JavaScript Plugin wie Diagramme oder Grafiken, die vor dem Aufruf in dem DOM geladen, um Ihre gesamte Datensatz benötigen) .

ich es gelöst, indem Sie einfach den Rang des Subtemplate auf die Gesamtzahl verglichen wird, für was auch immer Abfrage zurückgegeben werden soll ich tat. Sobald der Rang der Zählung gleich ist, können Sie Ihr Plugin aus dem subtemplate.rendered Helfer nennen, weil alle Subtemplates in das DOM eingefügt wurden. Also in Ihrem Beispiel:

Template.listItem.rendered = function() { 

    if(this.data.rank === ListItems.find({/* whatever query */}).count()) { 
     console.log("Last item has been inserted into DOM!"); 
     // Call your plugin 
     $("#carousel").owlCarousel({ 
      // plugin options, etc. 
     }); 
    } 
} 

Dann brauchen Sie nur Ihren Helfer für listItems einen Rang zurück, die einfach genug ist:

Template.home.helpers({ 

    listItems: function() {   
     return ListItems.find({/* whatever query */}).map(function(listItem, index) { 
      listItem.rank = index + 1; // Starts at 1 versus 0, just a preference 
     }); 
    } 
} 
0

Anscheinend gibt es verschiedene Möglichkeiten, um Ihre Situation zu behandeln. Sie können einfach Vorlagen-Abonnements verwenden.

Template.myView.onCreated(function() { 
    var self = this; 

    self.autorun(function(){ 
     self.mySub = self.subscribe('mySubscription'); 
    }); 

    if(self.mySub.ready()) { 
     // my sweet fancy code... 
    } 
}); 


<template name="myTemplate"> 
    <ul> 
     {{#if Template.subscriptionsReady}} 
      {{#each items}} 
       <li>{{item}}</li> 
      {{/each}} 
     {{else}} 
      <div class="loading">Loading...</div> 
     {{/if}} 
    </ul> 
</template>