Ember exclude a mixin from specific route - ember.js

I have a mixin for authorization checking which is included in application route so that all other routes get access to the mixin without explicitly importing it. However, I want to exclude my login route from inheriting it. How can I achieve that? One workaround is to import the mixin in all routes individually but that's error prone and don't want to resort to it unless I am sure that there is no way out.
Thanks.

That is not possible! All routes are nested inside the application route.
Better to have another parent route for all authenticated routes (e.g. /main) and you can extend the mixin here.
Other routes like login, register, forgot password,.. etc will be out of that authenticated route.

Related

Ember Octane How to convert custom mixins

This question is related to: Does Ember Octane Route class support using mixins? Mixins are officially deprecated in Ember Octane.
Question:
What is the best option to replace Ember mixins with and how do I implement it?
Context:
I have custom mixins that expand functionality offered by ember-simple-auth (~v1.8.2), which was not available at the time the methods were created (see below). I am currently using ember-simple-auth 3.0.0 https://github.com/simplabs/ember-simple-auth. In the documentation on github, they appear to be using their own mixins on Ember Octane as you can see:
// my-engine/addon/routes/index.js
import Route from '#ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
export default class IndexRoute extends Route.extend(AuthenticatedRouteMixin) {
triggerAuthentication() {
this.transitionToExternal('login');
}
}
It appears that I am not the only one having this issue as Simple Auth doesn't know what route they want to go down either: https://github.com/simplabs/ember-simple-auth/issues/2185
Options:
Pure native classes, sharing functionality via class inheritance.
Utility functions which can be imported and used in multiple classes.
Services which can be injected into multiple classes, sharing
functionality and state between them.
I have the following authentication mixins:
Application Route: This handles whether the user is logged in and whether the user has two-factor authentication (2FA) enabled, and if it is enabled, it verifies that the user has been authenticated through 2FA. This is because simple auth did not offer 2FA at the time of development of the mixin(s)
Authenticated Route: Makes sure the user is logged in. If they are not logged in then the user is routed to the login page. Once they login, they are routed back to the page that they initially tried to go to
UnAuthenticated Route: Prevents logged in users from going to certain routes that logged in users should not go to
Firstly I want to make very clear that mixins are not "officially deprecated" in Ember, and to my knowledge there's not even an active RFC about it. As the Upgrade Guides explain, Glimmer components do not support mixins due to not extending EmberObject, but the pre-existing framework classes (Route, Controller, etc) necessarily have to or it would be a breaking change.
There is no best option to replace mixins as it depends on the usage of the API. If you are asking how to replace ember-simple-auth mixins, my answer is that you can't until the addon itself provides alternative APIs. Mixins and the example code you posted will continue working for the foreseeable future.
You can see an example of using class inheritance to share functionality in this PR.
when i migrated to ember Octane i tried to replace ember-simple-auth mixins, i found that it would take me lot of time than rewriting my own authentication service, my example in this twiddle, i am using cookies, Auth service
if you are not using cookies , you could customize your adapter to include a token in the headers
I circled back with ESA on Git, and the same issue I cited in my OP has been closed with a new issue that has subsequently been merged:
https://github.com/simplabs/ember-simple-auth/pull/2198
ESA has now updated their library to get rid of route mixins.

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);

Reuse ember controller for two routes, but pre populate part of model on one of the routes

Lets say we were modelling repositories in an app. We would want to be able to see all of our repo's at /repos/.
We can create a repo at /repos/create. In our model a repo has a owner (user), going down this route I would want my first form field in my create.hbs template to be selecting a user.
But it may also make sense to create a repo through viewing a user at /users/userId/repos, and then perhaps the route being /users/userId/repos/create. The difference here would be that the first field of my form would have the user pre populated.
I know that I can reuse the same controller by specifying this in my route:
controllerName: 'repos/create'
(what would the ember generate statement/ the place to put this route in my folder structure be, for this complex route?)
But how would I pass in the user to the repos/create controller?
The comment by #Artych provided the best way to do this currently, so to expand on this.
In the end I created a repo-create component that may or may not be passed a user object. The logic for handling what to pre-populate can then be set in the logic of the component itself.
After this is was simply a case of having a one line template at both /repos/create.hbs and /user/repos/create.hbs, with the component.
I then followed the approach of using my user as my model in the routes/user/repos/create.js route and passing it to the component in my template as
{{repo-create user=model}}
With this approach I avoided doing anything explicitly on controllers, and moving up to Ember 2.x will be alot less painful.

Namespacing, sharing models, reusing controller in Ember

I have an Ember 1.10 application with api support for 2 models, Students and Parents, and basic CRUD setup for each model, as well as the ability to read and write messages to/from Students and Parents when visiting their respective /show pages.
I must create an Inbox in which the messages as well as all the other data on Students and Parents is available and must do so without the ability to change or add to the api (i.e. I can' create a Messages endpoint).
Basically, I need to keep the Student and Parent models and controllers as they are, but need to be able to access them from routes namespaced under the Inbox like so:
Students
-inbox
-index
-edit
-show
Parents
-inbox
Inbox
StudentInbox <-- should use StudentsRoute and StudentsController
-index
-show
-edit
ParentInbox
-index
-show
-edit
So that visiting /inbox/students/123 will be using the model defined in the StudentRoute and the actions and properties defined on the StudentController. The InboxStudentsShowRoute should similarly have access to the StudentsShowRoute and Controller.
Instead of copying and pasting the existing code from the Student and Parent routes and controllers, is there a way to proxy actions fired from templates under the Inbox namespace to the relevant models and controllers and maintain the ability to pass in params.
For example, passing in params like ?unread=true while on the InboxStudentsShow route would pass these to the StudentsShowRoute and Controller (and bubble appropriately to it's parent route and controller) or editing a Student from student/inbox/123/edit template would work just as it does on the student/123/edit template?
New to Ember and know there has to be a better way to reuse existing code and proxy actions from a namespace. Any advice would be greatly appreciated!
I'd go here with creating a Mixin which holds same code for Route and specify controllerName, templateName in InboxStudentsShowRoute. Then, you extend from this Mixin in both routes. Model, template, actions, properties will be the same.
For example, passing in params like ?unread=true while on the
InboxStudentsShow route would pass these to the StudentsShowRoute and
Controller (and bubble appropriately to it's parent route and
controller)
I'm not sure what do you mean here by pass these to the StudentsShowRoute and Controller, but CONTROLLER properties changed in one route should remain the same in another route, because controllers are singletons in Ember. I don't know what do you mean by passing query param to another route, because if you transition to other part of your app you change URL and you can't have 2 urls at the same time, so how could query params be passed to another route?
In Ember 1.10 query params are defined in Controllers so I guess behaviour will remain the same.
Example of InboxStudentsShowRoute(move code from StudentsShowRoute to StudentsShowMixin, define controller and template name):
import Ember from 'ember';
import StudentsShowMixin from '../mixins/students-show';
export default Ember.Route.extend(StudentsShowMixin, {
controllerName: 'students/show',
templateName: 'students/show'
});

ember-cli & common route and controller behavior options: Subclass, Mixin or initializer (service)?

I have properties and methods I want exposed to most/all instances of a class (i.e. all but a handful of routes, controllers, whatever). There seems to be multiple ways to accomplish this and I'm looking for guidance for best practices here.
More specifically, I've created a property on my application controller to hold a user session object. I want all other controllers to expose this data as if I had typed:
needs: ['application'],
userSession: Ember.computed.alias('controllers.application.userSession')
directly into the controller.
Further, I want to override all routes (other than the login route and maybe a couple more) implementations of beforeModel to check for the presence of userSession and redirect to the login route if absent.
This is being implemented in ember-cli FYI. So, that being the case, what's the "right" approach here? Do I try to inject these changes via initializers/services? Do I create mix-ins to do this stuff (I'm not a fan of having to remember to do that every time someone working on this does ember g controller that they then have to remember to add the mixin).
Sounds very much like the use of an initializer and a service is the best approach (them being split up makes for cleaner code). The initializer is just the code to load the service, the service does the hard work. The initializer should look something like:
import AuthService from '../services/auth';
export default {
name: 'auth-service',
initialize: function( container, app ) {
app.register( 'service:auth', AuthService, { singleton: true } );
app.inject( 'controller', 'auth', 'service:auth' );
app.inject( 'route', 'auth', 'service:auth' );
}
};
This then injects auth into every controller and route, and you should move the userSession from your application to the service.
My auth service is too big (and in my case: too specific, as it uses Firebase) to be quoting it here. I gave the gist of it in an answer yesterday: Short delay when trying to run redirect with ember route with firebase authentication
And as you mentioned it: people don't strictly need to remember to include mixins as you can override the blueprint that is used when someone does ember generate: http://www.ember-cli.com/#generators-and-blueprints