Transition to same route with same model - ember.js

I have a "Filter" model, and the user can alter this model by clicking on the page.
So when the user makes a change to the model, I want to transition to the same route he is currently on, because the URL is different depending on the details of the model (I have overridden "serialize" on the route to ensure that this is the case).
As things stand now, Ember seems to ignore my please to transition to the same route with the same model. The model is altered and my template changes, but I want the URL to also reflect the change to the Filter.
How can I do this?
Edit:
Here is the code in my ApplicationController
App.ApplicationController = Ember.Controller.extend
currentFilter: null
actions:
toggleRegion: (region_id) ->
region = this.store.getById('region', region_id)
filter = this.get('currentFilter')
# modify the filter
if filter.containsAtLeastOneCityOfRegion(region)
filter.disableRegion(region)
else
filter.enableRegion(region)
this.replaceRoute('listings', filter)
Now, this works as intended when I am in the "index" route, but if I am already in the "listings" route, the model is modified as intended, but route replacement is not happening, and consequently, my URL is not updated with the correct dynamic segments (coming from my ListingsRoute's "serialize" method).

It looks like Ember does a no-op if you want to transition to (or replace, as is the case with me) to the same route you are on, with a model object with the same identity as the current model.
So what I needed to do was clone the object, make the change I needed to make, and then do the transition.

Related

How to add properties to Ember model that you don't want to persist into database?

I have a model, which has some properties that I obviously want to fetch from database. In addition to this I want to add a "checked" property to that model, which determines, if the actual model element is checked on the user interface. (for example a model record represents an email, and the checked property determines if it is selected for deletion) And obviously I don't want to persist it to the database, I would like to set a default value to this, and than change it via ui actions).
My guess is that these kind of information should be stored at the controller level, but it is an ArrayController, so a single property in the controller won't do.
I'm sure it's not too difficult to solve, but I'm a newbie, and I could not find the answer in the ember guide.
Thanks!
You are right about using a controller. ArrayControllers have itemControllers. You want to specify an item controller and then put the checked attribute there. For example:
App.MailController = Ember.ArrayController.extend({
itemController: 'email'
});
App.EmailController = Ember.ObjectController.extend({
checked: false
});

Is there a "right" way to make the setupController hook fire consistently?

I have a page with a search bar. Upon entering text and clicking enter, a transition occurs to the same page with a query in the URL (ie .../search/banana). Due to the way the model and setupController hooks fire, I have set up my code as follows:
model: Updates the search text field with the text that was passed, /and changes the controller's model to the current JavaScript timestamp as a hack to make sure Ember calls setupController/.
setupController: obtains the text from the search field, and should then update the model with the proper search results.
What I'm doing in the model hook is a hack, but I'm not sure how else to do this in a way that remains consistent with my URL requirement (the search should work whether somebody manually enters an appropriate URL, or a transitionTo occurred)
I'd appreciate it if somebody could tell me if there's a "right" way to ensure that setupController is called regardless of whether or not Ember thinks that the model has changed (which seems to be the culprit that's currently necessitating the hack.)
I don't use setupController myself, but if the search bar is used on entire application, you should define this on ApplicationController. If not, you can define it in your controller.
Application template:
{{view Ember.TextField valueBinding="searchKeyword" action="doSearch"}}
App.js:
App.ApplicationController = Ember.Controller.extend({
searchKeyword: '',
actions: {
doSearch: function()
{
var keyword = this.get('searchKeyword');
// do your logic here
}
}
});
When user hit enter, it will trigger doSearch action and you can just
this.get('model').filter()
or any logic you want.
#dgbonomo, I think your existing approach is great. From an Ember point of view your model is changing each time that a new search happens. Think of the query as the main model, and the search results as a collection of models that "belong" to the query.

ember (+ ember-auth) linkTo loggedIn user profile

To get straight to the point, I have an app with a profile accessible like that:
this.resource('user', {path: '/user/:user_id'});
This can accessed from visitors and (logged in users). Now I also have a menu-bar which has, only for logged in users, a link to the user-profile (so that would be /user/:loggedin-user_id). I am using ember-auth to authenticate the user and ember data for my models.
The problem here seems to be {{linkTo 'user'}} doesn't work, because for one user that should link to /user/28, for the next one to /user/15. Simply to his/her own profile. I kinda get that it doesn't work, because how should ember know what ID to display (though yes, I don't understand the underlying reasons fully).
Ember-Auth does provide App.Auth.get('userId') to get the Id of the currently logged in user, but I don't really know how I can tell ember to know that as well.
So what I had to do know is setting the link manually via
<a {{bindAttr href="App.loggedInUser"}}>
where-as this variable gets set to the right url in the Application controller with App.Auth.get('userId'). This works, but is obviously quite a hack.
Is there any 'ember' way of solving that?
ember-auth dev here.
Straight to solution:
First, ask ember-auth to load the user current user model. Assuming that your user model is called App.User, and it implements App.User.find(id).
App.Auth = Ember.Auth.create
# ...
userModel: 'App.User' # default null
# pass the string, not a class App.User
# access the current user model
# as with all things ember, this is loaded asynchronously
App.Auth.get('user')
(copied from the docs.)
Next, pass this dynamic model to your handlebars links:
{{#linkTo 'user' App.Auth.user}}Link To Current User{{/link}}
Or otherwise - e.g. make it a binding to App.Auth.get('user'), etc.
Just in case, (js) App.get('foo') is equivalent to ember-handlebars App.foo.
Your user route has a dynamic segment. You can pass the user model to the linkTo and the anchor will point to that route for different users depending on the id of user model passed.
{{linkTo 'user' userObj}}Profile{{/linkTo}}
The model passed can be plain object with an id and the default serialization would still work. So if you wrap App.Auth.get('userId') into a object with an id you won't need a full model object.
Here's an example jsbin.

Ember: how do you access the model from the router?

Based on what I've read (please correct me if I'm mistaken), the logic that handles when a model should be saved and where to transition next should be in the router.
If that is the case, I'm running into a bit of a problem: I don't know how to access the model from the route.
This is my controller (and the console logs "CREATED" after I press submit):
App.ScoutsNewController = Ember.ObjectController.extend
submit: ->
model = #get('model')
model.on 'didCreate', ->
console.log 'CREATED' # I want to redirect to the index after creation
model.save()
I should move that logic into the route, right? Let's try that:
App.ScoutsNewRoute = Ember.Route.extend
model: ->
App.Scout.createRecord()
events:
submit: ->
# Based on what I've read, the right place to put the code you see in the controller is here. How do I get access to the model?
# I have tried #get('model'), #get('content')
Note: I understand that the submit event bubbles up from the view, to the controller, then finally the route, stopping at any one of them that has "submit" defined. So since I want the route to handle it, I removed the controller. I'm able to see any console.log done in the route, I just need to be able to get to the model instance.
I'm using Ember v1.0.0-rc.5-7-g610589a
Thanks!
Two options: this.currentModel or this.modelFor(routeName)
Update
I spoke to SeƱor Alex Matchneer about this. There are no plans for this.currentModel to go away anytime soon, but he considers this.modelFor(this.routeName) the public API.
what should work is
this.controllerFor('ScoutsNew').get('content')
this.currentModel isn't really the approved way as described here
but in my version of Ember (1.11) this.modelFor(this.routeName) returns null, so this is what worked for me
this.controllerFor(this.routeName).get('model')
You could also use this.controller.get('model'); but there are plans to remove the controller.
Till that we can use the above code to retrieve the routes current model
With Ember 3.0.0 this is a documented way that works for me:
const model = this.controller.model;

Force loading a single attribute with ember-data

I'm trying to find a way to update a single attribute of a model without it getting dirty. Just like Store.load but with just a single attribute.
The use case is that I want to upload an avatar image (which cannot be done with the current adapter's semantics AFAIK). I do it against a particular API endpoint and I afterwards I want to update the user with the resulting URL (so all his avatar appearances get updated).
I am in the context of a profile edition, so if I just load the whole user I loose all the changes of the form (because the values get overriden).
Is there any way to do so? Thanks!
You could model 'Avatar' separately, as a model in itself instead of an attribute. Then, that model can be updated via the store without worrying about conflicting changes with the rest of the User model.
I can think of two ways, but I haven't tried them. (They're both just workarounds)
You can set a user's property that is not a DS.attr. If it's a normal property, it shouldn't dirty the record.
If url is a database attribute, then create a computed property that listens to URL and use that in your templates.
App.User = Em.extend({
url: DS.attr('string'),
URL: function() {
return this.get('url');
}.property('url')
});
And then user.set('URL', imageUrl)
Another way could be to transition the record to the clean state.
user.get('stateManager').send('becameClean');
The second method is an ugly hack though.