I am trying to use the ember-data to build a model from a my own REST service. I have formatted my data according to how I understand the data should be returned from the service, but still stuck.
The issue is that I get no results showing in my view after initial page load. I dont think the model is being populated correctly.
What am I missing?
App = Ember.Application.create();
App.Account = DS.Model.extend({
first: DS.attr( 'string' ),
last: DS.attr( 'string' )
});
App.AccountAdapter = DS.RESTAdapter.extend({
namespace: 'api',
host: 'http://127.0.0.1:3000'
});
App.Router.map(function() {
this.route('home');
});
App.HomeRoute = Ember.Route.extend({
model: function() {
return this.store.find( 'account' );
}
});
App.HomeController = Ember.Controller.extend({
controllerTest : true
});
My data looks like the following:
{
"accounts": {
"id": 1,
"first": "John",
"last": "Doe"
}
}
from url:
http://127.0.0.1:3000/api/accounts
My view template is:
<script type="text/x-handlebars" data-template-name="home">
Home Template {{controllerTest}}
{{#each item in model}}
<br />
{{item.first}}
{{item.last}}
{{/each}}
</script>
Thanks.
I think your JSON format is slightly incorrect. It is my understanding you return a list of accounts, even if there's only one. Try this:
{
"accounts": [
{
"id": 1,
"first": "John",
"last": "Doe"
}
]
}
Try
{{#each }}
<br />
{{first}}
{{last}}
{{/each}}
or
{{log this}}
or use Ember-inspector to see what data do you have and whats going on there.
Related
I am trying to generate click able links using emberjs framework. I have the model setup correctly and I have the following handlebar template:
<script type="text/x-handlebars" data-template-name="index" >
{{#each name in model.mymodules }}
{{#link-to name 'home' }}{{name}}{{/link-to}}
{{/each
</script>
The idea is to call modulename/home on each link.
For ex: say I have 3 modules: "abc", "xyz", "123"
I want three links:
abc <a href="/abc/home">, xyz <a href="/xyz/home">, 123 <a href="/123/home">
What controller/route do I need to define for this to work.
jsfiddle:
http://jsfiddle.net/spkRa/2/
You need to make use of ember resources for dealing with this problem
Read http://emberjs.com/guides/routing/defining-your-routes/
Example of application code should be something like this. JSfidle http://jsfiddle.net/NQKvy/291/
App = Ember.Application.create({
LOG_TRANSITIONS: true,
LOG_TRANSITIONS_INTERNAL: true,
LOG_VIEW_LOOKUPS: true
});
App.Router.map(function() {
this.resource('modules', { path: '/modules' }, function() {
this.route('home', {path: ':module_name/home'});
});
});
App.IndexRoute = Ember.Route.extend({
model:function(){
return App.Modules;
}
});
App.ModulesHomeRoute = Ember.Route.extend({
model: function(params) {
//returns an object from an ember array based on the property value
return App.Module.findProperty('name',params.module_name);
},
serialize: function(model, params) {
//updates the url with the param value
return { module_name: model.get('name') };
}
});
App.Modules = Ember.A([
Ember.Object.create({name:'aaa'}),
Ember.Object.create({name:'bbb'}),
Ember.Object.create({name:'ccc'})
]);
And hadlebars code
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each}}
<li>{{name}}</li>
<li>{{#link-to 'modules.home' this}}{{name}}{{/link-to}}</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="modules/home">
This is the home of the module {{name}}
</script>
I want to simply render an Ember select view with the model defined in a route. Data is coming from fixtures adapter. When doing this, I receive the error: Ember.CollectionView's content must implement Ember.Array - You passed App.AuthorsController.
How can I solve this ?
See JSFIDDLE: http://jsfiddle.net/cyclomarc/frvJZ/4/
(after running the app, click on the 'Authors' link to goto the authors route with authorsController data.
CODE-HTML:
<script type="text/x-handlebars" data-template-name="application">
<h1>Ember select view</h1>
{{#linkTo 'authors'}}Authors{{/linkTo}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="authors">
{{view Ember.Select contentBinding="App.AuthorsController"}}
</script>
CODE-JS:
window.App = Ember.Application.create();
App.Router.map(function () {
this.resource('authors', { path: "/authors" });
});
App.AuthorsRoute = Ember.Route.extend({
model: function () {
return App.Author.find();
}
});
App.AuthorsController = Ember.ArrayController.extend({})
//DATA
//define model for category
App.Author = DS.Model.extend({
name: DS.attr('string'),
language: DS.attr('string')
});
App.Store = DS.Store.extend({
revision: 12,
adapter: 'DS.FixtureAdapter'
});
App.Author.FIXTURES = [
{
id: 1,
name: 'Luc Verschuren',
language: 'German'
},
{
id: 2,
name: 'Patrick Burms',
language: 'Dutch'
},
{
id: 3,
name: 'Jean Demeester',
language: 'French'
}
];
Try using the content property of your App.AuthorsController having the data:
<script type="text/x-handlebars" data-template-name="authors">
{{view Ember.Select
contentBinding="content"
optionLabelPath="content.name"}}
</script>
Working jsfiddle.
Hope it helps.
How do I pass a route name to a {{linkTo}} dynamically?
For example, given this code:
App.Router.map(function() {
this.resource('anon', {path: '/main'},
function() {
this.route('home', {path:'/home'});
this.route('about', { path: '/about' });
this.route('contact', { path: '/contact' });
});
});
App.NavController = Ember.ArrayController.extend({
selectedNav:'',
setNav:function(value){
var nav = App.Nav.find(value);
var items = nav.get('navItems');
this.set('content', items);
}
});
these templates:
<script type="text/x-handlebars" data-template-name="nav">
<ul class="nav">
{{#each in controller}}
{{ partial "basicNav"}}
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="_basicNav">
<li>{{#linkTo navItemPath}}{{navItemName}}{{/linkTo}}</li>
</script>
and these models with the following fixture data:
App.Nav = DS.Model.extend({
navItems:DS.hasMany('App.NavItem'),
name:DS.attr('string')
});
App.NavItem = DS.Model.extend({
nav:DS.belongsTo('App.Nav'),
navItemName:DS.attr('string'),
navItemPath:DS.attr('string')
});
App.Nav.FIXTURES = [
{
id: 10,
name: 'Anon',
navItems: [100,200,300]
}
];
App.NavItem.FIXTURES = [
{
id:100,
nav:10,
navItemName:'Home',
navItemPath:'anon.home'
},
{
id:200,
nav:10,
navItemName:'Contact',
navItemPath:'anon.contact'
},
{
id:300,
nav:10,
navItemName:'About',
navItemPath:'anon.about'
}
];
How do I pass navItemPath to the {{linkTo}} helper? In this code snippet:
{{#linkTo navItemPath}}{{navItemName}}{{/linkTo}}
ember complains that it can't find the "navItemPath" route, like it's looking for it literally. If I replace that with a valid literal route like:
{{#linkTo 'anon.home'}}{{navItemName}}{{/linkTo}}
ember will render the linkTo with the navItemName as expected, so I know the controller is passing it the right data, but of course all the routes are goofy. Am I missing something obvious?
You can't do that with LinkTo helper, you need to bind the href of your link to navItemPath using bindAttr
<a {{bindAttr href="navItemPath"}}>{{navItemName}}</a>
Make sure the the logic rending navItemPath's value takes into account the location API
Using:
ember-1.0.0-pre.4.js
ember-data.js REVISION:11
handlebars-1.0.rc.2.js
Please have a look at this jsFiddle illustrating the described problem.
I have a list of items that are displayed in a template. The template contain a linkTo helper that let's the controller add an item to the collection and is shown as a text input on the page.
Adding the item to the collection is done by the controller:
App.TodoItem = DS.Model.extend({
title: DS.attr('string', { defaultValue: "unknown" })
});
App.Router.map(function () {
this.resource('todo_items')
});
App.TodoItemsRoute = Em.Route.extend({
model: function () {
return App.TodoItem.find();
}
});
App.TodoItemsController = Em.ArrayController.extend({
addTodoItem: function () {
App.TodoItem.createRecord();
}
});
If I want the new item to be shown is the list, I have to pass params to createRecord, otherwise the item is not visible. The same behaviour can be reproduced by using Chrome's inspector and then the item can be made visible as follows:
// Open the jsFiddle http://jsfiddle.net/bazzel/BkFYd/ and select 'result(fiddle.jshell.net) in the inspector, then:
var item = App.TodoItem.createRecord();
// Nothing visible yet.
item.set('title', 'Whatever');
// Now the text input appear with the title as its value.
Is this expected behaviour and if so, what am I missing here?
I took time to redo your example the way i feel things should be done properly with Emberjs. You should rather make sure of transaction and properly define your views and then all your issues get taken care of. So here's how i think you should do this
Define a view for the textfield to capture the value being entered or
just bind it to the model property.
Listing items and adding a new item to the list should be done in two different views and should not be mixed together
<script type="text/x-handlebars">
{{outlet}}
<div>
{{outlet 'addItem'}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="todo_items">
{{#linkTo 'todo_items.new'}}Add Todo Item{{/linkTo}}
<ul>
{{#each item in controller}}
<li>
{{#unless item.isNew}}
{{item.title}}
{{/unless}}
</li>
{{/each}}
</ul>
</script>
Define different states for listing items and adding a new one
To benefit from automatic binding of your text field value to the
model property, you need to associate an ObjectController to the TodoItemsNew route
Finally, make use of transaction to create and commit records to the store
window.App = Em.Application.create();
App.TodoItem = DS.Model.extend({
title: DS.attr('string')
});
App.TodoItem.FIXTURES = [{
id: 1,
title: 'Lorem'
}, {
id: 2,
title: 'Ipsum'
}];
App.store = DS.Store.create({
revision: 11,
adapter: DS.FixtureAdapter.create()
});
App.Router.map(function () {
this.resource('todo_items',function(){
this.route('new');
})
});
App.IndexRoute = Em.Route.extend({
redirect: function () {
this.transitionTo('todo_items');
}
});
App.TodoItemsRoute = Em.Route.extend({
model: function () {
return App.TodoItem.find();
}
});
App.TodoItemsNewRoute = Em.Route.extend({
transaction: App.store.transaction(),
setupController:function(controller) {
console.info(controller.toString());
controller.set('content',this.transaction.createRecord(App.TodoItem));
},
renderTemplate: function() {
this.render('addItem',{
into:'application',
outlet:'addItem',
})
},
events: {
addItem: function() {
this.transaction.commit();
this.transitionTo('todo_items');
}
}
});
App.TodoItemsController = Em.ArrayController.extend();
App.TodoItemsNewController = Em.ObjectController.extend();
App.TextField = Ember.TextField.extend({
insertNewline: function () {
this.get('controller').send('addItem')
}
});
Here' is a working version of the example on jsfiddle. Hopefully, i helped with this example clarify some of your issues.
Thank you Ken for answering my question. It indeed feels like a more proper of way of doing this in Ember. However, I still think it's difficult to get the hang of which objects are accessible from where...
Your example inspired me to do a rewrite of my code. I also made some changes to your approach:
I'm not sure if it's the best practice, my I don't create a store instance. Instead I define a Store class.
The content for the TodoItemsNewController is set by calling the model property on the corresponding route.
renderTemplate in the TodoItemsNewRoute only needs the outlet key.
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="todo_items">
{{#linkTo 'todo_items.new'}}Add Todo Item{{/linkTo}}
<ul>
{{outlet "addItem"}}
{{#each controller}}
<li>
{{#unless isNew}}
{{title}}
{{/unless}}
</li>
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="todo_items/new">
{{view Ember.TextField valueBinding="title" placeholder="Enter title"}}
window.App = Em.Application.create();
App.TodoItem = DS.Model.extend({
title: DS.attr('string', {
defaultValue: "unknown"
})
});
App.TodoItem.FIXTURES = [{
id: 1,
title: 'Lorem'
}, {
id: 2,
title: 'Ipsum'
}];
App.Store = DS.Store.extend({
revision: 11,
adapter: DS.FixtureAdapter.create()
});
App.Router.map(function() {
this.resource('todo_items', function() {
this.route('new');
});
});
App.IndexRoute = Em.Route.extend({
redirect: function() {
this.transitionTo('todo_items');
}
});
App.TodoItemsRoute = Em.Route.extend({
model: function() {
return App.TodoItem.find();
}
});
App.TodoItemsNewRoute = Em.Route.extend({
model: function() {
return App.TodoItem.createRecord();
},
renderTemplate: function() {
this.render({
outlet: 'addItem'
});
}
});
App.TodoItemsNewView = Em.View.extend({
tagName: 'li'
});
The updated example is on jsFiddle.
Any reviews are welcome.
For the application I am developing Ember.js + Ember Data seems like a good solution. However I can not even get a simple version using both libraries to work. The problem is that the data provided by my JSON file is not correctly loaded or shown.
My app.js looks like. I run all libraries on the edge.
var App = Em.Application.create({});
App.store = DS.Store.create({
revision: 6,
adapter: DS.RESTAdapter.create({
bulkCommit: false
})
});
App.Item = DS.Model.extend({
pluginName: DS.attr('string')
});
App.regionController = Em.ArrayController.create({
content: App.store.findAll(App.Item)
});
I have one template that looks like:
<script type="text/x-handlebars">
<ul>
{{#each regionController}}
<li>{{item}}</li>
{{/each}}
</ul>
</script>
The request to the json file is made correctly (I see the request pop up in Firebug) and has the following contents:
{
items: [{
"id": "3",
"pluginName": "text"
},
{
"id": "3",
"pluginName": "split"
}]
}
Can anyone spot what I am doing wrong?
Your template should probably look like this:
<script type="text/x-handlebars">
<ul>
{{#each item in regionController}}
<li>{{item.pluginName}}</li>
{{/each}}
</ul>
</script>
Let me know if that works for you.