Ember Routing/Lookup with Attributes other than ID - ember.js

I thought I had a fairly simple solution to find models with attributes other than ID, but I'm running into one problem.
Current setup:
Ember Router:
App.Router.map ->
#resource "posts", ->
#route "show", {path: ':slug'}
App.PostsShowRoute = Ember.Route.extend
serialize: (model, params) ->
object = {}
name = params[0]
object[name] = model.get('slug')
return object
model: (params) -> App.Post.find(params.slug)
Rails Controller:
class PostsController < ApplicationController
def show
#post = Post.find_by_slug(params[:id])
render json: #post
end
end
THE PROBLEM
If params.slug == "some-post", when post object is returned from the server, post.id == "some-post". This messes up all the associations.
This is probably because DS.Store is assuming that if I call find(something), something is the id. I would have expected that the response from the server would overwrite this assumption, but it doesn't.
Is there a better way to do this? My use case is extremely simple. All I need to do is serialize and deserialize a post object by the slug attribute.
I'm using Ember Data revision 11

The solution turned out to be findQuery. I was using this some time ago, but I didn't know how to make use of the promise.
model: (params) ->
#get('store').findQuery(App.Post, {slug: params.slug}).then (data) ->
return data.get('firstObject')
and on the rails side:
class PostsController < ApplicationController
def index
#posts = params[:slug] ? Post.where(slug: params[:slug]) : Post.homepage
render json: #posts
end

Related

Ember sub route appending empty record to collection

Sub route appending empty record to collection I've got a bit of a tricky problem and I'm hoping you peeps can help.
This works as expected BUT if I refresh the page with a location sub route the locations list gets an empty record appended to the end.
App.Router.map ->
this.resource 'home', { path: '/' }
this.resource 'locations', ->
this.resource 'location', { path: ':slug' }
this.resource 'services'
this.resource 'contact'
this.resource 'login'
App.LocationsRoute = Ember.Route.extend
model: ->
this.store.find 'location'
App.LocationRoute = Ember.Route.extend
serialize: (model, params)->
slug: model.get 'slug'
model: (params)->
this.store.find 'location', params.slug
Any help greatly appreciated

Store promise resolved in template but not in controller

I have a select box with a list of users that I get from the store. I have a method on the controller where I try to get an initial user for the selectbox. No matter what I do I can't seem to get any user record to resolve on the method but the users show up fine in the selectbox. Can someone explain what I'm missing? Thank you in advance for the help.
Controller:
App.TodoModuleController = Ember.ObjectController.extend
users: Em.computed -> #store.findAll 'user'
selectedUser: Em.computed 'users', ->
#get('users').objectAt(1)
# There are 4 users in the db. Just using 1 for a test.
# I also tried
# #get('users').then (users)->
# users.objectAt(1)
selectedUserDidChange: Em.observer 'selectedUser', ->
if #get 'selectedUser' then #assignUser()
assignUser: ->
model = #get 'model'
model.set 'assigneeId', #get('selectedUser').get('id')
unless model.save()
console.log 'Error: TodoModuleController.assignUser'
Template:
{{view Em.Select content=users selection=selectedUser optionValuePath='content.id' optionLabelPath='content.firstName'}}
SOLUTION (thanks to Hrishi):
Todo Model:
App.Todo = DS.Model.extend
text: DS.attr 'string'
assignee: DS.belongsTo 'user', inverse: null
Todo Controller:
App.TodoModuleController = Ember.ObjectController.extend
users: Em.computed -> #store.findAll 'user'
assignedUserDidChange: Em.observer 'assignee', ->
if #get 'assignee' then #assignNewUser()
assignNewUser: ->
unless #get('model').save()
console.log 'Error: TodoModuleController.assignUser'
Select Box:
{{view Em.Select content=users selection=assignee optionValuePath='content.id' optionLabelPath='content.firstName'}}
Computed properties are not computed until someone (in your case, the template) tries to access them, in other words, they are computed lazily. Since the template tries to access them, the users array will get populated and you can see it on the page when you click on your select box. The way I would do this would be to put an attribute on the model called assignee and bind the view's selection directly to that.

Default ember template with specific model record

I've scouring for a bit, but can't find an answer to this. I've got an Ember route on a Rails app, and I would like one of the model records to be displayed by default when visiting the URL (right now, a user needs to click on a specific link to retrieve the data).
For example, when visiting /albums, I would like /albums/38 to be active and visible without forcing a redirect. Is that possible?
Thanks
App.Router.map ->
#resource 'albums', path: '/', ->
#resource 'album', path: '/:id'
App.AlbumsRoute = Ember.Route.extend
model: -> #store.find 'album'
Add an 'index' route for the 'albums' resource and redirect to the 'album' route with the first model. The index route will only be hit when you hit just the albums resource, and not when you hit deeper (aka not when you hit /albums/1).
App.AlbumsRoute = Ember.Route.extend
model: -> #store.find 'album'
App.AlbumsIndexRoute = Ember.Route.extend
model: ->
album = this.modelFor('albums').get 'firstObject' # This can be whatever...
this.transitionTo('album', album) if Ember.isEmpty(album) is no

Ember duplicate loading for models

I want to use the information already received within a linked template. Nesting routes has created a whole heap of errors, and I'm a little lost on how to pass this data, but it seems like it must be possible?
This works, but strikes me as a poor hack. Ideally I'd only want one API round-trip...
App.Router.map ->
#resource 'photos'
#route 'photo', path: 'photo/:photo_id'
App.PhotosRoute = Ember.Route.extend
model: ->
Ember.$.getJSON('http://api.flickr.com/services/feeds/photos_public.gne?tags=cake&tagmode=all&format=json&jsoncallback=?')
setupController: (controller,model) ->
controller.set 'photos', model.items.map (i) ->
i.id = model.items.indexOf(i)
App.PhotoRoute = Ember.Route.extend
model: (params) ->
jQuery.getJSON('http://api.flickr.com/services/feeds/photos_public.gne?tags=cake&tagmode=all&format=json&jsoncallback=?').then (api) ->
api.items[params.photo_id]
App.PhotosController = Ember.ObjectController.extend
itemController: 'photo'
{{#linkTo 'photo' this }}<img {{bindAttr src="media.m"}} />{{/linkTo}}
You can use the modelFor(routeName) to get the model from some route. In your case probally you will fetch all data in PhotosRoute, and want to reuse it. You can use this in your PhotoRoute:
App.PhotoRoute = Ember.Route.extend
model: (params) ->
#modelFor('photos').items[params.photo_id]
So just one api call will be done.

Pass a preset into a create controller

In my app, there are Templates and Documents. I want to be able to create a document from a template, i.e. the user will be able to go to the template show page, click a button, and be redirected to the new document page, with document text pre-filled from the template (I only need to have the text pre-filled, I don't care about the document knowing the template it's made from).
But how would I pass the text to the new document page?
I ended up doing the following:
App.Router.map ->
#resource 'documents', ->
#route 'new'
#route 'new_from_template', path: '/new/:template_id'
App.DocumentsNewFromTemplateRoute = Ember.Route.extend
model: (params) ->
model = App.Document.createRecord()
if params.template_id
App.Template.find(params.template_id).then (template) ->
model.set 'text', template.get('text')
model
setupController: (controller, model) ->
if model._reference.type == App.Template
model = #model(template_id: model.id)
#currentModel = model
#controllerFor('documentsNew').set 'model', model
renderTemplate: ->
#render 'documents/new'
And to link to new document form for template, I simply do
{{#linkTo 'documents.new_from_template' template}}Create a document{{/linkTo}}
You can define an action in the controller:
App.TemplateRoute = Em.Route.extend({
model: function() {
return Em.Object.create({
name: "A Template",
values: {title: "Templated Title"}
});
}
});
App.TemplateController = Em.ObjectController.extend({
goToNewDocument: function() {
var o = Em.Object.create(this.get('values'));
o.set('source', 'Template');
App.Router.router.transitionTo('newdoc', o);
}
});
App.NewdocRoute = Em.Route.extend({
model: function() {
return Em.Object.create({title: "Default Title", source: "Model hook"});
}
});
jsbin demo