Unfortunaltely, the example for links in the Ember guides (http://emberjs.com/guides/templates/links/) does not work somehow for me.
To show a list of tempworkers, and have a link for each tempworker to the detail page, I use the following code:
HTML:
<script type="text/x-handlebars" data-template-name="tempworkers">
<div>
{{#each controller }}
<tr>
<td>{{#linkTo 'tempworker' this}} {{firstname}} {{/linkTo}}</td>
<td>{{initials}}</td>
<td>{{completeSurname}}</td>
</tr>
{{/each}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="tempworker">
<div id="tempworker">
<p>{{view Ember.TextField valueBinding='firstname'}}</p>
<p>{{view Ember.TextField valueBinding='initials'}}</p>
<p>{{view Ember.TextField valueBinding='surnamePrefix'}}</p>
<p>{{view Ember.TextField valueBinding='surname'}}</p>
</div>
</script>
Javascript:
window.App = Ember.Application.create({
LOG_TRANSITIONS: true,
ENV.EXPERIMENTAL_CONTROL_HELPER = true;
});
App.Store = DS.Store.extend({
revision : 11,
adapter : DS.RESTAdapter.extend({
url : 'http://dev.start.flexplanners.com/api'
})
});
App.Router.map(function() {
this.route('tempworkers'); // /tempworkers
this.route('tempworker', { path: '/tempworkers/:id' }); // /tempworkers/:id
});
App.TempworkersRoute = Ember.Route.extend({
model : function() {
return App.Tempworker.find(); // return All tempworkers to the TempworkersController
}
});
/* TODO: Question -> According to http://emberjs.com/guides/routing/defining-your-routes/, this should be working by convention, and NOT explicitely defined here?
* But when I leave it out, it's broken :(
* */
App.TempworkerRoute = Ember.Route.extend({
model : function(params) {
return App.Tempworker.find(params.id); // return tempworker by id to the TempworkerController
}
});
Use cases:
When I visit the URL /tempworkers, the app fires a request
to my server API, and return the list in my tempworkers view.
When I visit the URL /tempworkers/[UUID], the app fires a
request to my server API, and return the requested tempworker
in my tempworker view.
When I click a generated link in the list of tempworkers, the app routes me
to /tempworkers/<App.Tempworker:ember343:47cfa9f2159d45758ceacc4c15ae1671>,
and shows the details in my tempworker view
My questions are as follows:
1) Are these 3 use cases showing expected behaviour?
2) If yes, does this show a distinction between 2 states, 1 is a fresh URL visit (including server API call), and 1 transitionTo another view, with a parsed in reference to a single object from the list?
3) If yes, when will be a moment to reload the list, and update the items in the list?
4) If no, how will I be able to have the linkTo generate a href like '/tempworkers/[UUID]', and let the app make a server API call to get the details of the tempworker?
Thanks in advance for your time!
Rainer.
You are doing it right, you've just made a typo, your route needs to be /tempworker/:tempworker_id.
App.Router.map(function() {
this.route('tempworkers'); // /tempworkers
this.route('tempworker', { path: '/tempworkers/:tempworker_id' }); // /tempworkers/:id
});
Ember uses the provided dynamic key when looking for a model to deserialize.
Related
This one has me baffled. I am still new to ember but I had some nested routes where I was calling transitionTo with the redirect hook on the parent route to pull in a nested child route.
App.CategoryRoute = Ember.Route.extend({
redirect: { transitionTo('subcategories'); }
});
I since removed the redirect hook and the transitionTo however the transition is still occurring. I even have closed my project and reopened, cleared the cache on all browsers but it still is holding onto the transition. I have also tried calling a bogus route under the redirect and it errors out. As soon as I remove, it goes back to this behavior and transitions into the child route. Has anyone ever had this happen? What am I doing wrong? Please let me know if you need more code.
Here is the rest of the code. in my troubleshooting i have removed and added so much stuff I will try to get it back to what it was when it started happening. I also removed as much 'fluff' as I could from the handlbars code sections to make it clearer.
App = Ember.Application.create({
rootElement: "#app",
LOG_TRANSITIONS: true
});
App.Router.map(function () {
this.resource("category", function () {
this.resource('subcategories', { path: "/" }, function () {
//subcategory route show list of items in that subcategory
this.resource('subcategory', { path: "/:subcategoryslug" }, function () {
//this is individual item
this.route("item", { path: "/:itemid" });
});
});
});
});
App.CategoryRoute = Ember.Route.extend({
redirect: { transitionTo('subcategories'); }
});
App.CategorySubcategoriesRoute = Ember.Route.extend({
model: function (params) {
Ember.Logger.warn("Inside category route");
categoryid = 47
//this calls extended object for json data
//works just fine when routes are rendered.
return App.Subcategory.findAll(categoryid);
}
});
Here is the handlebars blocks:
<script type="text/x-handlebars" data-template-name="index">
<h2>APPLICATION TITLE</h2>
<div>
<strong>{{#link-to 'category'}}CategoryName{{/link-to}}</strong>
</div>
</script>
<script type="text/x-handlebars" data-template-name="category/index">
<h2>Category Name</h2>
<div>
{{outlet "subcategories"}}
</div>
</script>
<script type="text/x-handlebars" data-template-name="category/subcategories">
{{#each subcategory in model}}
<div>
<strong>{{#link-to 'category.subcategory.index' subcategory}}{{subcategory.subcategoryname}}{{/link-to}}</strong><br>
{{subcategory.description}}
</div>
{{/each}}
</script>
I've got master-detail page layout as on image. I access this page through #/masters/:master_id app url.
Routes a defined as follows:
App.Router.map(function() {
this.resource('masters', { path: '/masters' }, function() {
this.route('detail', { path: '/:master_id' });
});
});
App.MastersRoute = Ember.Route.extend({
model: function() {
return App.DataStore.getData('/api/masters'); //returns Promise!
},
setupController: function(controller, model) {
controller.set("content", model);
}
});
App.MastersDetailRoute = Ember.Route.extend({
model: function(params) {
return this.modelFor("masters").find(function(item) {
return item.get("id") == params.master_id;
});
}
});
Templates:
<script type="text/x-handlebars-template" data-template-name="masters">
<div id="masters-grid">
{{#each master in model}}
<div {{action "show" master}}>
{{master.name}}
</div>
{{/each}}
</div>
<div id="detail">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars-template" data-template-name="masters/detail">
{{model.name}} <br />
{{model.age}} <br />
{{model.address}} <br />
</script>
When clicking through masters in the grid I want to show their details in Detail outlet and I do not want to reload all masters from API when changing the master selection.
I have a problem with MastersDetailRoute's model, because this.modelFor("masters") returns undefined. I think, it is caused by returning Promise in model hook. Any idea or workaround how to access one item from Masters model or controller in "child route" model hook?
I see a few things here.
when defining routes that have the same url as the route name theres no need to specify the path
the detail route should also be a resource as it is a route backed by a model
In the Masters route returning a promise is correct and supported natively by ember. The route wont be resolved until the promise is.
setup controller isn't required
its usually best to do the required api call to fetch the individual record in the detail route. This will only be used when loading the page for the first time (if f5 ing or coming from a bookmark)
in your masters template you can use id instead of typing data-template-name or better still look into use ember-cli/brocolli or grunt to precompile your templates
to prevent ember refetching your model when selecting a row use the handlebars helper link-to
{{#link-to 'masterDetail' master}}
{{master.name}}
{{/link-to}}
just to clarify, using link-to in this way passes the object specified in the second parameter as the model to the specified route (first parameter). In your case master will now be set as the model to the master detail route.
in masters detail theres no need to type "model" the default context (i.e. the value of "this") in your template is the controller, then if the property is not found on the controller it looks for it in the model.
Hope this helps
I am new to Ember.js, and I am trying to learn it by rewriting an old app of mine. Unfortunately I got stuck fairly early and need some help, as couldn't find the answer on the Web.
I have the following code:
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function () {
this.resource("users", function () {
this.resource("score", {path: "/:nickname"});
});
});
App.UsersRoute = Ember.Route.extend({
model: function () {
var url = "server url";
return new Ember.RSVP.Promise(function (resolve, reject) {
resolve($.getJSON(url));
});
}
});
App.ScoreRoute = Ember.Route.extend({
model: function () {
var url = "server url" + "?nickname=";
url += passedNicknameFromclickedLink;
return new Ember.RSVP.Promise(function (resolve, reject) {
resolve($.getJSON(url));
});
}
});
<script type="text/x-handlebars">
<nav>
{{#link-to "users"}}Users{{/link-to}}
</nav>
<main class="main-wrapper">{{outlet}}</main>
</script>
<script type="text/x-handlebars" data-template-name="users">
<div class="user-score">{{outlet}}</div>
<h2>Users</h2>
{{#each}}
<div>{{#link-to "score" nickname}}{{nickname}}{{/link-to}}</div>
<div>{{games}}</div>
<div>{{score}}</div>
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="score">
<h2>Score for {{nickname}}</h2>
{{#each categoryScores}}
<div class="scores">
<div>{{category}}</div>
<div>{{gamesPlayed}}</div>
<div>{{score}}</div>
</div>
{{/each}}
</script>
What I am trying to do is to take the {{nickname}} property clicked by the user in the {{#link-to}} in "users" template and pass it as "passedNicknameFromclickedLink" variable to App.ScoreRoute in order to make a server request. I was wondering what is the proper way to do this?
It looks like you've almost everything right, you just need 2 things. First, you can access the nickname dynamic segment in the model hook with the params parameter.
model: function(params) {
var passedNicknameFromclickedLink = params.nickname;
}
Secondly, because Ember assumes quite a few conventions with your route and models, you'll have to override the serialize method in the route as well. With the serialize method, what you want to do is start with your model (the JSON from the server) and give back the nickname that was used to find that model. I don't know exactly what your JSON looks like, but it might look something like this:
serialize: function(model) {
return encodeURI(model.nickname);
}
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 am trying to build a simple category browser with ember. I have two very simple views. When the user visits / they will see a list of all categories and when they click a category in that list they will be directed to #/10 where 10 is the id.
My problem is that when a user clicks on a category at the / route I am getting the following error
TypeError: arrangedContent.addArrayObserver is not a function
[Break On This Error]
didChange: 'arrangedContentArrayDidChange'
If I refresh the page at the #/10 route the proper api call is made to my backend /api/categories?parent=99. What could I be doing wrong that is throwing this error during the transition? A full example of my code is below.
Templates:
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="categories">
{{#each category in controller}}
<p>{{#linkTo 'category' category}}{{ category.name }}{{/linkTo}}</p>
{{/each}}
</script>
<!--this is an array instead of object -->
<script type="text/x-handlebars" data-template-name="category">
{{#each category in controller}}
<p>{{category.name}}</p>
{{/each}}
</script>
Javascript:
var App = Ember.Application.create();
App.Router.map(function(){
this.resource('categories', { path : '/' });
this.resource('category', { path : '/:category_id' });
});
App.CategoriesRoute = Ember.Route.extend({
model: function(){
return App.Category.find();
}
});
//this is causing the error possibly
App.CategoryRoute = Ember.Route.extend({
model: function(params){
return App.Category.find({parent: params.category_id});
}
});
App.CategoryController = Ember.ArrayController.extend();
// Models
App.Store = DS.Store.extend({
revision: 11,
adapter: 'DS.RESTAdapter'
});
DS.RESTAdapter.configure("plurals", {
category: "categories"
});
App.Category = DS.Model.extend({
name: DS.attr('string'),
parent_id: DS.attr('number')
});
Debug info:
DEBUG: Ember.VERSION : 1.0.0-rc.1
DEBUG: Handlebars.VERSION : 1.0.0-rc.3
DEBUG: jQuery.VERSION : 1.9.0
Hint: After writing this i realized that you probably did not get the model hook right. This hook is called when you are entering your app via url. It converts the URL into an appropriate model and transition with this model into the Route. I guess you thought that this model() hook would be called with the arguments of {{#linkTo}}? This is not the case!
This does not work because you are passing a single model to your #linkTo helper in your template. So Ember wants to set this single object as content of your ArrayController. This causes your error. And your model hook returns an array. Rule of Thumb: You should always pass the same data structure to #linkTo, which you are returning in your model hook.
Therefore i would suggest to use an event instead of linkTo and do the following:
<script type="text/x-handlebars" data-template-name="categories">
{{#each category in controller}}
<p {{action 'showParentCategory' category}}>{{category.name}}</p>
{{/each}}
</script>
App.CategoriesRoute = Ember.Route.extend({
model: function(){
return App.Category.find();
},
events: {
showParentCategory : function(parentCategory){
var cats = App.Category.find({parent: parentCategory.get("category_id")});
this.transitionTo("category", cats);
}
}
});
What have i done here?
I created an action called "showParentCategory".
As this is an action with is about routing, i am handling this event in your CategoriesRoute. As you see, events/action handlers are declared in the events property of your route.
I am performing the same logic there as in your model hook and then i am calling manually the transitinTo with the fetched categories.
UPDATE: How to serialize
By implementing serialize, you are telling Ember what to put into your url.
App.CategoryRoute = Ember.Route.extend({
model: function(params){
return App.Category.find({parent: params.category_id});
},
serialize : function(models){
var first = models.objectAt(0);
return {
category_id : first.get("parentId")
}
}
});
If you do #each over a numeric value instead of doing it on an array content in your template, this issue occurs.
I had a numeric value count in my 'poll' model . I was iterating like,
{{#each poll in content.count}}
{{/each}}
I think, we have to use #each only on ember arrays.