Ember.Router's transitionTo not updating URL - ember.js

I implemented the following using latest ember.js (0.9.8.1) by referring to Ember Router not updating url in Chrome and Safari. When I try switch b/w routes via router.transitionTo('route path') method (last 3 lines of the attached snippet), browser url is not updated correctly, but I do see the view markup being updated confirming that state change do happen. Could some help in identifying whether am I missing something here?
BTW: I tested this in Chrome 20.0.1132.27 beta-m
App = Ember.Application.create({});
App.IndexView = Ember.View.extend({
template: Ember.Handlebars.compile(
'hello world from index'
)
});
App.ShowView = Ember.View.extend({
template: Ember.Handlebars.compile(
'hello world from show'
)
});
App.Router = Ember.Router.extend({
location: 'hash',
enableLogging: true,
root: Ember.State.extend({
index: Ember.State.extend({
route: '/',
redirectsTo: 'tasks'
}),
tasks: Ember.State.extend({
route: '/tasks',
index: Ember.ViewState.extend({
route: '/',
view: App.IndexView
}),
show: Ember.ViewState.extend({
route: '/show',
view: App.ShowView
})
})
})
});
var router = App.Router.create({});
App.initialize(router);
router.transitionTo('root');
router.transitionTo('root.tasks');
router.transitionTo('root.tasks.show');

I ran your code, and in the console, I have the following error "Object hash has no method 'setURL'". By debugging a bit, I found that you have to define the location of the Router with:
location = Ember.Location.create({ style: 'hash' })
or
location = Ember.Location.create({implementation: 'hash'})
I don't know why exactly, but it seems to work. Perhaps it's only due to ember version.

You are mixing up 2 initialization methods.
When you define App.Router all you need to do is call App.initialize(). It automatically creates an instance of Ember.Router and assigns it to the variable App.stateManager. You can then use App.stateManager to call transitionTo.
You can define the variable router to extend Ember.Router then call App.initialize(router). This method also creates an instance of Ember.router and assigns it to App.stateManager. You can then use App.stateManager to call transitionTo.
Either of the methods will work but I prefer method 1. To manipulate the route we always use App.stateManager.

Related

ember.js cannot set property 'store' of undefined

I have been trying to set up an Ember.js application together with a RESTful API i have created in Laravel.
I have encountered a problem trying to get the data trough the store, and depending on my implementation, I get different errors, but never any working implementations.
The ember.js guide have one example, other places have other examples, and most information I find is outdated.
Here's my current code:
App = Ember.Application.create();
App.Router.map(function() {
this.resource("world", function() {
this.resource("planets");
});
});
App.PlanetsRoute = Ember.Route.extend({
model: function() {
return this.store.find('planet');
}
});
App.Planet = DS.Model.extend({
id: DS.attr('number'),
name: DS.attr('string'),
subjectId: DS.attr('number')
});
And when I try to click the link for planets, thats when the error occurs, and I get the following error right now:
Error while loading route: TypeError {} ember-1.0.0.js:394
Uncaught TypeError: Cannot set property 'store' of undefined emberdata.js:15
No request is sent for /planets at all. I had it working with a $.getJSON, but I wanted to try to implement the default ember-data RESTAdapter.
For reference, these are some of the implementations i've tried:
var store = this.get('store'); // or just this.get('store').find('planet')
return store.find('planet', 1) // (or findAl()) of store.findAll('planet');
App.store = DS.Store.create();
I also tried DS.Store.all('planet') as I found it in the ember.js api, but seemed like I ended up even further away from a solution.
Most other implementations give me an error telling me there is no such method find or findAll.
EDIT (Solution)
After alot of back and forward, I managed to make it work.
I'm not sure exactly which step fixed it, but I included the newest versions available from the web (Instead of locally), and the sourcecode now looks like this:
window.App = Ember.Application.create();
App.Router.map(function() {
this.resource("world", function() {
this.resource("planets");
});
});
App.PlanetsRoute = Ember.Route.extend({
model: function() {
return this.store.findAll('planet');
}
});
App.Planet = DS.Model.extend({
name: DS.attr(),
subjectId: DS.attr()
});
The error you had is probably due to the fact that you added a "s" plural of your objects.
i.e. if you use
App.Planets = DS.Model.extend({
})
you would get that error.

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 get an instance of a controller in Ember.js?

I'm trying to access an instance of a controller that has been wired automatically using App.initialize();
I've tried the below but it returns a Class not an instance.
Ember.get('App.router.invitesController')
I have a quick post about this exact subject on my Blog. It's a little big of a different method, but seems to work well for Ember.js RC1.
Check it out at: http://emersonlackey.com/article/emberjs-instance-of-controller-and-views
The basic idea is to do something like:
var myController = window.App.__container__.lookup('controller:Posts');
This answer works with RC1/RC2.
Now you can use the needs declaration in order to make the desired controller accessible. Here's an example:
Suppose I want to get something from my SettingsController from within my ApplicationController. I can do the following:
App.SettingsController = Ember.Controller.extend({
isPublic: true
});
App.ApplicationController = Ember.Controller.extend({
needs: 'settings',
isPublicBinding: 'controllers.settings.isPublic'
});
Now in the context of my ApplicationController, I can just do this.get('isPublic')
You can access a controller instance inside an action in the router via router.get('invitesController'), see http://jsfiddle.net/pangratz666/Pk4k2/:
App.InvitesController = Ember.ArrayController.extend();
App.Router = Ember.Router.extend({
root: Ember.Route.extend({
route: '/',
index: Ember.Route.extend({
route: '/',
connectOutlets: function(router, context) {
var invitesController = router.get('invitesController');
},
anAction: function(router) {
var invitesController = router.get('invitesController');
}
})
})
});​
You can access any controller instance by name using lookup method of Application instance.
To get Application instance you can use getOwner from any route or controller.
const controllerName = 'invites';
Ember.getOwner(this).lookup(`controller:${controllerName}`));
Works for me in Ember 2.4 - 3.4.

Who is instantiating my objects for me? :s

I'm experimenting with routing in ember at the moment, and have a working example. The problem is, I'm a bit confused WHY it works. Currently this route just has 2 simple views. Here is the code:
App = Em.Application.create();
App.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
redirectsTo: 'home' //when hitting the base URL, redirect to home
}),
home: Ember.Route.extend({
route: '/home',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('home');
}
}),
about: Ember.Route.extend({
route: '/about',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('about');
}
})
})
});
//Main controller + view
App.ApplicationController = Ember.Controller.extend({});
App.ApplicationView = Ember.View.extend({
templateName: 'application',
goHome: function(){
App.router.transitionTo('home');
},
goAbout: function(){
App.router.transitionTo('about');
}
});
// Home page
App.HomeView = Ember.View.extend({
templateName: 'home'
})
// About page
App.AboutController = Ember.Controller.extend({
numWidgets: 45
})
App.AboutView = Ember.View.extend({
numWidgetsBinding: 'App.aboutController.numWidgets',
templateName: 'about'
})
App.initialize();
In my HTML I just have a couple of really simple templates with the names "application", "home" and "about".
So, it all works, and looks very similar to all the examples floating about on the net. Great! But I'm confused about how it seems I have several things instantiated for me, without me asking to do it. Is this correct?
For example:
How is it creating an instance of ApplicationController?
In the connectOutlets functions, it's looking for a controller called "applicationController". I never created anything called "applicationController" (with lower-case "a"), I just extended a controller and called it "ApplicationController" (with a capital "A"). Why does this work?
How is it creating an instance of AboutController?
I did a simple test binding between the "about" page view and controller. In the view, I am binding with the variable 'App.aboutController.numWidgets'. I never called App.AboutController.create(). So how is there an instance of this ready for me to talk to? Again, it has a lower case letter ("aboutController"). All I ever did was extend a controller (and named it with a capital letter - "AboutController")
A little explanation would be great, as like any normal developer, I feel that using code where you dont know why it's working is crazy!
App.initialize(); does all the instantiation and injection stuff :), based on strong naming conventions: Ember naming / capitalization convention. When you call xxxController.connectOutlet(options), the option has is also conventional, see Confusion about naming conventions in emberjs
Hope that helps.
EDIT: With the latest master, you don't have to call App.initialize() manually. The application is auto-initialized when all is ready :)

You cannot use the same root element (body) multiple times in an Ember.Application

i'm getting error with ember 0.9.8.1
You cannot use the same root element (body) multiple times in an Ember.Application
any idea what this is happening? some suggestions on where i should look into?
thanks.
You cannot bind several Ember application to the same DOM element, as it will conflict for DOM maintenance.
You nevertheless can instanciate several Ember applications in the same page. Try something like that:
App1 = Ember.Application.create({
rootElement: '#app1'
});
App1.ApplicationController = Ember.Controller.extend();
App1.ApplicationView = Ember.View.extend({
templateName: 'app1-view'
})
App1.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
path: '/'
})
})
});
App2 = Ember.Application.create({
rootElement: '#app2'
});
App2.ApplicationController = Ember.Controller.extend();
App2.ApplicationView = Ember.View.extend({
templateName: 'app2-view'
})
App2.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
path: '/'
})
})
});
Here, we explicitly set the DOM element to which the app will bind, using rootElement property.
By default, an Ember app binds to body, so if you have twice, they conflict...
Example # http://jsfiddle.net/MikeAski/FMV8u/13/