ember (+ ember-auth) linkTo loggedIn user profile - ember.js

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.

Related

How can I model singletons without an id in ember?

I am new to ember and am trying to migrate an existing application, and would like to know what the recommendation is for modeling a single object that will be reused in multiple components on every page. ie: As part of the initial load, I would like to perform a GET request against a URL like 'https://example.com/currentUser' and get an object back like:
{
name: "Users Name"
email: "user#email.com",
profileImg: "http://example.com/pictureOfUser.png"
... snip ...
}
This object will then be used in components for menus, toolbars, and a form for updating it via a post to the same URL.
What is the best way to model this workflow in ember? Given that this is an incidental object and not the focus of most routes, it does not seem to make sense to specify it as the model of them. Also, does ember data handle cases where a model is a singleton and does not have an ID, or would I need to use something like Ember.$.ajax ?
What do you mean by "where a model is a singleton"?
If you use the ember-data default adapter, then yes, a model needs to have an ID, it's part of the JSONAPI spec. If you already have a backend with different conventions, take a look at extending or swapping out the default adapter.
A service is a singleton, and there is nothing preventing you from making an AJAX call there. You would be losing out on all the nice things that come along with ember-data, but, you can do it.

Transition to same route with same model

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.

Where to save detailed user session information for EmberJS app?

I am building my first EmberJS app, and am still trying to wrap my head around the best practices & conventions. I'm using ember-data to talk to a RESTful API, and have ember-auth working well enough to log in and save a user's ID & OAuth2 access token. However, I'd now like to maintain additional user information (e.g. name, email, etc) for use in the navbar and in various other areas of the app.
To do this, I am thinking it would be helpful to have a User object (model) that is read from a separate endpoint (e.g. /users/<id>). Since this info would be needed throughout the app, I'm inclined to store it on the ApplicationController, somewhat like this example from Ember's docs:
App.ApplicationController = Ember.Controller.extend({
// the initial value of the `search` property
search: '',
query: function() {
// the current value of the text field
var query = this.get('search');
this.transitionToRoute('search', { query: query });
}
});
However, my user object wouldn't quite be an action like query or a property like search, so I'm not sure this example applies.
I think I'll eventually want to call something like:
this.get('store').find('user', App.Auth.get('userId'));
but I'm just not sure where in the app that would go.
Main question: is the ApplicationController the right place for this information per Ember conventions, and if so, what might the code look like to retrieve it from the REST API?
Appreciate any thoughts to put me on the right track.
The general approach that I've taken before is to store the currently logged in user as App.CurrentUser. Then you can use it in any template. Once I've pulled the User object I call Ember.set('App.CurrentUser',user) to store it, and then Ember.get('App.CurrentUser') to retrieve it in other routes or controllers.
Here's a short jsbin with the general idea : http://jsbin.com/ucanam/994/edit

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.