I'm trying to render a view Team inside of an {{outlet}}. This Team view is comprised of a simple Person view (the team leader), and a collection of Person views (team members). The outlet is set up by calling connectOutlet() on the ApplicationController.
Although the Person child views are rendered in the markup as expected, all the values of name are missing. It sure seems like my bindings and/or controller are not set up properly. What am I missing?
Code and demo: http://jsfiddle.net/aek38/fkKFJ/
The relevant handlebar templates are:
<script type="text/x-handlebars" data-template-name="app">
<div class="container">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="person">
<em>Person name is:</em> {{name}}
</script>
<script type="text/x-handlebars" data-template-name="team">
<h3>Team Leader</h3>
<em>Leader name should be:</em>{{leader.name}}
{{view App.PersonView contentBinding="leader"}}
<h3>Team Members</h3>
{{#collection contentBinding="members"}}
{{view App.PersonView contentBinding="content"}}
{{/collection}}
</script>
Code snippet:
App = Ember.Application.create({
ready: function() {
this.initialize();
},
ApplicationController: Ember.Controller.extend(),
ApplicationView: Ember.View.extend({
templateName: "app"
}),
Person: Ember.Object.extend({
name: "Jane Doe"
}),
PersonController: Ember.ObjectController.extend(),
PersonView: Ember.View.extend({
templateName: "person"
}),
Team: Ember.Object.extend({
members: [],
leader: null
}),
TeamController: Ember.ObjectController.extend(),
TeamView: Ember.View.extend({
templateName: "team"
}),
// ...
You can use
{{view App.PersonView contextBinding="leader"}}
and use {{#each}} instead of {{#collection}} for the members
http://jsfiddle.net/LQTsV/1/
Not very sure whats going on but I tweaked your fiddle to get it working:
http://jsfiddle.net/lifeinafolder/sPcwv/
Seems as if bindings are not working properly in the sense:
contentBinding="this"
works but
contentBinding="this.leader"
doesn't.
Sorry but I couldn't work it out more.
You're setting the content variable on person view via contentBinding that should probably be personBinding. And then in your template you should get the view.person.name.
{{collection}} should be a {{each}} block.
{{#each members}}
{{view App.PersonView personBinding="this"}}
{{/each}}
And this person template will look in the right location for the name.
<script type="text/x-handlebars" data-template-name="person">
<em>Person name is:</em> {{view.person.name}}
</script>
Didn't change your js.
fiddle: http://jsfiddle.net/albertjan/fkKFJ/9/
Related
I have a template in wish I have 2 components that represents different Models. I need to create a model that contains both datas, for each component. If I have only one model everything works fine, but when I add other one, then this error happens in the console.
Error while processing route: events record is undefined ember$data$lib$system$store$finders$$_find/</<#http://localhost:1361/test/frontend/bower_components/ember-data/ember-data.prod.js:7475:11
Backburner.prototype.run#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:222:18
ember$data$lib$system$store$$Store<._adapterRun#http://localhost:1361/test/frontend/bower_components/ember-data/ember-data.prod.js:13133:16
ember$data$lib$system$store$finders$$_find/<#http://localhost:1361/test/frontend/bower_components/ember-data/ember-data.prod.js:7470:1
tryCatch#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:53070:14
invokeCallback#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:53085:15
publish#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:53053:9
#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:31253:7
Queue.prototype.invoke#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:901:9
Queue.prototype.flush#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:965:11
DeferredActionQueues.prototype.flush#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:765:11
Backburner.prototype.end#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:158:9
Backburner.prototype.run#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:226:13
run#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:19151:12
ember$data$lib$adapters$rest$adapter$$RestAdapter<.ajax/</hash.success#http://localhost:1361/test/frontend/bower_components/ember-data/ember-data.prod.js:1728:15
n.Callbacks/j#http://localhost:1361/test/frontend/bower_components/jquery/dist/jquery.min.js:2:26920
n.Callbacks/k.fireWith#http://localhost:1361/test/frontend/bower_components/jquery/dist/jquery.min.js:2:27738
x#http://localhost:1361/test/frontend/bower_components/jquery/dist/jquery.min.js:4:11251
.send/b/<#http://localhost:1361/test/frontend/bower_components/jquery/dist/jquery.min.js:4:14765
The route of the template that contains the components is:
App.EventsRoute = Ember.Route.extend({
model: function()
{
return Ember.RSVP.hash({
event: this.store.find('event'),
featured: this.store.find('event', 'featured')
});
}
});
Here is my template:
<script type="text/x-handlebars" id="events">
<div class="col-xs-8 pull-left">
{{#each event as |event|}}
{{#event-box event=event}}{{/event-box}}
{{else}}
no events
{{/each}}
...
{{#each featured as |highlight|}}
{{#highlight-event hlevent=highlight}}
{{/highlight-event}}
{{else}}
No Highlights
{{/each}}
...
</script>
Does anyone knows why this error happens and what can I do to solve it?
this.store.find('event', 'featured') is invalid.
Also, find is deprecated.
With the latest Ember Data, you should use store.query().
You'd probably use it like
this.store.query('event', {featured: true})
or
this.store.query('event', {filter: 'featured'})
You'll need to adjust your adapter accordingly. Something like:
urlForQuery: function(query, modelName) {
if (query && query.featured === true) {
delete query.featured;
return this._buildURL(modelName, 'featured');
}
return this._super(query, modelName);
}
This should generate an URL like http://..../events/featured.
I'm trying to understand certain peculiarity.
Setting xxx property and iterating #each in one controller works, while seemingly same operation with yyy #each doesn't...
I'm including highlights of the code and the runnable code snippet:
App.IndexController = Ember.Controller.extend({
xxx : [{name:"a"}, {name:"b"}], // this works just fine
});
{{#each item in xxx}}
<li>{{item.name}}</li>
{{/each}}
App.ColorController = Ember.Controller.extend({
yyy : [{name:"c"}, {name:"d"}], // this triggers deprecation
// You attempted to access `yyy` from ...
// But object proxying is deprecated. Please use `model.yyy` instead
});
{{#each item in yyy}}
<li>{{item.name}}</li>
{{/each}}
App = Ember.Application.create();
App.Color = DS.Model.extend({
name: DS.attr('string')
});
App.Router.map(function() {
this.resource('color', function(){
this.route('show', { path: ':color_id' });
});
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return [
{ id: 1, name: "Red" },
{ id: 2, name: "Blue" },
];
}
});
App.IndexController = Ember.Controller.extend({
xxx : [{name:"a"}, {name:"b"}], // this works just fine
});
App.ColorController = Ember.Controller.extend({
init : function() {
this._super();
console.info("Just to double check, this controller gets initialised");
},
yyy : [{name:"c"}, {name:"d"}], // this triggers deprecation
// You attempted to access `yyy` from ...
// But object proxying is deprecated. Please use `model.yyy` instead
});
<script type="text/x-handlebars">
<h2>Ember Starter Kit</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
<h3>Index</h3>
<ul>
{{#each color in model}}
<li>{{#link-to "color.show" color}} {{color.name}} {{/link-to}}</li>
{{/each}}
</ul>
<ul>
{{#each item in xxx}}
<li>{{item.name}}</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" id="color/show">
<h3>color/show</h3>
<h4>{{ model.name }}</h4>
<ul>
{{#each item in yyy}}
<li>{{item.name}}</li>
{{/each}}
</ul>
{{#link-to "application"}}Go back to the list{{/link-to}}
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.2/ember.debug.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.2/ember-template-compiler.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.2/ember-data.js"></script>
I'd like to learn more:
why it works in one case and doesn't work in another?
what is the Ember way of fixing it?
EDIT: Updated code snippet include Color model. To trigger deprecation warning click on one of the colours (Red, Blue)... This is what happens when I run the snippet:
Okay, as I expected - problem lies in naming conventions and relics of the past(ObjectController). Declaring ColorController creates controller for model, not a route. You need here controller for route, so changing ColorController to ColorShowController solves problem and values render. Deprecation's gone.
App = Ember.Application.create();
App.Color = DS.Model.extend({
name: DS.attr('string')
});
App.Router.map(function() {
this.resource('color', function(){
this.route('show', { path: ':color_id' });
});
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return [
{ id: 1, name: "Red" },
{ id: 2, name: "Blue" },
];
}
});
App.IndexController = Ember.Controller.extend({
xxx : [{name:"a"}, {name:"b"}], // this works just fine
});
App.ColorShowController = Ember.Controller.extend({
init : function() {
this._super();
console.info("Just to double check, this controller gets initialised");
},
yyy : [{name:"c"}, {name:"d"}], // this triggers deprecation
// You attempted to access `yyy` from ...
// But object proxying is deprecated. Please use `model.yyy` instead
});
<script type="text/x-handlebars">
<h2>Ember Starter Kit</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" id="index">
<h3>Index</h3>
<ul>
{{#each color in model}}
<li>{{#link-to "color.show" color}} {{color.name}} {{/link-to}}</li>
{{/each}}
</ul>
<ul>
{{#each item in xxx}}
<li>{{item.name}}</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" id="color/show">
<h3>color/show</h3>
<h4>{{ model.name }}</h4>
<ul>
{{#each item in yyy}}
<li>{{item.name}}</li>
{{/each}}
</ul>
{{#link-to "application"}}Go back to the list{{/link-to}}
</script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.2/ember.debug.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.2/ember-template-compiler.js"></script>
<script src="http://builds.emberjs.com/tags/v1.13.2/ember-data.js"></script>
I've made a jsbin to illustrate my issue.
the binding seems KO with lastname property defined inside the itemController and the fullname value is not updated in my items loop.
What am I doing wrong ?
Controller for item in list is different than one you edit property lastname for, so it will never get updated. Propery lastname has to be specified as Model's property (if using Ember Data you simply don't use DS.attr for it and it won't be persisted). If you use custom library for data persistence you have to manually remove lastname property. You can use Ember Inspector extension to see that there are 5 controllers when you click on item. 4 for each item in list and one is being generated when you click. You edit property lastname for this fifth controller. To solve this you can use:
JavaScript:
App = Ember.Application.create();
App.Router.map(function() {
this.resource('items', function() {
this.resource('item', {path: '/:item_id'});
});
});
App.Model = Ember.Object.extend({
firstname: 'foo',
lastname: 'bar',
fullname: function() {
return this.get('firstname') + ' ' + this.get('lastname');
}.property('firstname', 'lastname')
});
App.ItemsRoute = Ember.Route.extend({
model: function() {
return [App.Model.create({id: 1}), App.Model.create({id: 2}), App.Model.create({id: 3}), App.Model.create({id: 4})];
}
});
App.ItemRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor('items').findBy('id', +params.item_id);
}
});
Templates:
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{link-to "items" "items"}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="items">
<ul>
{{#each item in model}}
<li>
{{#link-to 'item' item.id}}
{{item.fullname}} {{item.id}}
{{/link-to}}
</li>
{{/each}}
</ul>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="item">
{{input value=model.firstname}}
{{input value=model.lastname}}
{{model.fullname}}
</script>
Please keep in mind that ArrayController and ObjectController aren't recommended to use, because they will be deprecated in future. Demo.
I have some nested routes.
App.Router.map(function() {
this.route("dashboard", { path: "/dashboard" });
this.resource("customers", { path: "/customers" },function(){
this.resource("customer",{ path: "/:customer_id" },function(){
this.resource("customer.contact",{path:'/contact'});
});
});
});
TEMPLATES
customers/index
<script type="text/x-handlebars" data-template-name="customers/index">
<h3>Customers</h3>
<table>
{{#each item in model}}
<tr>
<td>{{item.name}}</td>
{{#link-to "customer" item tagName="td"}}Info{{/link-to}}
</tr>
{{/each}}
</table>
</script>
customer
<script type="text/x-handlebars" data-template-name="customer">
<h3>Customer {{name}}</h3>
{{#link-to}}Gallery{{/link-to}}
{{#link-to "customer.contact" this}}Contact{{/link-to}}
{{outlet}}
</script>
contact
<script type="text/x-handlebars" data-template-name="customer/contact">
<h3>Contact info of customer {{name}}</h3>
{{contact}}
</script>
Go Customers->Info
Everything works fine, the link from "customers/index" template passes the item to the customer template where {{name}} will be used. but if i want to pass the context to "contact" template, it doesnt work.
here is the JsBin
http://emberjs.jsbin.com/EveQOke/107
You need to specify a route for customer contact (as well for customer). The reason it works initially is because the link-to is passing the model to the route, so it can skip the non-existent model hook. But when you refresh the page, or hit the contact route, which has no dynamic segment, you need to tell ember that you want to use a model. There is a beta feature that allows all the routes under a resource to use the resource if they don't have another resource defined, but that's still a feature, and isn't yet gold.
App.CustomerRoute = Ember.Route.extend({
model: function(param){
return this.store.find('customer', param.customer_id);
}
});
App.CustomerContactRoute = Ember.Route.extend({
model: function(){
return this.modelFor('customer');
}
});
http://jsbin.com/EveQOke/110/edit
What I'm trying to do is very basic but I'm having very little luck...
Simply enough, I don't want to display a chunk of HTML until a certain Ember Data model property is fully loaded.
As you can see from the jsfiddle, the parent model: App.Person gets loaded into the DOM and it also loads the 3 placeholders for its hasMany property belts.
It then executes the request to populate App.Belt and fills in the placeholders.
While this is usually ok, it makes a big mess of things when trying to build an SVG, for example. Since the surrounding <svg> tags will get appended to the DOM immediately and then some time down the track (once the asynchronous request returns data), the inner svg components will be added between the tags. This usually creates browser rendering errors.
TL;DR
In the example, how do I defer the <h3>...</h3> section of the template from being added to the DOM until the model data and its relationships (belts) are fully loaded? This way everything gets visually and physically added to the DOM at once.
The JS:
// Create Ember App
App = Ember.Application.create();
// Create Ember Data Store
App.store = DS.Store.create({
revision: 11,
//Exagerate latency to demonstrate problem with relationships being loaded sequentially.
adapter: DS.FixtureAdapter.create({latency: 5000})
});
// Create parent model with hasMany relationship
App.Person = DS.Model.extend({
name: DS.attr( 'string' ),
belts: DS.hasMany( 'App.Belt' )
});
// Create child model with belongsTo relationship
App.Belt = DS.Model.extend({
type: DS.attr( 'string' ),
parent: DS.belongsTo( 'App.Person' )
});
// Add Parent fixtures
App.Person.FIXTURES = [{
"id" : 1,
"name" : "Trevor",
"belts" : [1, 2, 3]
}];
// Add Child fixtures
App.Belt.FIXTURES = [{
"id" : 1,
"type" : "leather"
}, {
"id" : 2,
"type" : "rock"
}, {
"id" : 3,
"type" : "party-time"
}];
// Set route behaviour
App.IndexRoute = Ember.Route.extend({
model: function() {
return App.Person.find();
},
renderTemplate: function() {
this.render('people');
}
});
The HTML/HBS:
<script type="text/x-handlebars">
<h1>Application</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" id="people">
<h3>Don't load this header until every belt defined in App.Person.belts is loaded</h3>
<ul>
{{#each controller}}
{{debugger}}
<li>Id: {{id}}</li>
<li>Name: {{name}}</li>
<li>Belt types:
<ul>
{{#each belts}}
<li>{{type}}</li>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</script>
The fiddle: http://jsfiddle.net/zfkNp/4/
Check for the controller.content.length and belts.isLoaded, See the jsfiddle for a solution.
<script type="text/x-handlebars" id="people">
{{#if controller.ready}}
<h3>Don't load this header until every belt defined in App.Person.belts is loaded</h3>
{{/if}}
<ul>
{{#each controller}}
{{debugger}}
{{#if belts.isLoaded}}
<li>Id: {{id}}</li>
<li>Name: {{name}}</li>
<li>Belt types:
<ul>
{{#each belts}}
<li>{{type}}</li>
{{/each}}
</ul>
</li>
{{/if}}
{{/each}}
</ul>
</script>
App.IndexController = Ember.ArrayController.extend({
content: null,
ready:function() {
return this.get('content.length')>0
}.property('content.length')
});