Ember.js dynamic routes not resolving in test, but work in production - ember.js

So, I'm trying to use the Twitter-style URL syntax, allowing a user to go to example.com/quaunaut to visit the user page of the user with the username 'quaunaut'. I was able to accomplish this via:
app/router.js
export default Router.map(function() {
this.route('users.show', { path: '/:user_username' });
});
app/routes/users/show.js
export default Ember.Route.extend({
model: function(params) {
return this.store.find('user', { username: params.user_username }).then(function(result) {
return result.get('firstObject');
});
},
serialize: function(model) {
return { user_username: model.get('username') };
}
});
Now, when live or run via ember s, this works fantastically. However, in tests, it seems for some reason to not resolve.
var application, server, USERS;
USERS = {
'example1': [{
id: 1,
username: 'example1'
}],
'example2': [{
id: 2,
username: 'example2'
}]
};
module('Acceptance: UsersShow', {
beforeEach: function() {
application = startApp();
server = new Pretender(function() {
this.get('/api/users', function(request) {
return [
201,
{ 'content-type': 'application/javascript' },
JSON.stringify(USERS[request.queryParams.username])
];
});
});
},
afterEach: function() {
Ember.run(application, 'destroy');
server.shutdown();
}
});
test('visiting users.show route', function(assert) {
visit('/example1');
andThen(function() {
assert.equal(currentPath(), 'users.show');
assert.equal(find('#username').text(), 'example1');
});
});
Which results in the following test results:
Acceptance: UsersShow: visiting users.show route
✘ failed
expected users.show
✘ failed
expected example1
So, any ideas why currentPath() isn't resolving? If you also have any recommendations for better means to implement what I'm looking to do here, I'm certainly open to it.

Your visit syntax isn't quite right, should be:
test('visiting users.show route', function(assert) {
visit('/example1').then(function() {
assert.equal(currentPath(), 'users.show');
assert.equal(find('#username').text(), 'example1');
});
});

Related

Is my router.js is correct?

Hi I am getting an error of UnrecognizedUrl when I am trying to access my route on my browser to dashboard/posts/id/comments. Below is my router.js I would like to ask if my router is wrong or can someone please tell me the right approach
this.route('dashboard', function() {
this.route('posts', function() {
this.route('show', { path: ':post_id' }, function() {
this.route('comments', { path: ':post_id/comments'}, function() { });
});
});
});
However if I put the {{outlet}} on my resource file app/pods/dashboard/posts/show/template.hbs it does show the content I put on my app/pods/dashboard/posts/show/comments/template.hbs when I changed my router.js to
this.route('dashboard', function() {
this.route('posts', function() {
this.route('show', { path: ':post_id' }, function() {
this.route('comments');
});
});
});
My goal is I want to show the content of app/pods/dashboard/posts/show/comments/template.hbs on a different page which in the browser url should be dashboard/posts/id/comments
It should be like
this.route('dashboard', function() {
this.route('routeA', function() {
this.route('childRouteA', { path: '/:childRouteA_id' }, function() {
this.route('childRouteAb');
});
});
});
Ex: dashboard/routeA/id/childRouteAb
If childRouteAb is a dynamic id then, it should be like
this.route('dashboard', function() {
this.route('routeA', function() {
this.route('childRouteA', { path: '/:childRouteA_id' }, function() {
this.route('childRouteAb', { path: '/:childRouteAb'});
});
});
});
Ex: dashboard/routeA/id/id2
If you need the url to specify the type of id before the id, you can do like this.
this.route('dashboard', function() {
this.route('routeA', function() {
this.route('childRouteA', { path: '/childRouteA/:childRouteA_id' }, function() {
this.route('childRouteB', { path: '/childRouteB/:childRouteB_id'});
});
});
});
Ex: dashboard/routeA/childRouteA/id1/childRouteB/id2

Uncaught TypeError: Cannot read property 'login' of undefined

I'm trying to set up OAuth with Firebase and Ember. For some reason it's returning the error
Uncaught TypeError: Cannot read property 'login' of undefined
App.LoginController = Ember.ObjectController.extend({
actions: {
login: function() {
var controller = this;
debugger;
controller.get("session").login().then(function(user) {
// Persist your users details.
}, function() {
// User rejected authentication request
});
}
},
});
I was thinking maybe the user is undefined, but I've defined it in a model:
App.User = DS.Model.extend({
username: DS.attr('string'),
});
Then I thought maybe it's the "session" that's undefined--I used the debugger to look up & it says it's an unknown mixin.
var session = Ember.Object.extend({
ref: new Firebase("https://glowing-fire.firebaseio.com/"),
addFirebaseCallback: function() {
var session = this;
this.get("ref").onAuth(function(authData) {
if (authData) {
session.set("isAuthenticated", true);
} else {
session.set("isAuthenticated", false);
}
});
}.on("init"),
login: function() {
return new Promise(function(resolve, reject) {
this.get("ref").authWithOAuthPopup("facebook", function(error, user) {
if (user) {
resolve(user);
} else {
reject(error);
}
});
});
},
currentUser: function() {
return this.get("ref").getAuth();
}.property("isAuthenticated")
});
App.Session = Ember.Object.extend({
initialize: function(container, app) {
app.register("session:main", session);
app.inject("controller", "session", "session:main");
app.inject("route", "session", "session:main");
}
});
I'd really appreciate your help!
The issue might be that you are trying to access an injected property, but the code that does the injection is never called. The recommended way to inject properties is described on this page.
More specifically the samples below (from the Ember.js website) should help
Using an application initializer:
App = Ember.Application.extend();
App.Logger = Ember.Object.extend({
log: function(m) {
console.log(m);
}
});
App.IndexRoute = Ember.Route.extend({
activate: function(){
// The logger property is injected into all routes
this.logger.log('Entered the index route!');
}
});
Ember.Application.initializer({
name: 'logger',
initialize: function(container, application) {
application.register('logger:main', App.Logger);
application.inject('route', 'logger', 'logger:main');
}
});
App.create();
or directly on the application:
App = Ember.Application.create();
App.register('logger:main', {
log: function(m) {
console.log(m);
}
}, { instantiate: false });
App.inject('route', 'logger', 'logger:main');

Ember-simple-auth with laravel

I'm having trouble creating a custom authenticator for my laravel backend. I'm not sure if this is the correct custom authenticator for laravel, but I'm using this as a starting point (https://github.com/simplabs/ember-simple-auth/blob/master/examples/6-custom-server.html).
My Ember.SimpleAuth is undefined. Here is what I have in my app.js.
import Ember from 'ember';
import Resolver from 'ember/resolver';
import loadInitializers from 'ember/load-initializers';
Ember.MODEL_FACTORY_INJECTIONS = true;
window.ENV = window.ENV || {};
window.ENV['simple-auth'] = {
authorizer: 'authorizer:custom'
};
Ember.Application.initializer({
name: 'authentication',
before: 'simple-auth',
initialize: function(container, application) {
//register the laravel authenticator so the session can find it
container.register('authenticator:laravel', App.LaravelAuthenticator);
container.register('authorizer:custom', App.CustomAuthorizer);
}
});
var App = Ember.Application.extend({
modulePrefix: 'ember-simple-auth-sample', // TODO: loaded via config
Resolver: Resolver
});
App.LaravelAuthenticator = Ember.SimpleAuth.Authenticators.Base.extend({
tokenEndpoint: '/v4/session',
restore: function(data) {
return new Ember.RSVP.Promise(function(resolve, reject) {
if (!Ember.isEmpty(data.token)) {
resolve(data);
} else {
reject();
}
});
},
authenticate: function(credentials) {
var _this = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.$.ajax({
url: _this.tokenEndpoint,
type: 'POST',
data: JSON.stringify({ session: { identification: credentials.identification, password: credentials.password } }),
contentType: 'application/json'
}).then(function(response) {
Ember.run(function() {
resolve({ token: response.session.token });
});
}, function(xhr, status, error) {
var response = JSON.parse(xhr.responseText);
Ember.run(function() {
reject(response.error);
});
});
});
},
invalidate: function() {
var _this = this;
return new Ember.RSVP.Promise(function(resolve) {
Ember.$.ajax({ url: _this.tokenEndpoint, type: 'DELETE' }).always(function() {
resolve();
});
});
}
});
// the custom authorizer that authorizes requests against the custom server
App.CustomAuthorizer = Ember.SimpleAuth.Authorizers.Base.extend({
authorize: function(jqXHR, requestOptions) {
if (this.get('session.isAuthenticated') && !Ember.isEmpty(this.get('session.token'))) {
jqXHR.setRequestHeader('Authorization', 'Token: ' + this.get('session.token'));
}
}
});
loadInitializers(App, 'ember-simple-auth-sample');
export default App;
Ember.SimpleAuth doesn't exist anymore, it now has it's own global SimpleAuth when you use the browserified distribution. It looks like you're using ember-cli though which means you're using the AMD distribution of Ember Simple Auth anyway which doesn't define any global at all. For instructions on how to use Ember Simple Auth with ember-cli see this blog post.
Apart from that your authenticator and authorizer look fine on first glance and should generally work that way.

Push data belonging to a parent model with Ember.js

Following this post, the scenario is the same outlined here, i.e. a list of posts, each having zero or more comments.
Suppose one has the following router.
App.Router.map(function() {
this.resource('posts', function() {
this.resource('post', { path: '/:post_id' }, function() {
this.resource('comments', function() {
this.route('create');
});
});
});
});
The model for a post is the following one.
App.Post = DS.Model.extend({
title: DS.attr('string'),
comments: DS.hasMany('comment', { async: true })
});
A post, for example, is the following one.
{
"post": {
"id": 1,
"title": "Rails is omakase",
"links": { "comments": "/posts/1/comments" }
}
}
This way, I am able to load the comments of this post using the following route.
App.CommentsRoute = Ember.Route.extend({
model: function() {
return this.modelFor('post').get('comments');
}
});
The question now is: how can I create the CommentsCreateRoute, so that the new comment is actually posted to /posts/1/comments and not to /comments?
So far I have the following.
App.CommentsCreateRoute = Ember.Route.extend({
model: function() {
var newComment = this.store.createRecord('comment');
newComment.set('title', 'test comment');
return newComment;
}
});
App.CommentsCreateController = Ember.ObjectController.extend({
actions: {
saveEditing: function(){
var newComment = this.get('model');
newComment.save().then(function(comment) {
/* OK saved */
}, function(response) {
/* show errors */
});
}
}
});
Does this REST scheme make sense?
GET /posts/{post_id}/comments
PUT /posts/{post_id}/comments/{comment_id}
POST /posts/{post_id}/comments
or should I use the following?
GET /posts/{post_id}/comments
PUT /comments/{comment_id}
POST /comments
or any other? Which would be the most "standard"?

Ember + Ember Data Route error handling issue

In soume routes in my app error action is never triggered and I can't figure out why. On some Routes error action works fine.
This is application route:
Simitu.ApplicationRoute = Ember.Route.extend({
init: function() {
this._super();
Simitu.AuthManager = Simitu.AuthManager.create();
},
model: function() {
if (Simitu.AuthManager.get('session.user'))
return this.store.find('admin', Simitu.AuthManager.get('session.user'));
},
actions: {
error: function(reason, transition) {
if (reason.status === 401) {
Simitu.AuthManager.reset();
this.transitionTo('login');
}
}
}
});
On this route Error is never triggered:
Simitu.PlacesIndexRoute = Ember.Route.extend({
model: function() {
var self = this;
// force adapter request
this.store.find('place');
return this.store.filter('place', function(record) {
// return just places that belongs to this client / application
return record.get('client_id') === self.modelFor('client');
});
},
actions: {
createNew: function() {
var place = this.store.createRecord('place');
// tree structure in places is not implemented yet
//parent = this.store.find('place', params.place_id);
place.set('client_id', this.modelFor('client'));
// open place
this.transitionTo('place', place);
},
error: function(error, transition) {
return true;
}
}
});
And on this Route everything works just fine:
Simitu.ClientsRoute = Ember.Route.extend({
model: function() {
return this.store.find('client');
},
actions: {
error: function() {
return true;
}
}
});
Have anybody some ide why?
The error action is fired on the resource, not an individual route.
http://emberjs.jsbin.com/cayidiwa/1/edit
This is how my router looks like. Maybe it breaks because of the nesting or filter logic in models. I fixed it in beforeModel hook in routes but still have not clue what is wrong with my first solution.
Simitu.Router.map(function () {
this.resource('login');
this.resource('clients');
this.resource('client', { path: 'clients/:client_id'}, function() {
this.resource('places', function() {
this.resource('place', { path: ':place_id' });
});
this.resource('placecategories',{ path: 'places-categories' }, function() {
this.route('new');
});
});
});
I move some of auth handling logic to beforeModel hook.
Simitu.AuthRoute = Ember.Route.extend({
beforeModel: function(transition) {
if (!Simitu.AuthManager.isAutenticated()) {
this.redirectToLogin(transition);
}
},
redirectToLogin: function(transition) {
this.transitionTo('login');
},
actions: {
error: function(reason, transition) {
if (reason.status === 401) {
Simitu.AuthManager.reset();
this.redirectToLogin(transoition);
}
}
}
});