How do you update a controller from another controller in ember.js? - ember.js

In my application I have common header that outlets into the main application layout. In that header you can select a site. I need that site selection to update another template that is rendered in the application layout. Having struggled with this for a few days I think the correct way to solve this is to use a shared service, to have the header controller observe the site selection and set that value in the shared service, then to have the index controller use a Ember.computed.alias on the value in the service controller. The following is an example of my code:
controllers/header.js
import Ember from 'ember';
export default Ember.Controller.extend({
sessionService: Ember.inject.service(),
currentSiteChanged: Ember.observer('session.current_site', function(){
var current_site = this.get('session.current_site');
console.log('currentSiteObserver', current_site);
this.get('sessionService').set('currentSite', current_site);
}),
});
controllers/index.js
import Ember from 'ember';
export default Ember.Controller.extend({
sessionService: Ember.inject.service(),
currentSite: Ember.computed.alias('sessionService.currentSite'),
dashboardData: function(){
var currentSite = this.get('currentSite');
console.log("in dashboardData", currentSite);
//other code that uses the currentSite
}.property('currentSite'),
});
services/session-service.js
import Ember from 'ember';
export default Ember.Service.extend({
currentSite: null,
setCurrentSite: function(){
var currentSite = this.get('session.current_site');
this.set('currentSite', currentSite );
}.on('init'),
});
I think this should allow someone to select a site in the header and have the dashboardData property in index update to use that selection. When the page initially loads the header defaults to the first site and the index renders it has the correct site value that it must have gotten from the session-service, however if you select another site the index does not get updated. Via the console.logs and debugging I can see that the header is observing the change and setting the value on the session-service.
Additionally I have tried solving this other ways (injecting the header service into the index and observing a property, injecting the index in the header and directly setting the value, sending and listening to events,etc) but I am willing to try anything or to be corrected that this isn't the correct way to solve the problem.
I am using ember 1.13.8 and moving to 2 isn't an option at the moment.

I don't think a service is an appropriate solution to this problem.
You want your application to have a good RESTful url design (respect for urls is a corner-stone of the Ember framework), so try to capture your application state in the URL.
Consider that if a user were to select a site, and then hit refresh they would lose their selection unless you stored it somehow in a cookie or localStorage.
I would recommend using either routes or query parameters to solve your problem.
Routes
Using routes is fairly straightforward (http://whatever.com/sites/pet-hampsters).
Query Params
You can also use query params, something like this http://whatever.com/?site=pet%20hampsters.
To do this you would write an action that bubbles up to your application controller and sets the value of the 'site' queryParam. Any of your sub-controllers on the currently active route can then read the site value with the new Ember.inject syntax. This is the conventional way to manage dependencies between controllers.

Related

Ember 2.5 Application Controller not present?

This is a very simple and probably easily resolved one.
I have created an emberjs application controller via ember generate controller application from which I want to return some basic computed properties based on the current path to higher level controllers and components. Basically, something like this:
export default Ember.Controller.extend({
entity: Ember.computed('currentPath', () => {
return this.get('currentPath').split('.')[0];
})
});
Oddly enough, I cannot access these computed properties anywhere (they turn out undefined, even if I replace them with a debug string), and in the Ember Inspector's view tree, the application controller is apparently not even present:
I have an older Ember 1.13.0 app, where I'm using the application controller with no difficulty. Have I missed a deprecation here? Or do I need to register the application controller in a specific location?
okay, I solved this differently using injection of the routing service directly into the component (see Component to be notified on route change in EmberJS 2 for details)

Multiple layouts in ember.js 2.7.0

I want to have two layouts, one for guests which can see some routes like: /, /contacts, /rules, etc... and another one for authenticated users, it means they must login before they can go to authorized routes. How can I define two layouts for different groups of routes?
there is a way to use Ember's router to your advantage to solve this problem by nesting the authenticated routes inside a route. Here's an example router:
Router.map(function() {
this.route('contacts');
this.route('rules');
this.route('authenticated', { path: '/' }, function() {
this.route('settings');
this.route('profile');
});
});
Going to /contacts and /rules wouldn't need any authentication, but going to /settings would.
Notice the path option passed to the authenticated route. Since we set it to / so it doesn't show up in the URL, it'll take the place of application.index. If this sounds strange to you, read about what an index page is in the Ember.js tutorial.
The answer is to maintain a service that saves the user's state.
// services/user-state
import Ember from 'ember';
export default Ember.Service.extend({
loggedIn: true
});
Then, depending on how you organized things, you could inject the service into a controller or a route. So - you'd have access to loggedIn
In your template, you would use handlebars/htmlbars if helper.
{{#if loggedIn}}
render logged-in stuff...
{{/else}}
render message explaining that this is only for logged in users
{{/if}}
You can also redirect the user to another route based on the current session data. You may have entire routes that are for certain roles, or you may have portions of your template that behave differently based on role.
If you weren't logged in, then you may not be able to visit your profile page. That route may be off limits entirely, or it may redirect you to a login page. On the other hand, maybe it's just a "login" button component or a portion of your template that shows if you aren't logged in vs. "hello sheriffderek" if you were.
There are also Ember addons for more robust "role" outlines. Think about a blog page, that may have an 'edit' button for the actual user, but 'flag' button for a moderator. In this case, you can't just have an entirely different route nested somewhere.
Most authentication addons / libraries are going to have a 'service' with some sort of session state. But Template wise, the basic idea is like a JS if/else statement.
if (helpful) {
this.upvote();
} else {
// something else
}
I hope this helps. : )
Have a look at the ember-simple-auth addon.
It has many useful classes all about authorization, including mixins for your usecase. If you want to make route only visible for logged-in users, simply use the AuthenticatedRouteMixin like this:
/app/routes/protectedRoute.js
import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin);

How to get session data created by Auth0 in Ember.js 2.3

I am using Ember.js 2.3 (and Ember-Data 2.3). I'm setting up a simple user auth process using Auth0. Nothing fancy yet, just installed Auth0 according to:
https://auth0.com/docs/quickstart/spa/ember2js/no-api
Now, my setup is pretty much exactly the same as the project given here. However, it seems that I can only access the session from application.hbs and not any other template. Or route. Or anything else.
So this handlebars snippet:
{{#if session.isAuthenticated}}
{{session.data.authenticated.profile.name}}
{{else}}
NOPE
{{/if}}
This works on application.hbs, but nowhere else. This does not make sense to me. If Auth0 itself says that session.data can be accessed from any template, and that such a handlebars snippet even exists, there must be something I'm missing. I need to be able to show certain portions of the client side as well as restrict some actions based on the currently signed-in user (and whether someone is actually signed in ), all of which are included in the session.data object.
It doesn't seem appropriate to pass this object to every component I'm going to create, and the only way I can think of getting this data right now is to manually get it from localStorage. I could perhaps make this manual process a mixin and have it included everywhere but before I try to find roundabout solutions, I want to make sure that I'm not missing something in the implementation itself.
How would I be able to access the session token throughout the application aside from application.hbs itself?
EDIT: Updating question in response to comments. My protected route looks like this:
import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
});
I am still unable to get session data, unfortunately.
In ember-simple-auth session is a service which You need to inject to the other routes. So if you want to use the session data other than the application template, you need to extend those routes with AuthenticatedRouteMixin
(just like you did in routes/home.js). In controllers you need to inject the service, for example:
// app/controllers/application.js
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service('session')
…
});
Take a look at ember-simple-auth.
You can find another implementation if you check ember-simple-auth-token
Edit:
Try this in your routes/protected_route.js (it works for me)
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
session: Ember.inject.service('session'),
actions: {
invalidateSession: function() {
this.get('session').invalidate();
}
}
});

How to create a controller property that updates when a service property changes

Using Ember 1.13
I may be missing something and going about this in the totally wrong way.
I have session state saved in an ember service that is available to all my controllers. It has a boolean property isExistingSession.
In the header of my app I want to conditionally display a login button or user info depending on the value of isExistingSession.
As far as I know I can't use the service property directly in my handlebar so my solution was to create a computed property on the applicationController that always equals the value of of isExistingSession on the sessionService
export default Ember.Controller.extend({
isExistingSession: function () {
return sessionService.get('isExistingSession');
}.property('sessionService.isExistingSession'),
But the computing the property off of an outside entity seems to be invalid.
Any idea on how to accomplish this?
As far as I know I can't use the service property directly in my handlebar
Just to clarify: any property available in a given controller will be available in its associated template. So if you inject your session service in your application controller you should be able to use any property of that service in your application.hbs template.
Now to solve your issue: how do you inject the service in your controller? You have 2 alternatives:
application.inject('controller', 'sessionService', 'service:session'); This will inject your session service in all the controllers, making in available to all your templates as well. (see http://guides.emberjs.com/v1.10.0/understanding-ember/dependency-injection-and-service-lookup/)
sessionService: Ember.inject.service('session'), This will inject your session service in a single controller (see http://emberjs.com/api/classes/Ember.inject.html#method_service)
Bottom line is that you should not need a computed property. I'd recommend using the Ember inspector to check whether your session is properly injected in your controller.
Also, consider using ember-simple-auth, an awesome add-on to manage authentication in Ember.

request process in emberjs

I am new guy to emberjs, I want to know flow of request in emberjs.
There are some query related to emberjs:
what is specific role of controller in emberjs, wherever I have seen we can create action in template.
There is standard naming convention and association between controller, routes and view, but how can to associate a controller, routes and views.
how to flow control when a request process?
a routes handover control to a controller or
a controller handover control to routes.
if we want to associate a controller and a routes manually then how to associate.
what is specific role of controller in emberjs, wherever I have seen
we can create action in template.
Controller is connecting your model with view like in MVC pattern. In Ember.JS you can use controller to keep your logic that will be used on one particular module, manage dependencies or store conditions. In templates you can use only simple conditions (without and/or), so whenever you need some more complex condition you should put in inside controller. For instance
App.PersonController = Ember.ObjectController.extend({
isPersonRich: function() {
return #get('person.money') > 1000000 && #get('person.isReal')
}.property('person.money', 'person.isReal')
});
So I person that is not fictional and have more 1 000 000 assets is rich.
{{#if isPersonRich}}
<p>This person is rich!</p>
{{/if}}
There is standard naming convention and association between
controller, routes and view, but how can to associate a controller,
routes and views.
Route usually fetch data from your backend.
App.PersonRoute = Ember.Route.extend({
model: function(params) {
this.store.find('person', params.person_id);
}
});
Each time when you enter the persons route ember is going to make call to your api (using ember data in this case) to find given person. Moreover, it's going to display loading route in this case and give you some fallback after failure.
PersonView would be the place where you can put your jQuery code that is going to be executed after template would be successfully rendered.
App.PersonView = Ember.View.extend({
didInsertElement: function() {
this.$().find('*[data-toggle=tooltip]').tooltip();
}
});
In this example I am adding bootstrap tooltip to template.
how to flow control when a request process?
Route is procceded before controller, you have even setupController method inside each route that set model to the controller by default.
if we want to associate a controller and a routes manually then how to
associate.
You can overwrite setupController method and eventually renderTemplate. There is nothing more to do. I advice you to stick to ember name conventions.
Additionaly take a look that if your controller is not going to handle fired action it is going to propagate to your route.