How can one use Blaze.Each? For example, I'd like to emulate this:
{{#each Items}}
{{this}}
{{/each}}
in JS. I need to keep reactivity.
Something like:
Blaze.Each(Items.find(), function(item) {
console.log(item);
});
I don't get previous answer
This
html
<template name="yourTemplate">
<div id="each-area">
</div>
</template>
js
Template.yourTemplate.onRendered(function () {
var renderableContent = Blaze.Each(Collection.find({}), function(){
return Template.templateToRender;
});
Blaze.render(renderableContent, this.find("#each-area"));
});
Is complete equivalent of this:
html
<template name="yourTemplate">
<div id="each-area">
{{#each data}}
{{>templateToRender}}
{{/each}}
</div>
</template>
js
Template.yourTemplate.helpers({
"data": function(){
return Collection.find({})
}
});
this.autorun(function(){
var items = Items.find();
_.each(items.fetch(), function(item) {
console.log(item);
});
});
This works. It console.logs everytime an Item is added.
w/o underscore and autorun:
Blaze.Each(Data.find().fetch(), function(item){
console.log(item)
})
Related
Im trying to run some jquery code after the template renders. But it's not working. When I run the code in the console manually, it works. Here's the js :
Template.PanelLayout.helpers({
restricted: function() {
return !Meteor.user();
},
authInProcess: function() {
return Meteor.loggingIn();
},
canShow: function() {
return !!Meteor.user();
}
});
Template.PanelLayout.rendered = function() {
$(document).ready(function() { // This is the code I want to run
$(".button-collapse").sideNav();
$('.collapsible').collapsible();
});
}
Template code :
<template name="PanelLayout">
{{#if restricted}}
{{> NotFound}}
{{else}}
{{#if authInProcess}}
{{> spinner}}
{{else}}
{{#if canShow}}
<header>
{{> PanelMainNav}}
</header>
<main id="panel-content">
{{> Template.dynamic template=content }}
</main>
{{/if}}
{{/if}}
{{/if}}
</template>
Im not sure but I think it's because I have added the if else statements to load the content only when the user is logged in? How can I fix this?
Probably it's because rendered function is called after document is ready, therefore your hook document.ready won't work, you should remove it, so it will look like this(also rendered function is deprecated, use onRendered instead):
Template.PanelLayout.onRendered(function() {
$(".button-collapse").sideNav();
$('.collapsible').collapsible();
});
I'm having issues finding a way to update the UI AFTER adding to a collection. So in the example below after you click the button and add to the collection an additional input is added to the DOM. All good, but i'd like to find a way to target the new input element and preferably give it focus in addition to CSS. Unfortunately I can't find any info that helps solve this AFTER the DOM's been updated. Any ideas? Thanks
<body>
{{> myTemplate}}
</body>
<template name="myTemplate">
{{#each myCollection}}
<input type="text" value="{{name}}"><br>
{{/each}}
<br>
<button>Click</button><input type="text" value="test" name="testBox">
</template>
test = new Meteor.Collection("test");
if (Meteor.isClient) {
Template.myTemplate.rendered = function()
{
console.log("rendered");
this.$('input').focus()
}
Template.myTemplate.helpers({
'myCollection' : function(){
var testCollection = test.find({});
console.log("helpers");
return testCollection;
}
});
Template.myTemplate.events({
'click button': function(event){
event.preventDefault();
var val = $('[name="testBox"]').val();
console.log("events");
return test.insert({name: val});
}
});
}
Turn what you're adding into a template and call that template's rendered to set the needed css or do whatever transforms are needed.
HTML:
<body>
{{> myTemplate}}
</body>
<template name="item">
<input type="text" value="{{name}}"><br>
</template>
<template name="myTemplate">
{{#each myCollection}}
{{> item this}}
{{/each}}
<br>
<button>Click</button><input type="text" value="test" name="testBox">
</template>
JS:
test = new Meteor.Collection("test");
if (Meteor.isClient) {
Template.myTemplate.onRendered(function() {
console.log("rendered");
this.$('input').focus()
});
Template.myTemplate.helpers({
'myCollection' : function(){
var testCollection = test.find({});
console.log("helpers");
return testCollection;
}
});
Template.myTemplate.events({
'click button': function(event){
event.preventDefault();
var val = $('[name="testBox"]').val();
console.log("events");
test.insert({name: val});
}
});
Template.item.onRendered(function() {
this.$('input').focus();
}
}
On a side note, you should use onRendered instead of rendered as the latter has been deprecated for the former.
Do it inside of your myCollection helper function. Use jquery to target the last input in your template and focus it, add css. Meteor's template helpers are reactive computations based on the DOMs usage of their reactive variables, so it will run each time the DOM updates based on your collection.
If I first open http://localhost:3000/, then click the test link, the roles labels will be displayed.
But if I directly open http://localhost:3000/test(Input the url in Chrome's address bar and hit enter), the roles labels will not be displayed.
Here is my code:
In client startup I subscribe to something:
Meteor.publish("Roles", function(){
return Roles.find();
});
Meteor.startup(function() {
if(Meteor.isClient) {
Meteor.subscribe('Roles');
}
});
And roles template:
Template.roles.helper( {
allRoles: function() {
return Roles.find();
}
})
html
<template name="roles">
<div>
{{#each allRoles}}
<label>test label</label>
{{/each}}
</div>
</template>
The problem is sometime roles template is rendered before the Roles is ready.
So these is no role labels displayed.
But according to Meteor document, helpers is a reactive computation, and Database queries on Collections is reactive data source. So after Roles is ready, the {{#with allRoles}} is reactive and should be displayed.
Why does roles not be displayed?
And then I rewrite my code to:
Meteor.startup(function() {
if(Meteor.isClient) {
roles_sub = Meteor.subscribe('Roles');
}
});
Template.roles.helper( {
allRoles: function() {
console.log(2);
return Roles.find();
},
isReady: function() {
console.log(1);
console.log(roles_sub.ready());
return roles_sub.ready();
}
})
html
<template name="roles">
<div>
{{#if isReady}}
{{#each allRoles}}
<label>test label</label>
{{/each}}
{{/if}}
</div>
</template>
And still role labels cannot be displayed.
And console gives me:
1
false
1
false
1
true
2
Which means isReady() is reactive? but why my roles labels remains blank?
Can somebody explain this?
use Template.subscriptionsReady
server/publish.js
Meteor.publish("Roles", function(){
return Roles.find();
});
client/client.js
Meteor.startup(function() {
Meteor.subscribe('Roles');
});
Template.roles.helpers({ // --> .helper change to .helpers
allRoles: function() {
return Roles.find();
}
})
client/templates.html
<template name="roles">
<div>
{{# if Template.subscriptionsReady }}
{{#with allRoles}}
<label>{{> role }}</label>
{{/with}}
{{ else }}
loading....
{{/if}}
</div>
</template>
<template name="role">
<div>{{ _id }}</div>
</template>
A reactive function that returns true when all of the subscriptions
https://github.com/meteor/meteor/blob/9fe2f4b442ec84eac5352b476d014c977c5ae4a2/packages/blaze/template.js#L424
I'm very new to data binding and the two frameworks. Right now I'm pretty stuck at how to bind the data within a polymer element.
For example, I have a book list with books' name. If I only use blaze to do the rendering, I would do it in the follow way:
//app.js
Template.bookList.helpers({
books: function () {
return Books.find({});
}
});
//app.html
<template name="bookList">
<h1>List</h1>
<ul>
{{#each books}}
{{> book}}
{{/each}}
</ul>
</template>
<template name="book">
<li>{{name}}</li>
</template>
Now I'm using it with polymer, I do:
//my-book-list.html
<polymer-element name="my-book-list">
<template>
<h1>List</h1>
<content></content>
</template>
</polymer-element>
//app.html
<template name="bookList">
<my-book-list>
<ul>
{{#each books}}
{{> book}}
{{/each}}
</ul>
</my-book-list>
</template>
<template name="book">
<li>{{name}}</li>
</template>
So I place the dynamic data inside of the polymer item through the content block. Although it still does the job, I don't want it that way. I want to do the data-binding inside the polymer element, something like(I hope it makes sense to you):
//my-book-list.html
<polymer-element name="my-book-list">
<template bind="{{books}}">
<h1>List</h1>
<ul>
<template repeat>
<li>{{name}}</li>
</template>
</ul>
</template>
</polymer-element>
//app.html
<template name="bookList">
<my-book-list></my-book-list>
</template>
Is there a way to do it? Thanks in advance.
Progress:
I now can put books purely inside the polymer element, the problem now is that it doesn't seem to react when the data change because polymer doesn't observe change of a object, and I am struggling in finding a way to observe all the nested values inside a object:
<polymer-element name="my-book-list">
<template bind="{{books | mapBooks}}">
<h1>List</h1>
<ul>
<template repeat>
<li>{{name}}</li>
</template>
</ul>
</template>
<script>
Polymer("my-book-list", {
books: Books.find(),
mapBooks : function(booksCursor) {
return booksCursor.map(function(p) { return {id: p.id, name: p.name}})
}
});
</script>
</polymer-element>
Finally got a hacky solution, but I don't know if this is the best practice or not:
<polymer-element name="my-book-list">
//Force polymer to update DOM when books.lastUpdate change
<template bind="{{books.lastUpdate | getBooks}}">
<h1>List</h1>
<ul>
<template repeat>
<li>{{name}}</li>
</template>
</ul>
</template>
<script>
Polymer("my-book-list", {
ready: function() {
var books = this.books;
this.books.observeChanges( //Observe the change of cursor and update a field
{
added: function(id, fields) {
console.log("Item added");
books.lastUpdate = new Date();
},
changed: function(id, fields) {
console.log("Item changed");
books.lastUpdate = new Date();
},
removed: function(id, fields) {
console.log("Item deleted");
books.lastUpdate = new Date();
}
}
),
books: Books.find(),
getBooks : function() {
return Books.find().map(function(p) { return {id: p.id, name: p.name}})
}
});
</script>
</polymer-element>
In the Handlebars.js - template i am getting a senario to check the value of the array, and if the value is not equal to do a task and equal to do a taks.. i do like this, But it thrown a error, how to handle this scenario.. any one help me?
Or any one give the way to handle this properly.
myJson would be :
{
"links":[{"label":"x","link":"x"},
{"label":"y","link":"y"},
{"label":"Logout","link":"Logout"}]
}
{{#each links}}
{{#if !lable.Logout}}
<li>
{{label}}
{{#if subLinks}}
<ul>
{{#each subLinks}}
<li>{{label}}</li>
{{/each}}
</ul>
{{else}}
<div>{{label}}</div>
{{/if}}
{{/each}}
Handlebars alone cannot handle conditional if statements, you need to use a helper function.
<div id="myDiv"></div>
<script type="text/x-handlebars-template" id="handlebar">
{{#each links}}
{{#ifCond label "Logout" }}
<li>
{{label}}
{{#if subLinks}}
<ul>
{{#each subLinks}}
<li>{{label}}</li>
{{/each}}
</ul>
{{/if}}
{{else}}
<div>
{{label}}
</div>
{{/ifCond}}
{{/each}}
</script>
<script>
$( document ).ready(function() {
var source = $("#handlebar").html();
var template = Handlebars.compile(source);
var context = {
"links":[
{"label":"x","link":"x"},
{"label":"y","link":"y"},
{"label":"Logout","link":"Logout"}
]
};
var html = template(context);
$("#myDiv").html(html);
});
Handlebars.registerHelper('ifCond', function(v1, v2, options) {
if(v1 === v2) {
return options.fn(this);
}
return options.inverse(this);
});
</script>