Ember: How to access another model data in a dynamic segment template - javascript

In a dynamic segment template, how do you display data from a model using the route ?
so for example I have those three routes with phone_id as dynamic segment
Router.map(function() {
this.route('phones');
this.route('phone', {path: 'phones/:phone_id'});
this.route('numbers');
});
in phones/:phone_id template, I am trying to show all the numbers model. so in phone.js route, I tried to return the number model and output it but it showed nothing.
import Ember from 'ember';
export default Ember.Route.extend({
numbers(){
return this.get("store").findAll('number')
}
});
I tried it also with the params.phone_id as argument but it did not work. (no error was shown also).
the template phone.hbs looks like
<h5> Device Id: {{model.device_id}}</h5>
{{#each numbers as |number|}}
{{number.digits}}
{{/each}}
funny thing is model.device_id returns the correct one even though I did not even set it to return that in phone.js route. But the each loop for numbers which I did implement something for does not return anything.
Is there a workaround to return number model data in phone.hbs dynamic segment template ?
EDIT:
the way I am reaching my dynamic segment is through a link to:
{{#each phones as |phone|}}
<li>{{#link-to 'phone' phone}} {{phone.id}}{{/link-to}}</li>
{{/each}}

Only object from returned from model hook of route is set as model of controller.
if you want to use numbers as it is in template then write it as a computed property in controller.
numbers:Ember.computed(function(){
return this.store.findAll('number');
});
or you can set these properties in model itself
so model hook of your route will look like this
model:function(params){
return Ember.RSVP.hash({
phone: this.store.findRecord('phone',params.phone_id),
numbers: this.store.findAll('number')
});
}
after this you will get two properties in your model
Now your template will look like this
<h5> Device Id: {{model.phone.device_id}}</h5>
{{#each model.numbers as |number|}}
{{number.digits}}
{{/each}}

Related

Iron router with Meteor display data to template

I am using iron router with Meteor (latest versions). I have a template:
<template name="home">
{{#each products}}
<p>{{name}}</p>
{{/each}}
JJJJJJJJJJJJ
</template>
and in lib/router.js:
Router.configure({
layoutTemplate:'layout'
});
Router.map(function () {
this.route('home', {
path:'/',
template:'home',
data : function () {
return Products.find();
}
});
});
When I run the page, I see empty page with this JJJJJJJJJJJJ, added for test to see is it template loads.
In the Products collection are 2 items with name. I can read (select), add, remove items for this collection via WEB browser console, but the collection is not rendered in the template.
What can be the error ?
The data function of Iron Router sets the data context for the template. In your case, you are setting the data context to be a cursor but attempting to access the cursor with products, which does not exist as a helper, either registered globally or on the template itself.
There's a few ways you can fix this, but I would suggest letting the router simply determine which template to render and let the template fetch it's own data.
Adjusted router:
Router.map(function () {
this.route('home', {
path: '/',
template: 'home'
}
);
Template helper:
Template.home.helpers({
products() {
return Products.find();
}
});
Alternatively, you can use the this keyword within the template to access the current data context:
<template name="home">
{{#each this}}
<p>{{name}}</p>
{{/each}}
JJJJJJJJJJJJ
</template>
However, it is not easy to discern what is being put into this unless you can follow the flow of the data through the route to the template. This also couples this template very tightly with this route, as the route itself is determining the context of your template.
The solution is in lib/router.js, need to set attribute data like this :
data : function () {
templateData = {
products: Products.find()
};
return templateData;
}
, NOT like before - return Products.find(); .

Get Object attribute with array Notation inside ember template

Just trying something but not sure how to do it.
Scenario:- I'm trying to render a page which have a configuration data and view data, so basically My template loop on the configuration data to render page and fetch value from view data to display.
so my model have something like
model:{'confData':[{'displayName':'value1','fieldName':'Field1'},
{repeat similar object}
],
'viewData':{'Field1':'value1','Field2':'value2',....}
}
Now in template I'm trying to render like:
{{#each confData}}
this.displayName {{input type='text' value=viewData[this.fieldName] }}
{{/each}}
but it does not accept it. Any suggestion on how I can deal with this sort of problem.
PS: the confdata and view data in actual application will be api call which i can't change.
I saw somthing like http://jsfiddle.net/GRaa5/4/ but when I tried changing as http://jsfiddle.net/weyzay5v/ it failed.
Thanks.
There is no way to do what you want purely with Handlebars. Remember that Handlebars templates are logic-less and for presentation only. My suggestion would be to make a computed property in your controller:
data: function() {
var confData = this.get('confData');
var viewData = this.get('viewData');
return confData.map(function(data) {
return {
displayName: data.displayName,
value: viewData[data.filedName]
};
});
}.property('confData.[]', 'viewData')
Then in your template:
{{#each data}}
{{displayName}} {{input type='text' value=value}}
{{/each}}

Ember.js not re-rendering template after Model loaded

I'm learning Ember.js using a Ruby on Rails API server. I've got the routes, template, and model all setup and working - but the template is never re-rendered once the data has been loaded from the API server. I'm not getting any error messages, and I know the customer is being loaded from looking at the Ember inspector.
Customer list is supposed to be displayed after start
Customer list is being loaded correctly from the API server:
Router
// javacripts/router.js
App.Router.map(function() {
this.resource('customers', { path: "/" });
});
Customers Route
// javascripts/routes/customer_routes.js
App.CustomersRoute = Ember.Route.extend({
model: function() {
return this.store.find('customer');
},
renderTemplate: function() {
this.render('customers/index');
}
});
Customer Model
// javascripts/models/customer.js
App.Customer = DS.Model.extend({
name: DS.attr('string')
});
Customer Index Template
// javacripts/templates/customers/index.js.handlebars
<ul>
<li>start</li>
{{#each customers}}
<li>{{name}}</li>
{{/each}}
</ul>
Store
// javacripts/store.js
App.ApplicationAdapter = DS.ActiveModelAdapter.extend({
namespace: 'api/v1'
});
Instead of
{{#each customers}}
It should read either
{{#each controller}}
{{name}}
{{/each}}
or
{{#each customer in controller}}
{{customer.name}}
{{/each}}
I have recently posted two screencasts. One showing how to get started with a new application, and one showing how to setup Grunt:
https://www.youtube.com/watch?v=T-s34EVSE_0
https://www.youtube.com/watch?v=kPaHt_F3VcU
You might also get some use out of a talk I gave earlier this year as well, which goes through developing a simple application during the talk, including Ember Data.
https://www.youtube.com/watch?v=KH5RreHtaaQ
Your customers/index template is referencing a "customers" collection that doesn't exist.
Your route's model hook is returning an array of records, which makes Ember generate an Ember.ArrayController with its model set to your array of customers. It doesn't have a property called "customers", so the {{#each customers}} doesn't have anything to iterate over. If you change it to just {{#each}} (because this in this scope references the controller, which is array-like) or {{#each model}} (to explicitly access the model array of the ArrayController), it should work correctly.
Also, your renderTemplate hook in the Route is the default behavior, so you can just delete it.
Incidentally, I'd recommend just using an Ember JSBin or something while you're starting out and learning the basics, so when you need to ask for help, you can just link to the bin, and people have live code they can work with to help you out, with a minimum of effort. That low barrier to entry makes a big difference to people who are doing free work for internet points.

Ember.js Dynamic segment not working

I'm using Ember 1.0.0 and the latest build of Ember Data (beta), and I have a route with a dynamic segment that isn't working.
I have defined the routes below:
PwdMgr.Router.map(function() {
this.resource("passwords", function(){
this.resource("password", {path: "/:password_id"}, function(){
this.route("edit");
});
});
}
In the template passwords.index I display a list of models like this:
{{#each}}
<tr>
<td>{{id}}</td>
<td>{{name}}</td>
<td>{{client.name}}</td>
<td>{{service.name}}</td>
<td>{{localisation.name}}</td>
<td>{{status.name}}</td>
<td>{{login}}</td>
<td>{{password}}</td>
<td>
{{#link-to 'password.index' this}}<span class="glyphicon glyphicon-search"></span>{{/link-to}}
{{#link-to 'password.edit' this}}<span class="glyphicon glyphicon-pencil"></span>{{/link-to}}
<span class="glyphicon glyphicon-remove" {{action 'edit' password}}></span>
</td>
</tr>
{{/each}}
I have two links, one that goes to the route password.index and one to the route passwword.edit. I provide the model for the dynamic segment and the handlebars creates the URL correctly (/passwords/1 and /passwords/1/edit).
My problem is that when I get to the URL /password/1 (or /password/1/edit), the model is not a single object but an array of objects.
Since I'm using the default pattern, as explained in the guides, I didn't setup Route objects. But for debugging purposes I created a route object for the password.index route. Here's what it looks like:
PwdMgr.PasswordIndexRoute = Ember.Route.extend({
model: function(params){
console.log(params);
return this.get('store').find('password',params.password_id);
},
setupController: function(controller, model){
console.log(model);
}
});
And here's my console log:
Object {} app.js (line 31)
<DS.RecordArray:ember435> { content=[3], store=<DS.Store:ember506>, isLoaded=true, more...} app.js (line 35)
The empty object explains why I get an array of object but is there a reason why the params variable is an empty object?
Thanks a lot
[EDIT]
I have changed my Router.map like so:
PwdMgr.Router.map(function() {
this.resource("passwords", function(){
this.route("detail", {path: "/:password_id"});
this.route("edit", {path: "/:password_id/edit"});
});
}):
And the dynamic segment for both "detail" and "edit" routes works fine. I think the problem comes from the fact that the dynamic segment is in the nested resource, which is strange because the Emberjs guides' examples are with dynamic segments in nested resources.
password/1 doesn't appear to be a real route, I'd try passwords/1, and get rid of the slash on the path.
PwdMgr.Router.map(function() {
this.resource("passwords", function(){
this.resource("password", {path: ":password_id"}, function(){
this.route("edit");
});
});
}
Or change it to
PwdMgr.Router.map(function() {
this.resource("passwords", function(){});
this.resource("password", {path: "password/:password_id"}, function(){
this.route("edit");
});
}
I found my mistake thanks to Daniel's comment.
My routes were setup like this (before my edit):
passwords
password
password.detail
password.edit
password.index
I was using the route PwdMgr.PasswordsRoute to setup my models and its corresponding template passwords.
The problem was that I was in the passwords route and going directly to the password.detail route. I think there was a problem going from passwords to password.detail (or password.edit) with the model parameter.
Anyway, once I changed my route to PwdMgr.PasswordsIndexRoute and the corresponding template to passwords/index, everything was going as expected. Models were passed correctly through the dynamic segment.
Thanks a lot Daniel for pointing out my error.

Emberjs: How to render a view with the view name in variable?

i would like to add some cards to a board, all cards are very different. so i want to create different view for each card that can bind different events and template. I set a 'type' property in the card model which to distinguish the cards.
the board template look like below:
{{#each card in cards}}
{{render card.type card class="card"}}
{{/each}}
However, the first argument for the render help can not be a variable, it can only be the card view name string.
anyone know how to achieve this?
As you already mentioned the built-in render helper only accepts a string to lookup the template to render, therefore one possible solution would be to write your own custom render helper, which then after getting the correct name with card.get('type') delegates the rendering to the built-in render helper. This could look something like this:
Ember.Handlebars.registerBoundHelper('renderCard', function(context, card, options) {
return Ember.Handlebars.helpers.render.call(context, card.get('type'), 'card', options);
});
After that, you could use it like this in your template:
{{#each card in cards}}
{{renderCard this card}}
{{/each}}
Edit
I've also added a basic jsbin that shows it working.
Edit 2
Edited the jsbin again, using object data to be rendered into the template, see here.
Edit 3
Lamentably the DS.FixtureAdapter does not support embedded records which is what you need to make it work. But you could configure you orignal DS.RESTAdapter like this:
App.Adapter = DS.RESTAdapter.extend();
App.Adapter.map('App.Dashboard',
cards: {embedded: 'always'}
);
App.Store = DS.Store.extend({
adapter: App.Adapter
});
This way the card records are always loaded with the parent record. I guess doing this change would make the call to card.get('type') in you handlebar helper return the value rather then undefined.
Hope it helps.
For Ember 1.9 solution above doesn't work. Use another way to register helper:
Ember.Handlebars.registerHelper 'renderDynamic', (context, model, options)->
contextString = options.hash.contextString
model = options.data.view.getStream(model).value()
return Ember.Handlebars.helpers.render(model.get('type'), contextString, options)
And in the template:
= hb 'renderDynamic "this" currentModel contextString="curremtModel"
or in handlebars:
{{renderDynamic "this" currentModel contextString="curremtModel"}}

Categories