Filling Ember selection with data received from server - ember.js

I would like to receive json data from server and then generate html selection tag in Ember. This is my handlebars template:
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="rankings">
{{view Ember.Select contentBinding="countries" optionLabelPath="countries.name" optionValuePath="countries.path"}}
</script>
and my Ember trunk
App.ApplicationAdapter = DS.RESTAdapter;
App.Router.map(function() {
this.route('rankings')
});
App.RankingsRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('countries', this.store.findAll('country'));
}
});
App.RankingsController = Ember.Controller.extend({
countries: []
});
App.Country = DS.Model.extend({
path: DS.attr('string'),
name: DS.attr('string'),
}
It is not working fine. I am not having any errors in console and template is not rendering with this route and controller. What can be wrong?

Ember.Select looks for data on your controller's model. You have the countries array set up outside of your content, so it will not be able to view the data.
Personally, I would do it in the following way, though there are a couple of different approaches you can take to get similar results:
App.ApplicationAdapter = DS.RESTAdapter;
App.Router.map(function() {
this.route('rankings')
});
App.RankingsRoute = Ember.Route.extend({
model: function() {
return {countries : this.store.findAll('country')};
}
});
App.RankingsController = Ember.Controller.extend({
});
App.Country = DS.Model.extend({
path: DS.attr('string'),
name: DS.attr('string'),
}

Related

FirebaseAdapter not recognized on my Ember app

I'm trying to use Firebase with my Ember app. I installed Emberfire but my app returns
Uncaught TypeError: Cannot read property 'extend' of undefined
Because it doesn't read 'FirebaseAdapter'
App.ApplicationAdapter = DS.FirebaseAdapter.extend({
firebase: new Firebase('https://glowing-fire-number.firebaseio.com/')
});
I checked if Ember data is being loaded before Firebase and EmberFire too. Here are my script references:
<!-- script references -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="http://maps.googleapis.com/maps/api/js?sensor=false&extension=.js&output=embed"></script>
<script src="js/scripts.js"></script>
<script src="js/libs/jquery-1.11.2.min.js"></script>
<script src="js/libs/handlebars-v1.3.0.js"></script>
<script src="js/libs/ember.js"></script>
<script src="js/libs/moment.js"></script>
<script src="http://builds.emberjs.com/tags/v1.0.0-beta.12/ember-data.js"></script>
<script src="js/app.js"></script>
<script src="vendor/list-view.js"></script>
<script src="js/controller.js"></script>
<script src="https://cdn.firebase.com/js/client/2.2.2/firebase.js"></script>
<script src="https://cdn.firebase.com/libs/emberfire/1.3.3/emberfire.min.js"></script>
And my App.js
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
this.resource('stacks')
this.resource('stack', {path: ':stack_id'}, function() {
this.route('edit');
});
this.route('create');
});
App.IndexRoute = Ember.Route.extend({
beforeModel: function() {
this.transitionTo('stacks');
}
});
App.StacksRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('stack');
}
});
App.StackRoute = Ember.Route.extend({
model: function(params) {
return stacks.findBy('id', params.stack_id);
}
});
Ember.Handlebars.helper('format-date', function(date) {
return moment(date).fromNow();
});
App.CreateRoute = Ember.Route.extend({
});
//EmberFire stuff
App.ApplicationAdapter = DS.FirebaseAdapter.extend({
firebase: new Firebase('https://glowing-fire-2514.firebaseio.com/')
});
App.Stack = DS.Model.extend({
title: DS.attr('string'),
location: DS.attr('string'),
date: DS.attr('number'),
details: DS.attr('string')
});
Any idea? I appreciate your help!
It looks like you need to include app.js after you include EmberFire. There's also a missing semicolon in your router after this.resource('stacks') but I don't think that's causing the error.
We just released some updates that make it much easier to use EmberFire as an ember-cli addon, I'd recommend checking it out. Details are here: https://www.firebase.com/blog/2015-03-09-new-emberfire-features.html

Ember How can i render a child route into his parent?

I'm new with Ember and trying to render the content into the category template. So, if I clicked on the category, it will show me details and list content in the category template. I have tested something, but it didn't work. I have searched for this problem, but I can't solve it. I hope you can help me.
best regards
app.js
TableNotices.Router.map(function() {
this.resource('tableNotices', { path: '/' }, function(){
this.resource('category', {path: ':id'}, function(){
this.route('contents');
this.resource('content', {path: '/content/:id'});
});
});
});
TableNotices.ContentsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('category').get('contents');
}
});
TableNotices.Content = DS.Model.extend({
content: DS.attr('string'),
contentType: DS.attr('string'),
orderPos: DS.attr('number'),
category: DS.belongsTo('category')
});
TableNotices.Category = DS.Model.extend({
name: DS.attr('string'),
parent: DS.attr('number'),
picture: DS.attr('string'),
contents: DS.hasMany('content', {async:true})
});
index.html:
<script type="text/x-handlebars" data-template-name="category">
{{name}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="contents">
foobar
</script>
jsbin
In the route you can specify how to render the templates with renderTemplate.
TableNotices.ContentsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('category').get('contents');
}
renderTemplate: function() {
this.render('contents', { // the template to render
into: 'category', // the template to render into
outlet: 'category', // the name of the outlet in that template
controller: 'contents' // the controller to use for the template
});
}
});
<script type="text/x-handlebars" data-template-name="category">
{{name}}
{{outlet "contents"}}
</script>
Although Aaron Renoir's answer will work, the best way to think of this is that because your routes are nested, your templates need to be nested as well. Ember will render each of the nested templates into the parent template when these nested routes are activated. This gives you a nice way to delegate different parts of your page to various templates which helps to keep the content organized.
Each of your "parent" templates will need an {{outlet}} for the child templates to be rendered into.

Ember Select-view error: Ember.CollectionView's content must implement Ember.Array

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.

Emberjs Application Routes and Controllers

Can someone explain why this works:
Code in App.js:
App.ApplicationRoute = Ember.Route.extend({
setupController : function (params) {
this.controllerFor('food').set('model', App.Food.find(params.food_id));
}
});
But the following won't, unless I explicitly declare App.FoodController = Ember.ObjectController.extend();
App.FoodRoute = Ember.Route.extend({
model : function(params) {
return App.Food.find(params.food_id);
}
});
This is the code I'm using in index.html and does not change between blocks of code
<script type="text/x-handlebars" data-template-name="application">
{{ outlet }}
</script>
<script type="text/x-handlebars" data-template-name="food">
{{name}}
</script>
Router:
App.Router.map(function() {
this.resource( 'foods' );
this.resource( 'food', { path : '/food/:food_id' } );
});
The code that you have shown seems OK. Here is a working fiddle that proves it:
http://jsfiddle.net/ebXeS/2/
The only thing wrong about the code is this part (which is excluded from the fiddle):
App.ApplicationRoute = Ember.Route.extend({
setupController : function (params) {
this.controllerFor('food').set('model', App.Food.find(params.food_id));
}
});
According to your Router definition, you should not have food_id in the parameters of your application route. More than that, you should access the controller for the food route in the uhm... FoodRoute. Read more about Ember and the way it does routing (http://emberjs.com/guides/).

createRecord called w/o params does not add object to collection

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.