Resource with dynamic segment and retrieving the model in a nested route - ember.js

I have the following route :
#resource 'groups', {path: 'events/:event_id/groups'}, ->
#route 'new'
In my new route, in order to build a new App.Group object, I need to know which is the event we're in.
So I have the following route, which corresponds to the resource :
App.GroupsRoute = Ember.Route.extend
model: (params) ->
App.Event.find params.event_id
And the one which corresponds to the route :
App.GroupsNewRoute = Ember.Route.extend
model: ->
App.store.createRecord App.Group
I would need to specify the group's event when creating it. However, in the GroupsNewRoute, I don't seem to be able to retrieve the event. The params don't include event_id.
How can I get the model from a parent resource in a route ?

There are a few different issues here:
You should not use App.store. The store should be available on routes as this.get('store').
I'm not sure why you're naming your route GroupsRoute. Since it represents an event, you should name it EventRoute. This is not required, but it would be more idiomatic. Also, you don't need to specify the model for GroupsRoute. What you are doing is the default behavior.
Finally, you can get the model for another route via: this.modelFor('groups') in your route. That should solve your issue here.

Related

When creating a new data object in Ember that relies on another object how do you pass it along?

I am on a page where I can see a specific customer, part of my router.js is:
this.route('customers');
this.route('customer', {path: "customers/:customer_id"});
this.route('customer.order.create', { path: "customers/:customer_id/order/create" });
customer.order.create needs to load in my main view and so is not nested. An order 'has a' customer.
I've setup my /customer/order/create controller to have
needs: "customer"
I want to access the customer in my /customer/order/create.hbs template like this:
<h3>New Order for {{controllers.customer.name}}</h3>
When I end up creating the order I will also want to set newOrder.customer = customer.
customer.hbs links like so
<div>
{{#link-to 'customer.order.create' model}}Create Order{{/link-to}}
</div>
Currently {{controllers.customer.name}} renders nothing, what piece of the puzzle am I missing to get to the customer in my order/create route?
Or putting it more generally, what route/controller/etc code do I need when I have a parent object which belongs to my child object in a /parentObject/parent_id/childObject/create type scenario.
There are many points to fix:
1) {{controllers.customer}} is Controller Object, {{controllers.customer.name}} it's name property. I think you want {{controllers.customer.model.name}}.
2) "..newOrder.customer = customer.." should be
newOrder.set('customer', this.get('controllers.customer.model'));
3) your customer.order.create route model hook shoudn't be empty, since you are using dynamic segment customer_id:
//route
model: function(params) {
return this.find('customer', params.customer_id);
}
4) Your routes are not nested, so {{controllers.customer.model.name}} would be empty if your customer route is not activated. So you should use: {{model.name}} instead of {{controllers.customer.model.name}}
When you click link you passes model directly, and model hook is not fired, so all looks good. When you visit url directly, model hook is fired. If it is empty you will see nothing.
General concept: it is dependancy injection concept, you could read here: http://guides.emberjs.com/v1.12.0/understanding-ember/dependency-injection-and-service-lookup/
You should be able to get the customer from the store. Give the following code a try:
The route:
export default Ember.Route.extend({
model: function(params) {
return Ember.RSVP.hash({
customer: this.store.find('customer', params.customer_id)
});
}
});
The controller:
export default Ember.Controller.extend({
customer: Ember.computed.alias('model.customer')
});
And it should be directly accessible as customer in your template, like so:
<h3>New order for {{customer.name}}</h3>
I changed needs: "customer" to needs: ["customer"] and then used {{model.name}} in my template. Seems that needs requires an array of strings and not just a string, after I fixed that Ember took compare of the rest without the need to create a /customers/order/create.js route.
For a more complete answer see Artych's answer if you don't want everything taken care of.

Creating a new resource in ember

I'm building an office reception app in Ember. When a person arrives at the office, they pop open the app and are taken through a three step wizard:
Choose a reason for visiting
Choose the person you've come to see
Confirm
The app also allows an administrator to view all of the visits and to view an individual visit.
I have Visit and Person models I've hooked up to a server using Ember Data. Here's what my routes look like:
App.Router.map () ->
#resource 'visits', ->
#resource 'visit', { path: '/:visit_id' }
#resource 'new', ->
#route 'welcome'
#route 'directory'
#route 'thanks'
This allows me to create a new Visit model in the VisitsNewRoute and use it in the welcome, directory and thanks views.
This works. However, it feels wrong to have a new resource, especially since it's conceivable I'll want at least one more new route in my application.
Is there a better way to do this?
I think that you can change the new resource to newVisit like this:
App.Router.map () ->
#resource 'visits', ->
#resource 'visit', { path: '/:visit_id' }
#resource 'newVisit', ->
#route 'welcome'
#route 'directory'
#route 'thanks'
Now you will have a NewVisitRoute where you can create a new Visit model to use in each of the child routes.
And you will be able to make a transition to this routes with the route names: newVisit.welcome, newVisit.directory and newVisit.thanks. You can use this route names in a link-to helper link this:
{{link-to "Welcome", "newVisit.welcome"}}
The recommended practice is to use a create/new route under the resource type, so new under visits, then `transitionTo('visit.welcome', newRecord). (I'm saying all of this with the assumption that welcome, directory, and thanks aren't part of the new record creation).
App.Router.map () ->
#resource 'visits', ->
#route 'new'
#resource 'visit', { path: '/:visit_id' }
#route 'welcome'
#route 'directory'
#route 'thanks'
Ember doesn't always name routes the way you want when dealing with routes nested more than one level. I would name your 'new' route as follows:
#resource 'visits.new', path: 'new', ->
There are a number of approaches you can use to structuring your routes depending on how you assign model ids and whether or not you are using localStorage to preserve user edits until they are persisted to the server.
I have my route pattern as follows:
App.Router.map () ->
#resource 'visits', ->
#route 'new'
#route 'crud', path: ':visit_id'
My 'new' routes create a new resource in the routes model callback which in my models auto-generates a v4 UUID. The new route then performs a transitionTo the crud route in the afterModel callback. In effect the 'visits.new' route acts as a trampoline and allows you to easily use {{link-to 'visits.new'}} from templates/menus etc.
The above approach allows you to to have a single crud route and crud controller that can handle all the show/create/update/delete actions for the model. The models isNew property can be used within your templates to handle any differences between create and update.
I also use localStorage so that newly created (but not yet persisted) models survive a browser refresh, the UUIDs really come in handy for both this and for persisting complex model graphs.
The above router pattern occurs quite a lot in my app so I have defined some base Route classes and a route class builder but the general pattern is as follows:
If using UUIDs:
App.VisitsNewRoute = Em.Route.extend
model: (params, transition)->
App.Visit.create(params)
afterModel: (model,transition) ->
#transitionTo 'visits.crud', model
App.VisitsCrudRoute = Em.Route.extend
model: (params,transition) ->
App.Visit.find(params['visit_id'])
If not using UUID's then the routes are different. I did something like this before I moved to UUIDs, it treats model id 'new' as a special case:
App.Router.map () ->
#resource 'visits', ->
#route 'crud', path: ':visit_id'
App.VisitsCrudRoute = App.Route.extend
model: (params, transition) ->
visit_id = params['visit_id']
if visit_id == 'new' then App.Visit.create() else App.Visit.find(visit_id)
serialize: (model, params) ->
return if params.length < 1 or !model
segment = {}
segment[params[0]] = if model.isNew() then 'new' else model.get('id')
segment
For your specific case of managing the wizard step state I would consider using Ember Query Params, which allow you specify the current step in a parameter at the controller level
Query params example:
App.VisitsCrudController = Em.ObjectController.extend
queryParams: ['step'],
step: 'welcome'
Link to next step in the view:
{{#link-to 'visits.crud' (query-params step="directory")}}Next{{/link-to}}
You may also want to define some computed properties for the next and previous steps, and some boolean properties such as isWelcome, isDirectory for your view logic.

Getting polymorphic parent route in Ember

How do I dynamically get a route's parent route's model. Say my router looks like this:
App.Router.map ->
#resource 'farms', ->
#resource 'farm', path: '/:farm_id', ->
#resource 'attachments'
#resource 'fields', ->
#resource 'field', path: '/:field_id', ->
#resource 'attachments'
If I have 2 routes: App.FieldRoute and App.AttachmentsIndexRoute, and I want to get the Field model from App.AttachmentsIndexRoute, I could do:
model: ->
#modelFor('field')
How do I do the same thing dynamically, not specifying the model's route explicitly? In other words, if I'm looking at attachments for a Farm instead, the same code should work.
(Isn't there a way in Ember to grab the parent route from a nested route?)
UPDATE:
Doesn't look like there's a clean (API) way to do this. I found some hacks using internal properties like router.currentHandlerInfos. Might have to go that route until Ember team adds a proper way of doing this.
There is no API for that, and I think it will stay that way.
A clean workaround would be to reopen the Route and add this functionality.
See here: Getting parent route in EmberJS

Dynamic segments not getting passed to route params

I'm using Ember 1.0 and Ember-Data 1.0Beta. I'm trying to pass a dynamic segment to a route like so:
#resource 'organization', path: 'organizations/:organization_id', ->
#route 'edit'
Then in my edit route:
Whistlr.OrganizationEditRoute = Ember.Route.extend
model: (params) ->
#store.find('organization', params.organization_id)
Unfortunately, the params hash turns up empty. When I inspect it in the console, it's just a simple {}. In turn, params.organization_id is null. This happens even when the URL looks correct: "/organizations/1/edit`
This closely resembles the setup in the Ember guides. What could I be getting wrong?
The reason for this is the dynamic segment (:organization_id) is part of the organization resource and not the edit resource. This means that only OrganizationRoute will have access to the params.organization_id.
However, if you need the model in your OrganizationEditRoute you can use modelFor to access it.
Whistlr.OrganizationEditRoute = Ember.Route.extend
model: (params) ->
#modelFor('organization')
This lets the OrganizationRoute load the model from it's dynamic segment and then the OrganizationEditRoute can just simply access that model like so.

Getting context in ember templates in singular resource index outlet

I just want to be able to display a campaign model's properties in the templates rendered in the my resource template's outlet.
My router looks like this:
App.Router.map (match)->
#resource 'campaigns', ->
#resource 'campaign',
path: ':campaign_id', ->
#route 'chat',
path: '/chat'
I set up 3 templates for my campaign resource:
campaign
campaign.index
campaign.chat
I am not sure how I am supposed to get acces to the model identified by the dynamic segment in my url. I was able to set it up successfully in my campaign route like so:
App.CampaignRoute = Ember.Route.extend
model: (params) -> App.Campaign.find(params.campaign_id)
This doesn't seem to work for the index and chat routes.
I was thinking something like {{someProperty}} or {{campaign.someProperty}} would work by default here.
Why does the context change and how do I get it?
My routes and templates are rendering fine, minus the context I want.
Setting the model this way in my campaign.index and campaign.chat routes made all the difference:
App.CampaignIndexRoute = Ember.Route.extend
model: ->
#modelFor('campaign')
App.CampaignChatRoute = Ember.Route.extend
model: ->
#modelFor('campaign')