I cannot run the code below because router is undefined in my view. However I'm struggling to understand where I actually defined var router = new MyRouter(); my view, controller app start etc?
I'm using marionette and my router seems to automatically match my controllers.
success: function (page) {
id = page.get('id')
router.navigate('page/' + id, {trigger: true});
}
If you didn't declare a router attribute, you don't have one available (it doesn't get created automatically or anything), hence the undefined value.
To navigate to a given page, you can also use Backbone.history.navigate (see for example https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/app.js).
In addition, you might want to reconsider using the trigger: true option, per the reasons explained here (Routing chapter) in the free sample to my book on Marionette.
Related
I have the View containing some button. When that View becomes activated it should take the some URL parameter (in my case -- site id) and set it to the button attribute "data-site_id". There is a router too in the app. But I don't know how to implement it with the best way. Till now I see 3 supposed solutions:
Extract site id from URL hash. URL is build by such a pattern:
"sites/edit/:id(/:tab)": "editSite",
the question is -- may I use here a router itself (and, if yes then how?) or can not, and should parse it with common JS way? Of course, router & view are two different objects and located in different files/scopes.
Save the site_id in model. But I'm not sure how to store it from router. I think I can create an instance of model, set it as variable under router scope and then treat it as usual, something like this:
(function(app, $, config, _) {
var Model = new app.modelName();
var Router = app.Router = Backbone.Router.extend({
routes: {
"": "start",
//....
"sites/edit/:id(/:tab)": "editSite",
//...
},
//....
editSite: function(id, tab){
Model.set('site_id', id);
}
//....
})(window.Application, jQuery, window.chatConfig, _);
after that I can extract the site id from model in any time I want
Assign a data-site_id attribute to the button just from the router. But this doesn't look as a best practice, right?
So what is your advice?
Your second suggested solution makes the most sense, you could then instantiate your view passing that model to the constructor.
See http://backbonejs.org/#View-constructor
Is there a way to selectively tell Durandal to reinitialize a view model. I am aware of the singleton vs new instance approaches to initialize view models.
//singleton since a declared object is returned
define(function() {
return { prop1: 1, prop2: 2 }
});
//new instance since a constructor is returned
define(function() {
var ctor = function(){};
return ctor;
});
I generally don't like to declare view models as singletons, but I have to do it in a special case due to sub routing which requires me to pass data from a parent router to my child router. However, the singleton has other side effects, so I was wondering: Is there a way to selectively request a new instance of the view model even if it was initially declared as a singleton?
Not that I know of - this is more of a limitation of requirejs versus Durandal, though. Once require has loaded the module, so far as I know it will always return you the same version of that module. Unless there's a way to tell require to reload the module?
The only thing I could think of would be to "reset" the view model during the activate method. If you're changing routes and finding that the activate method isn't being called, it may be because Durandal thinks that your module is already active (in which case it won't reactivate). You can change this behavior by customising the areSameItem function for the router (see this question for an explanation).
Hope that helps.
I don't know if it helps in your case, but you could use the activate() method in your view model. For more information see http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks.
If I navigate to a view by clicking on a link such as 127.0.0.1/#/project/1, the correct view gets displayed. However, if I call this url directly in the browser (or hit refresh), the view won't be displayed. What could be the reason for this behaviour?
The way I set up the Router is as follows:
var AppRouter = Backbone.Router.extend({
routes: { },
initialize:function () { }
});
var app = new AppRouter();
and then in every module (I'm using require.js), a route and handler will be added
app.route("project/:id", "showProject");
Could it be that the routes aren't registered yet and thus the callbacks won't be called?
Make sure that you are calling Backbone.history.start() after all of your routers are loaded/instantiated and routes defined: http://backbonejs.org/#History-start
Alternatively, you could stop the history with Backbone.history.stop(), and start it again. Then the added route(s) will be picked up.
BTW, you can test if the history is currently started with the boolean Backbone.History.started (note the capital 'H' is necessary).
I have an action:
{{action create target="controller"}}
which I have targeted to the bound controller (rather than the router) like this:
App.AddBoardController = Ember.Controller.extend
create: ->
App.store.createRecord App.Board, {title: #get "boardName"}
App.store.commit()
//TODO: Redirect to route
How do I redirect back to a route from the controller action?
Use transitionToRoute('route') to redirect inside an Ember controller action:
App.AddBoardController = Ember.Controller.extend({
create: function(){
...
//TODO: Redirect to route
this.transitionToRoute('route_name');
}
...
In fact, this is not Ember idiomatic. From what I know, and what I have learnt from Tom Dale himself, here are some remarks about that code:
First, you should not transitionTo from elsewhere than inside the router: by doing so, you are exposing yourself to serious issues as you don't know in which state is the router, so to keep stuff running, you will quickly have to degrade your design, and by the way the overall quality of you code, and finally the stability of your app,
Second, the action content you are showing should be located inside the router to avoid undesired context execution. The router is indeed a way to enforce a coherent behavior for the whole app, with actions being processed only in certain states. While you are putting the actions implementation into Controllers, those actions can be called at anytime, any including wrong...
Finally, Ember's controllers are not aimed to contain behavior as they rather are value-added wrappers, holding mainly computed properties. If you nevertheless want to factorize primitives, maybe the model can be a good place, or a third party context, but certainly not the Controller.
You should definitely put the action inside the router, and transitionTo accordingly.
Hope this will help.
UPDATE
First example (close to your sample)
In the appropriated route:
saveAndReturnSomewhere: function (router, event) {
var store = router.get('store'),
boardName = event.context; // you pass the (data|data container) here. In the view: {{action saveAndReturnSomewhere context="..."}}
store.createRecord(App.Board, {
title: boardName
});
store.commit();
router.transitionTo('somewhere');
}
Refactored example
I would recommend having the following routes:
show: displays an existing item,
edit: proposes to input item's fields
Into the enclosing route, following event handlers:
createItem: create a new record and transitionTo edit route, e.g
editItem: transitionTo edit route
Into the edit route, following event handlers:
saveItem: which will commit store and transitionTo show route, e.g
EDIT: Keep reading, Mike's answer discusses some of the problems with this approach.
You can just call transitionTo directly on the router. If you are using defaults this looks like App.router.transitionTo('route', context).
I think I'm missing some basics about Backbone's routing functions.
I'm building an app and it looks something like so:
file: app.js
App = {}
App.nav = new Backbone.Router;
require('app/controller');
file: controller.js
App.nav.route('home', 'home', function () {
console.log("Home Activated");
});
App.navigate('home');
At this point the browser changes the URL in the address bar to /home but nothing happens and I don't get the Home Activated console message.
I've tried using my own routing class (i.e. Backbone.Router.extend({})) but I don't really see a point in it as I still need to initialize it, and I want to use a central history/navigation in my app that all modules/controllers add routing to it rather than creating a router for every controller.
What am I doing wrong?
http://documentcloud.github.com/backbone/#Router-navigate
From the documentation:
If you wish to also call the route function, set the trigger option to true.
But as OlliM wrote, you need to activate the history first!
So your answer should be:
Backbone.history.start();
App.nav.navigate('home', {trigger: true});
edit:
forgot to put "nav"
For the routing to work, you need to call Backbone.history.start() after setting up your routes (basically after you've done everything else). See: http://documentcloud.github.com/backbone/#History-start
I just want to point this out as it saved me a world of hurt and heartache.
If you are routing to a custom page such as
Backbone.router.navigate('/some/page'); // does not work
And it appears to not be working. Add a trailing '/'
Backbone.router.navigate('/some/page/'); // works
This cost me a few hours of troubleshooting...