how to render only one template instance in meteor - templates

Hi I have the following template :
<template name="users">
<ul id="item-list">
{{#each trackedUser}}
<li id="{{_id}}">
<span class="name">{{mUsername}}</span>
<p><span class="description">{{mDescription}}</span></p>
</li>
{{/each}}
</ul>
</template>
With this helper :
Template.users.helpers({
trackedUser: function() {
var curs = TrackedUser.find();
users = curs.fetch();
return users;
}
});
The problem is that in this case for each new user, all the users are being reput in the dom.
Is there a better way to write this code so that for each new user only the
<li id="{{_id}}">
<span class="name">{{mUsername}}</span>
<p><span class="description">{{mDescription}}</span></p>
</li>
is redrawn ?
Thank you

Avoid using fetch. When you use fetch() you don't pass along a cursor to handlebars so it can't re-render only the portion which has changed.
Template.users.helpers({
trackedUser: function() {
var curs = TrackedUser.find();
return curs;
}
});
For the case of anywhere else on your template (not in a loop) you could also use Reactivity Isolation which is wrapping your content in {{#isolate}}..{{/isolate} blocks.

Related

Meteor: Bind function to work for every iteration of template

I'm fairly new to Meteor and I can't get my head around how to get a function to work in every iteration of a template independently from the others. I've written a very basic function where I want to display a pop up inside each template on button click, but the pop up always appears at the first generated template instead of being bound to the specific button I click. I've searched around and found that it might have to do with template instances or reactive variables but as of now I'm rather confused.
Could this be on the right track? https://dweldon.silvrback.com/template-instance
My code:
.html
<body>
{{> test}}
</body>
<template name="test">
{{#each tasks}}
<button type="button" id="popup">Click Me!</button>
<div id="pops">Hi</div>
{{/each}}
</template>
.js
Tasks = new Mongo.Collection("tasks");
if (Meteor.isClient) {
Template.test.helpers({
tasks: function () {
return Tasks.find({});
}
});
Template.test.events({
'click #popup': function(e, template) {
template.$('#pops').fadeIn();
}
});
}
I haven't written anything in the server code at all.
Thanks!
Don't use IDs, use classes instead. Why? Because you are not allowed to have several elements with the same ID.
Create new template task and render it inside each loop.
HTML:
<body>
{{> test}}
</body>
<template name="test">
{{#each tasks}}
{{> task}}
{{/each}}
</template>
<template name="task">
<button type="button" class="popup">Click Me!</button>
<div class="pops">Hi</div>
</template>
JS:
Template.task.events({
'click .popup': function() {
template.$('.pops').fadeIn();
}
});
try changing your code like this
<template name="test">
{{#each tasks}}
<button type="button">Click Me!</button>
<div id={{_id}}>Hi</div>
{{/each}}
</template>
Template.test.events({
'click button': function(e, template) {
var id = '#'+this._id;
$(id).fadeIn();
}
});
}

Binding class from an array in Ember.js

I'm starting with Ember.js and I'm feeling a bit lost.
I have an object like this:
App.BRANDS = [
{
brand:'Audi'
},{
brand:'BMW'
},{
brand:'Skoda'
}];
So what I'm trying is to display all the elements in that object with the {{#each}} component, show the text inside and bind the same text as a className. So I code this in the route:
App.InsuranceAutoSelectBrandRoute = App.Route.extend({
model: function(){
return App.BRANDS;
}
});
And this in the template:
<article>
{{#each brand in model tagName='ul'}}
<li class='item-space'>
<span {{bind-attr class=':brand-auto classNameAuto'}}></span>
{{brand.brand}}
</li>
{{/each}}
</article>
Then the issue is that the name of each brand, before I bind it as a class attr I have to lowercase it...
App.InsuranceAutoSelectBrandController = Ember.Controller.extend({
classNameAuto: function() {
App.BRANDS.forEach(function(item, index){
return item.brand.toLowerCase();
});
}.property()
});
If in the same point where I'm returning the value I make a log, it works, but the class attr is not showing.
What I'd like:
<article>
<li class='item-space'>
<span class='brand-auto audi'}}></span>
Audi
</li>
<li class='item-space'>
<span class='brand-auto bmw'}}></span>
BMW
</li>
<li class='item-space'>
<span class='brand-auto skoda'}}></span>
Skoda
</li>
</article>
Forgive me for my English level and thank you
First, as #Kitler said, bind-attr is deprecated. If you are using 1.13.x or higher, you may just insert class as any other variable, {{brand.brand}}.
Second, your code is incorrect and will not work. The easiest way to do what you want is via helper. You need to create a helper, lower-case and use it in this way:
<article>
<ul>
{{#each model as |brand|}}
<li class='item-space'>
<span class='brand-auto {{lower-case brand.brand}}'>{{brand.brand}}</span>
</li>
{{/each}}
</ul>
</article>
How to create a helper you may learn from documentation (http://guides.emberjs.com/v1.13.0/templates/writing-helpers/). If you use ember-cli, following code should work:
//app/helpers/lower-case.js
import Ember from 'ember';
export default Ember.Handlebars.makeBoundHelper(function (str) {
return str.toLowerCase();
});

How to get current model for a route from a controller or a view?

I want to implement item-list/item-detail pattern in Ember, but the nuance is that the detail view must appear next to the selected item. E.g:
<ul>
<li><div>Post 1<div></li>
<li><div>Post 2<div></li>
<li><div>Post 3<div></li>
<li>
<div>Post 4<div>
<div>
<ul>
<li>Comment 1</li>
<li>Comment 2</li>
<li>Comment 3</li>
</ul>
</div>
</li>
<li><div>Post 5<div></li>
</ul>
The Handlebars template I tried is:
<script type='text/x-handlebars' data-template-name='posts'>
<ul>
{{#each model}}
{{#linkTo 'post' this}}
<div>{{title}}</div>
{{/linkTo}}
{{#if isSelected}} <!-- How to implement isSelected ? -->
<div>{{!-- render selected post's comments --}}</div>
{{/if}}
{{/each}}
</ul>
</script>
I tried this in controller:
App.PostController = Em.ObjectController.extend({
isSelected: function() {
return this.get('content.id') === /* what to put here? */;
}
});
What I'm stuck with is how to implement isSelected in 'Ember'-way? Am I going in right direction?
You are on the right track. The trick is to use a different controller to wrap products in the item-list vs. the item-detail. So start by specifying that the handlebars {{each}} helper should wrap each entry in a ListedProductController
{{#each model itemController="listedProduct"}}
Now define ListedProductController, adding the isSelected function you'd been writing. Now it can reference the singleton ProductController via the needs array, comparing the product that was set by the router to the listed product.
App.ProductController = Ember.ObjectController.extend({});
App.ListedProductController = Ember.ObjectController.extend({
needs: ['product'],
isSelected: function() {
return this.get('content.id') === this.get('controllers.product.id');
}.property('content.id', 'controllers.product.id')
});
I've posted a working example here: http://jsbin.com/odobat/2/edit

Ember.js binding array element to view

I have the following code in a ember.js template. userController is an ArrayController, with multipe "users" within.
{{#each CollaborativeEditor.userController}}
{{#view CollaborativeEditor.OnlineUserView userBinding="this"}}
<div class="avatar">
<div class="avatar_name">{{name}}</div>
<div class="avatar_status">{{status}}</div>
</div>
<div id="dropdown-1">
<ul>
<li><a href="#" {{action startChat target="onlineUser"}}>Talk to </a></li>
</ul>
</div>
{{/view}}
{{/each}}
This is the code of the respective view:
CollaborativeEditor.OnlineUserView = Ember.View.extend({
tagName: 'li',
startChat : function() {
console.log(this.get('user'));
}
});
Although, the name and status is set correctly for each user, the startChat action attached to the link always prints the first user of the array to the console.
What is wrong with the binding ?
Thanks a lot for your request, to put it in a jsfiddle !
While I was trying to reproduce the error there, I realized the problem, and it has nothing to do with ember.
The div with id="dropdown-1" was called from another link and it was always the same id, therefore always the same action with its user-binding.
Now I've bound the Id to the user-object and it works perfectly.

EmberJS - How to call view method from inside a nested {{#each}}

I need to trigger the {{showAlias}} view method from within the {{#each content.activitytypes}} I have tried {{_parentView.showAlias}}. I believe I need to call it on the parent's parent, is that correct and how? I can call the {{showAlias}} outside of the {{#each...
Handlebars Template
<script type="text/x-handlebars">
<h2>Activities</h2>
<ul>
{{#each Km.activityController}}
{{#view Km.ActivityView contentBinding="this"}}
<li>
{{content.id}}:{{content.name}}
<ul>
{{#each content.activitytypes}}
<li>{{showAlias}}
{{name}} {{aliasname}}
</li>
{{/each}}
</ul>
</li>
{{/view}}
{{/each}}
</ul>
</script>
View
Km.ActivityView = Em.View.extend({
showAlias: function () {
var arr = this.getPath('content.activitytypes'),
show = false;
console.log(arr)
arr.forEach(function(item) {
var aliasArr = item.showaliasaccountid;
if (typeof aliasArr !== 'undefined') {
if (jQuery.inArray(2,aliasArr)) {
console.log(aliasArr);
show = true;
}
}
});
}.property('content.#each.activitytype.#each'),
});
{{parentView.showAlias}} will work, but in these situations with nested views and sub eachs I always find the code to be more maintainable with CollectionViews. Otherwise you end up stuffing too much inside of a single template/view.
http://docs.emberjs.com/#doc=Ember.CollectionView&src=false