So I want to use query parameters in the URL of my application. The Ember guide describes the solution: http://emberjs.com/guides/routing/query-params/
Unsuccessfully I tried it out in my ember-cli project, so I've set up a small test project without cli.
Route:
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
Template:
<script type="text/x-handlebars" id="index">
<ul>
{{#each item in model}}
<li {{action 'pickColour' item}}>{{item}}</li>
{{/each}}
</ul>
<div>Currently selected: {{selected}}</div>
</script>
Controller:
App.IndexController = Ember.ArrayController.extend({
queryParams: ['selected'],
selected: null,
actions: {
pickColour: function(colour) {
console.log("Colour " + colour + " selected");
this.set('selected', colour);
}
}
});
According to the Ember guide this should bind the selected field of the controller to the url parameters. But in this case no url parameters is set when I click a specific colour.
It should be so simple yet I can't make it work. Am I gloriously overlooking something?
Edit: SOLUTION
I missed the fact that for now it's only available in the beta. If you read this in the future, be aware that it might be available in the latest full release.
This is working just fine in version 1.9.0, so this question can probably be closed.
Working demo here
Related
I am new to Ember.js, and I am building a web application that is using Ember.js and Ember-Data for its front-end technology. I am trying to understand what would be the best practice for when you might have multiple ember-data bound components on a page that use an independent model.
Here is kind of what I'm trying to do:
https://gist.github.com/redrobot5050/6e775f4c5be221cd3c42
(There's a link on the page to editing it within jsbin this gist. For some reason, I can't get a 'Share' URL off the vanity URL.)
I have a template like so:
<script type="text/x-handlebars" data-template-name="index">
<p>Options for graphics quality: </p>
<ul>
{{#each item in model}}
<li>{{item.setting}}</li>
{{/each}}
</ul>
<p>Currently Selected: {{model.selectedValue}}</p>
<p>Draw Distance Options:</p>
<ul>
{{#each item in dropdown}}
<li>{{item.distance}}
{{/each}}
</ul>
<p>Currently Selected Distance: {{selectedDistance}}
</p>
{{outlet}}
<button {{action 'openModal' 'modal' model}}>Change Settings</button>
</script>
In this template, all the data binds correctly and appears in scope. However, when I attempt to modify it within its modal dialog box, only Quality is bound to its Ember.Select view. I have attempted to force the binding in the IndexController with a controller.set but it does not appear to be working.
My IndexController looks like this:
App.IndexRoute = Ember.Route.extend({
model: function() {
var qualityList = this.store.find('quality');
console.log('qualityList=' + qualityList);
return qualityList;
//return App.Quality.FIXTURES;
},
setupController : function(controller, model) {
controller.set('model', model);
var drawDistanceList = this.store.find('drawDistance');
console.log('distanceList=' + drawDistanceList );
controller.set('dropdown', drawDistanceList);
controller.set('selectedDistance', 1);
//this.controllerFor('modal').set('dropdown', drawDistanceList );
}
});
The JSBin really shows off what I am attempting to do: I want to load/bind each of the drop downs independently from the same controller. The JSBin does not work correctly because I'm not really sure how to do this, just yet. I am posting to stackExchange to see if someone can modify this JSBin and show me what I'm doing wrong, or if someone can point me in the right direction, design-wise on how to solve this problem?
(For example, I think a possible solution could be to create the dropdowns as components, and load the data through their controller or pass it in as properties from the parent controller, but I really want to know what is the "The Ember Way").
App.IndexRoute = Ember.Route.extend({
model: function() {
return Ember.RSVP.hash({
qualityList: this.store.find('quality'),
drawDistanceList: this.store.find('drawDistance')
});
},
setupController: function(controller, model) {
controller.set('model', model.qualityList);
controller.set('dropdown', model.drawDistanceList);
}
});
Documentation for Ember.RSVP.hash used to be here: http://emberjs.com/api/classes/Ember.RSVP.html#method_hash. I'm not sure why it has disappeared.
For the moment, you can find it at: http://web.archive.org/web/20140718075313/http://emberjs.com/api/classes/Ember.RSVP.html#method_hash
I am using ember 1.3.1 and ember-data 1.0.0-beta.5. On creating new mode I get following error
Assertion failed: Cannot clone an Ember.Object that does not implement Ember.Copyable
Following is my model code
App.myModel = DS.Model.extend({
name : DS.attr('string'),
age : DS.attr('string')
});
In my create route model function
return Em.Object.create({});
and finally on save I do following
this.store.createRecord('property', this.get('model'));
Although despite the error, my backend service is called successfully and new model is saved.
Please guide.
Thanks
I had the same issue which I fixed by doing the following:
In the model function of the route replace
return Em.Object.create({});
with
return this.store.createRecord('myModel');
and on save replace
this.store.createRecord('myModel', this.get('model'));
with
this.get('model').save();
For the sake of completeness, in the scenario described by #acidleaf this is the solution offered by Yehuda Katz from the ember core team in this video:
Off the Menu: Building a Client-Side With Ember and Rails - Yehuda Katz # Rails Israel 2013
In the route from which you're returning a list of resources to display (i.e the plural version of the resource StoriesRoute, PostsRoute, etc..), you'll returned a filtered list containing those which are not new:
model: function() {
this.store.find('myModel');
return this.store.filter('myModel',function(myModel){
return !myModel.get('isNew');
});
}
I am quite new to Ember and still trying to catch all problems caused when migrating to newer versions of Ember and Ember Data, but...
On one hand I think you have a mistake in last code block and that it should be:
this.store.createRecord('myModel', this.get('model'));
// myModel instead of property
But on the other hand I dont think this will be the problem :-/
anyway, try to look (and compare) to changes for Ember data here: https://github.com/emberjs/data/blob/master/TRANSITION.md
and also on this http://discuss.emberjs.com/t/createrecord-using-this-get-model-throws-an-error/3968 or similiar
hope it helps!
J.
I have ran into this problem while learning Ember. The accepted answer works, but it first creates a new empty record in the store. This was not desired in my application as it displays the empty record in my view.
My Solution
Router
App.ItemsNewRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('content', {});
}
});
Controller
App.ItemsNewController = Ember.ObjectController.extend({
actions: {
save: function() {
this.store.createRecord('item', {
title: this.get('newTitle'),
category: this.get('newCategory')
}).save();
this.transitionToRoute('items');
}
}
});
Template
<script type="text/x-handlebars" data-template-name="items">
<ul class="list-group">
{{#each}}
<li class="list-group-item">{{title}} - {{category}}</li>
{{/each}}
{{outlet}}
<li class="list-group-item">{{#link-to "items.new"}}Add{{/link-to}}</li>
</ul>
</script>
<script type="text/x-handlebars" data-template-name="items/new">
<li class="list-group-item">
{{input class="form-control" value=newTitle placeholder="Title"}}
{{input class="form-control" value=newCategory placeholder="Category"}}
<button class="btn btn-default" {{action "save"}}>Save</button>
</li>
</script>
I tried to implement user name displaying after log in. It displays in top menu. But top menu is getting displayed before log in, so it user name is getting cached.
I tried many approaches, and using volatile() is seems the best option, but it doesn't work. In this simple example currentTime calculates only once:
<script type="text/x-handlebars" data-template-name="application">
{{currentTime}}
</script>
App.ApplicationController = Ember.Controller.extend({
currentTime: function() {
console.log('computing value');
var time = new Date();
return time;
}.property().volatile()
});
Ember version 1.3
P.S. I prepared the gist to illustrate this issue: http://jsbin.com/OPUSoTaF/1
Actually, I can't find ANY way do display dynamic value in Ember's application template. Tried to display value from another controller using {{render}} helper, value still gets cached.
It seems that I just need to update value on ApplicationController from some other controller, and to do it in a proper way. Like this:
App.LoginController = Ember.Controller.extend({
needs: 'application',
setTime: function() {
this.get('controllers.application').set('currentTime', new Date());
}
});
The application to illustrate: http://jsbin.com/OPUSoTaF/4/edit
You can change ember properties and thus views using Handlebars {{action 'actionName'}} helper. You can add action helper to almost any UI element in your handlebars template an it is usually triggered on click. When triggered it calls actionName method on the controller.
Example:
Handlebars template:
<script type="text/x-handlebars" data-template-name="application">
<button {{action 'login'}}>Login</button>
{{loginTime}}
</script>
Controller:
App.ApplicationController = Ember.Controller.extend({
loginTime: 'User not logged in yet',
actions: {
login: function() {
// ... Do some login stuff ...
this.set('loginTime', new Date());
}
}
});
Working jsbin example is here: http://jsbin.com/udUyOXaL/1/edit
I am trying to set up a dashboard that can monitor and display information on multiple models. The ArrayController seems like the correct object to use, but I am not sure what I am doing wrong.
Can someone explain where I've gone astray here?
jsBin Example: http://jsbin.com/IgoJiWi/8/edit?html,js,output
javascript:
App = Ember.Application.create();
/* ROUTES */
App.Router.map(function() {
this.resource('options');
this.resource('dashboard');
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('options');
}
});
App.OptionsRoute = Ember.Route.extend({
model: function() {
var a = Em.A();
a.pushObject( App.Options.create({title: 'A', cost: '100'}));
a.pushObject( App.Options.create({title: 'B', cost: '200'}));
a.pushObject( App.Options.create({title: 'C', cost: '300'}));
return a;
}
});
/* MODELS */
App.Options = Ember.Object.extend({
title: '',
cost: '',
quantity: ''
});
/* CONTROLLERS */
App.optionsController = Ember.ArrayController.extend({
legend: 'test',
len: this.length,
totalCost: function() {
return this.reduce( function(prevCost, cost, i, enumerable){
return prevCost + cost;
});
}.property('#each.cost')
});
handlebars:
<script type="text/x-handlebars" data-template-name="application">
<p><strong>Ember.js example</strong><br>Using an ArrayController to access aggrigated data for all models of type X.</p>
{{render dashboard}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="options">
<h2>Options:</h2>
<dl>
{{#each}}
<dt>Title: {{title}}</dt>
<dd>Cost: {{cost}} {{input value=cost}}</dd>
{{/each}}
</dl>
</script>
<script type="text/x-handlebars" data-template-name="dashboard">
<h2>Overview:</h2>
<p>Why won't this display info about the options below? I suspect that the optionsController does not actually contain options A-C...</p>
{{#with App.optionsController}}
<p>Total number of options (expect 3): {{len}}</p>
<p>Total cost of options (expect 600): {{totalCost}}</p>
{{/with}}
</script>
Without getting into the why of doing things this way, there were a couple problems with making it just work.
optionsController needs to be OptionsController
the active controller in the dashboard will be DashboardController (autogenerated if not defined) so you need to open that and give it a reference to options
in reduce, the second argument is an item reference, so you need to do get('cost') on it
in order for javascript to know you want integer math, you need to use parseInt
This is a working jsbin: http://jsbin.com/acazAjeW/1/edit
lol, kingpin2k and I seem to be competing for answering ember questions these days.
Part of the problem is, your dashboard exists even when the options may not, which might be the route you are going in the future, here's a partial version that works, I'll look into the other version.
http://jsbin.com/ImOJEJej/1/edit
Using render
http://jsbin.com/ImOJEJej/3/edit
I'm going round in circles here, trying to pull all the components together to produce the desired view. I feel as if I just need to just tweak the dial to bring it all into focus but at the moment it aludes me.
I have two models - Person and Address - which I have created two templates for; I then want to render these two templates in another 'main' template. At the moment I am not linking them in anyway (eventually 1 person will have many nested addresses) because I want to understand the general principes first.
The two templates work individually using App.Router.map
this.resource('listOfPeopleTemplate', { path: '/' });
or
this.resource('listOfAddressesTemplate', { path: '/' });
but not together or when I add the mainViewTemplate and try to add both into that:
App.Router.map(function () {
//this.resource('listOfAddressesTemplate', { path: '/' });
//this.resource('listOfPeopleTemplate', { path: '/' });
this.resource('mainViewTemplate', { path: '/' });
});
The problem seems centered around:
App.MainViewTemplateRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('listOfPeopleTemplate', {into: 'mainViewTemplate', outlet: 'peops'});
this.render('listOfAddressesTemplate', {into: 'mainViewTemplate', outlet: 'address'});
}
});
Errors returned are "outlet (people) was specified but not found"; and "The value that #each loops over must be an Array..". I can see that I may need to do something about the controller for both the Addresses and People but I don't know what. Fact is, i've got myself into such a muddle I now can't even get the originally successfull version working (with either the address or people displaying in their own template).
I have made the following fiddle http://jsfiddle.net/4gQYs/4/. Please, bring me into focus!
I hope I understood your problem!
I have two routes people and places.
App.Router.map(function(){
this.resource('people');
this.resource('places');
});
I am loading the model for both the controller in model hook of people route.
App.PeopleRoute=Ember.Route.extend({
model:function(){
var places=Em.A();
$.getJSON("js/places.js").then(function(json){places.setObjects(json)});
var placesController=this.generateController('places',places);
placesController.set('content',places);
var people=Em.A();
$.getJSON("js/people.js").then(function(json){people.setObjects(json)});
return people;
},
renderTemplate:function(){
this.render('people',{into:"application",outlet:"people"});
this.render('places',{into:"application",outlet:"places"});
}
});
The following is not needed.May be useful in displaying some related data.
App.PeopleController=Ember.ArrayController.extend({
needs:'places'
});
Now I am rendering the two templates in main application template.
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet people}}
{{outlet places}}
</script>
<script type="text/x-handlebars" data-template-name="people">
{{#each controller}}
<p>{{name}}</p>
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="places">
{{#each controller}}
<p>{{name}}</p>
{{/each}}
</script>