How to manually invoke a route in ember.js RC1 - ember.js

I have the following controller and I'd like to bubble up an event using send
App.PersonController = Ember.ObjectController.extend({
page: function(page) {
var model = PersonApp.Page.create({id: page});
this.send("person.page", model); //should bubble up ...
}
});
here is my route setup
PersonApp.Router.map(function(match) {
this.resource("person", { path: "/" }, function() {
this.route("page", { path: "/page/:page_id" });
});
});
here is the simple page model (shim basically)
PersonApp.Page = Ember.Object.extend({
});
although I'm using the route "person.page" and I'm passing a valid model I get the following error (seemingly the router does not have this route?)
Uncaught Error: Nothing handled the event 'person.page'.
If it helps debug the controller / router relationship I noticed inside my controller if I dump this.get('target') ...
_debugContainerKey: "router:main"
and if I dig further ... and print this
this.get('target').get('router')
I see a router w/ my route under the currentHandlerInfos array ... not sure if I should be this deep though
... another slight update
If I do this (full blown) it seems to modify the window.location but my model/setupController hooks on the route are never hit
this.get('target').get('router').transitionTo(route, model);

I think, send is just used for events of a route. Assuming your controller would call send like this:
//in the controller
this.send("personPage", model);
// a matching Route
App.PersonRoute = Ember.Route.extend({
events : {
personPage : function(page){
// this should be called
}
}
});
For your case you need to leverage transitionTo (your access on the router property was too much, i think. The router instance of Ember.Router has again a router property. Pretty confusing :-)).
this.get("target").transitionTo("person.page", model);

Related

Emberjs call a method from an other object

This might be a silly question, but I can't find out anything about it anywhere...
I create a method in one of my controller to verify if the user session is still good, and I'm using this method in almost every page of my app in my beforeModel. But the thing is that I don't want to copy/paste the code every time in every route, this will be dirty and I really don't like it.
Lets say I have this controller :
App.LoginController = Ember.ObjectController.extend({
...
isSession: function() {
var session = this;
Ember.$
.get(host + '/session', function(data) {
console.log('DEBUG: Session OK');
})
.fail(function() {
console.log('DEBUG: Session FAIL');
session.transitionToRoute('login');
});
}
});
How can I call it in this router :
App.HomeRoute = Ember.Route.extend({
beforeModel: function(transition) {
//Here
},
model: function() {
return this.store.all('login');
}
});
I've tried this this.get('loginController').isSession(); but I receive this error Error while loading route: TypeError: Cannot call method 'isSession' of undefined
Thanks for the help !
[edit]
I don't have much to show but this :
My map
App.Router.map(function() {
this.route('login', { path: '/' });
this.route('home');
this.resource('enquiries', function() {
this.route('enquiry', { path: '/:enquiry_id' }, function() {
this.route('update');
});
});
});
Most likely I only Have a LoginController and my HomeRoute. (its the beginning of the app)
I don't need to create a Route for my Login because I have an action helper in my login template and I'm redirected to my Home template after that.
You need to use controllerFor() method in order to call method on controller from router. If method is an action you need to use send() method, like this.controllerFor('login').send('isSession')
App.HomeRoute = Ember.Route.extend({
actions: {
willTransition: function(transition) {
transition.abort();
this.controllerFor('login').isSession()
}
});
If you don't need a return value from isSession you might consider making it an action on a top-level route. The router.send method in the docs has a pretty good example of how you declare actions as well as how you call them. Note that send is also a method you can call on a controller. Actions bubble up from a controller, to the parent route, and then all the way up the route hierarchy, as shown here

Ember.js setting application property on load

I'm trying to fetch the current logged in user via my REST API and then set it as a property of the ApplicationController. This is how I'm trying to do it:
App.ApplicationController = Ember.Controller.extend({
init: function() {
this._super();
var self = this;
App.User.findCurrent().then(function(user) {
self.set('currentUser', user);
});
}
});
App.User = Ember.Object.extend({});
App.User.reopenClass({
findCurrent: function() {
return $.getJSON('/api/v1/users/current').then(
function(response) {
return response.user;
}
);
}
});
When I check the Chrome network tab, I see there's a call to the API and the JSON is returned, but when I try to access e.g. {{currentUser.name}} in my application template (or a partial of it), it doesn't return the name. No errors are given as well.
But in the application template it doesn't return it.
What am I missing?
Thanks!
Edit
When I create another controller, e.g. HelpController and visit /help, then {{currentUser.name}} does return the username:
App.HelpController = Ember.Controller.extend({
needs: ['application'],
currentUser: Ember.computed.alias('controllers.application.currentUser')
});
Edit 2
Sorry, I forgot to mention that I'm actually trying to use {{currentUser.name}} from a partial ({{partial 'sidebar'}}), but that shouldn't change anything, because that's the same scope, right?
Edit 3
I noticed something very strange. When I call {{currentUser.name}} in my application template (which is not what I want btw), then it also works in the {{partial 'sidebar'}}.
Edit 4
As per request:
DEBUG: Ember.VERSION : 1.0.0-rc.6 ember.js?body=1:361
DEBUG: Handlebars.VERSION : 1.0.0-rc.4 ember.js?body=1:361
DEBUG: jQuery.VERSION : 1.10.0
This isn't the correct place to put this logic. You can use the route hooks model and afterModel on the ApplicationRoute, to do this easily. In general in ember loading of data is done in the routes hooks. This allows the router pause while loading so by the time your controller and templates come into play, they are working with loaded data.
App.ApplicationRoute = function() {
model: function() {
return App.User.findCurrent();
},
afterModel: function(model) {
App.set('currentUser', model)
}
}

Ember: Get route instance from the controller

I have a multi-step flow that the user can go through sequentially or jump straight to a section (if the sections in between are completed). I think this logic should be in the Route object. However, from within the controller, how do I access the route instance. For example, it would be ideal to be able to do something like this in the controller:
App.Flow = Em.ObjectController.extend({
submit: function(){
// Validation and XHR requests
// ...
// Go to the next step
route.goToNextStep();
}
}
From within a controller, you can access the router via this.get('target'). So this.get('target').send('goToNextStep') should work.
Like so:
App.Flow = Em.ObjectController.extend({
submit: function(){
// ...
this.get('target').send('gotoNextStep');
}
}
App.FlowRoute = Ember.Route.extend({
events: {
gotoNextStep: function(){
// ...
this.transitionTo(routeName);
}
}
}
You need to get the route for such conditions,
so from the controller just say,
App.Flow = Em.ObjectController.extend({
submit: function(){
var self =this;
// Validation and XHR requests
// ...
// Go to the next step
self.send('goToNextStep');
}
}
and define your goToNextStep event in your route's event hash
'this' is what points to the router, but you shouldn't add any methods to that prototype. Instead, make some sort of event that triggers the transition to the next step.
In addition to target, another way to do this in Ember now is with getOwner.
For example, to send an action to your application route:
import Component from '#ember/component';
import { action } from '#ember/object'; // https://github.com/pzuraq/ember-decorators-polyfill
import { getOwner } from '#ember/application';
export default class MyTopLevelComponent extends Component {
#action
closeModal() {
getOwner(this).lookup('route:application').send('closeModal');
}
});

Connecting a View in connectOutlet with Ember RC1

From this [EDIT] [ToDo's sample]1, [/EDIT] I can connect a View via the connectOutlet. Is there an updated example for this using RC1?
index: Ember.Route.extend({
route: '/',
connectOutlets: function( router ) {
var controller = router.get( 'applicationController' );
var context = controller.namespace.entriesController;
context.set( 'filterBy', '' );
// This require was left here exclusively for design purposes
// Loads decoupled controller/view based on current route
require([ 'app/controllers/todos', 'app/views/items' ],
function( TodosController, ItemsView ) {
controller.connectOutlet({
viewClass: ItemsView,
controller: TodosController.create(),
context: context
});
}
);
}
}),
Actually the example you are linking should work. As you might know the Router API has changed and the code based on pre4 should still work. I am not aware of the requirements for the Todos App, so i cannot 100% tell, if it still works:
Todos.Router.map(function() {
this.resource('todos', { path: '/' }, function() {
this.route('active');
this.route('completed');
});
});
Todos.TodosRoute = Ember.Route.extend({
model: function() {
return Todos.Todo.find();
}
});
Todos.TodosIndexRoute = Ember.Route.extend({
setupController: function() {
var todos = Todos.Todo.find();
this.controllerFor('todos').set('filteredTodos', todos);
}
});
Here a little summary of the changes to the old router API:
You don't extend the Ember.Router Class anymore.
The URL Mappings don't reside in the Routes anymore. This is done via Todos.Router.map.
There is no connectOutlets event anymore in your routes. Instead there are 3 events you can implement: model(), setupController() & renderTemplate().
A little explanation on the hooks:
model(): Is called once when your route is entered via URL. This should return your model, which should become the content of your controller.
setupController(): Here you can get your controller and set its content how you may like. The default implementation sets the controller, that is name matching your route to the result of model().
renderTemplate(): Inside this hook you should use the new render method of routes to do the rendering. The render method is somehow the method that matches the old connectOutlets the most. There is also default implementation. Therefore it is also not implemented in the pre4 version of todomvc.
As Milkyway stated, you realy have to read the guides, but i hope this gets you started a little bit better.

How to save path and return to it with Ember's V2 Router

So, I'm having some issues with Ember's new router. I'm trying to save and later return to the current path for a given dynamic segment, so my urls might look like
#/inventory/vehicle/1001
Which can then branch off into
#/inventory/vehicle/1001/details
#/inventory/vehicle/1001/photos
#/inventory/vehicle/1001/description
etc. I need a way to return to the most recent route. The Ember guides have a method for this here:
http://emberjs.com/guides/routing/redirection/
The problem with this method is that by creating the "/choose" route and assigning it to "/", this overwrites the standard "/inventory/vehicle/1001" route. For instance, if I were to try to create a linkTo a vehicle like so:
{{#linkTo "vehicle" vehicle}}
Then Ember will throw an error because the "vehicle" route no longer exists. Instead, it must be set to:
{{#linkTo "vehicle.choose" vehicle}}
Which works, activates the VehicleChooseRoute and everything. Except, since "vehicle.choose" is technically a child of "vehicle", the #linkTo ONLY has an active class applied when the current route is
#/inventory/vehicle/1001
Which instantaneously redirects to the latest filter, and so it's basically never on. So basically I'm trying to figure out a way around this. I tried changing the path of "vehicle.choose" to be the standard path (#/inventory/vehicle/1001/choose) so it doesn't overwrite the "vehicle" route, and then setting up VehicleRoute like so:
Case.Router.map(function() {
this.resource('inventory', function(){
this.route('review');
this.route('sheets');
this.resource('vehicle', { path: '/vehicle/:vehicle_id' }, function(){
this.route('choose');
this.route('details');
this.route('consignor');
this.route('additional');
this.route('price');
this.route('dmv');
this.route('expenses');
this.route('description');
this.route('tasks');
});
});
});
App.VehicleRoute = Ember.Route.extend({
model: function(params) {
return Case.Vehicle.find(params.vehicle_id);
},
setupController: function(controller, model) {
model.set('active', true);
},
redirect: function() {
this.transitionTo('vehicle.choose');
}
})
Case.VehicleChooseRoute = Ember.Route.extend({
redirect: function() {
var lastFilter = this.controllerFor('vehicle').get('lastFilter');
this.transitionTo('vehicle.' + (lastFilter || 'details'));
}
});
But the problem that arises from this (aside from feeling rather hacked together) is that redirect replaces the entire template that would normally be rendered by "vehicle" so I only get the subview. So that's not an option.