Is it possible to define a catch-all route or an error route in Sammy.js? I know I can bind to 'error' but if no route matches that does not seem to be triggered.
Thanks!
You should override the notFound function.
Like this:
var app = $.sammy('#app', function() {
this.notFound = function(){
// do something
}
});
This is recommended by the author of Sammy.
According to the documentation for Sammy routes,
Paths can be defined as strings or
regular expressions.
As such, it should be possible to create a route like this, at the end of your routes, that is a catch-all:
get(/.*/, function() {
...
});
Related
I am wondering the appropriate way to access a route model from a different non nested route controller.
If I have my routes set up like this: (this works however, not sure if its proper)
App.Router.map(function() {
this.route('admin');
this.route('page1');
}
And the Page 1 route has a model like this:
App.page1Model = {content:'Content of simple model'};
App.Page1Route = Ember.Route.extend({
model(){
return App.page1Model;
});
Then the admin controller wants to access the page1 route, I can only do it like this:
App.AdminController = Ember.Controller.extend({
page1Model:App.page1Model,
Now do stuff with page1Model.....
});
Ive tried to use Ember.inject.controller() however that only works for me when my routes are nested and I want to access Parent controller from child. Is there a way to use that syntax to get what I want, or is there a better way than what im doing?
Thanks
There's an inherent problem with what you're asking for: when the user is on the admin page, they're not on the page1 page, so there's no page1 context. Some questions you might want to ask:
what happens if the user goes to /admin having never gone to /page1?
what happens if the user goes to /page1 then /page2 then /admin?
I can think of two Ember-esque ways of doing what you want:
A Page1ModelService. Here, you create an Ember.Service that holds an instance of Page1Model. You inject the service into route:page1 and route:admin and let them each pull off the instance. Whether they can change which instance of the model is showing is up to you.
Return a Page1Model instance in the model hook for route:application. This route sits above both route:page1 and route:admin, so they can both look up the model as follows:
// route:application
model() {
return App.Page1Model.create();
}
// route:page1
model() {
return this.modelFor('application');
}
I was able to achieve my goal through using registers and injection. Can someone please take a look and let me know if this is 'proper' through Ember standards or if there is a better way ( #James A. Rosen :) )?
OH! If there is a better way to attach the model to the page1 route, please let me know. This worked though I am not sure if i like the .model after create().
JSBIN: http://jsbin.com/tikezoyube/1/edit?html,js,output
JS of that:
var App = Ember.Application.create();
var page1Model = {title:'Old Title'};
var page1ModelFactory = Ember.Object.extend({
model : page1Model
});
App.Router.map(function(){
this.route('page1');
this.route('admin');
});
App.register('model:page1', page1ModelFactory);
App.inject('controller:admin','page1Model','model:page1');
App.Page1Route = Ember.Route.extend({
model(){ return page1ModelFactory.create().model; }
});
App.AdminController = Ember.Controller.extend({
actions:{
updateTitle:function(){
console.log(this.get('page1Model').model.title);
this.get('page1Model').set('model.title','THE NEW TITLE!');
console.log(this.get('page1Model').model.title);
this.transitionToRoute('page1');
}
}
});
Thanks!
How might I go about making the above happen? For instance, if a user navigates to this URL:
app.com/#search/test.com/index.html
with this router set up in a controller to catch it:
var searchRouter = new Marionette.AppRouter({
controller: searchController,
appRoutes: {
'search': 'init',
'search/:term': 'search'
}
});
How would the function search be passed term inclusive of the / and everything that follows? In my current testing, it seems that this route isn't called at all when the search term contains a /.
Routes matching are sometimes tough and can be a pain to debug, I have had a lot of issues with that so far.
So I would like to suggest you something else:
Just have 1 route to cover both scenarios and route to init or search inside your listener:
appRoutes: {
'search(/:term)': 'searchListener'
},
searchListener: function(term) {
if (!term) this.init();
else this.search(term);
}
This is more just to express the idea, feel free to make it better. I hope this approach solves your problem.
In Backbone, is there any way to trigger a route event handler, without changing the URL?
What I mean is that I want to trigger a route handler, but I don't want to change the URL.
Hence, I don't want to use
router.navigate(route, {trigger: true});
as this will cause the URL to change.
The router itself is connected to a function. The simple answer is to call the function straight away, simply bypassing the route handling.
Example
(function( $, Backbone ) {
var exports = window.app = window.app || {},
Router = Backbone.Router.extend({
// Here you declare what the routes are in your router
// and what functionality they should trigger.
routes: {
"help" : "help",
"search/:query" : "search",
"search/:query/p:page": "search"
},
// Declare route functions.
help : function() {},
search: function( query, page ) {}
});
// Export the router.
exports.router = new Router();
// Just a dummy object for calling the router.
var cookieMonster = {
init: function() {
// Do something on init.
// End with calling the route help function.
exports.router.help();
}
};
}(jQuery, Backbone));
cookieMonster.init() would in this case end with a call to the help function in the router.
A tip is to look at Backbone Marionette where you have a Controller which has the function logic seperated from the routes, one of many things that make Marionette awesome.
For what its worth, Marionette routing is explained extensively here: http://samples.leanpub.com/marionette-gentle-introduction-sample.pdf
The strategy that is discussed is separating URL management from application reactions (e.g. switching sub-applications). This means that you're then free to have your app trigger a handler (using a Marionette event) without modifying the URl fragment.
Have you tried Backbone.history.loadUrl(route);?
I want to have a kind of RESTful URL structure like below:
/accounts
/accounts/account/123
I've set up my routes as such:
MyRouter = Backbone.Router.extend({
routes : {
'/accounts': 'accounts',
'/accounts/account/:account': 'account',
},
accounts: function() {
console.log('accounts CALLED');
},
account: function() {
console.log('account CALLED');
},
});
The problem, is when I go to /accounts/account/123 , both accounts() and account() get called (as the URL matches both routes). I tried a route such as /accounts$, but it doesn't look like it's supported in the routes hash.
Is there a way to accomplish this? Would a manual router.route(route, name, callback) work instead (although I really prefer not to do that).
I got this cleared up by another SO question. I didn't realise that I had to use the router.navigate function strictly.
Using the router programmatically (not via browser bar), those duplicate calls go away. I'm also seeing the expected functions called only once... when using router.navigate.
I still have to find out how to capture back & forward buttons to call those functions. Thanks for the feedback so far.
Try /accounts$ instead. $ is the regex for the end of the string.
I have a simple implementation of Backbone.js which goes like this -
$(document).ready(function(){
Workspace = Backbone.Router.extend({
routes: {
"/getAcademics": "academics"
},
academics: function(){
alert("ok");
$("#content").append("<div>Academics</div>");
}
});
var myWorkspace = new Workspace;
});
The link in the body is given as -
Academics
Though the router routes to the link #/getAcademics but it does not execute the function academics. Any clues as how to rectify this. Am I missing something else.
Thanks
After you instantiate your router, and before you attempt to use any of its routes, you need to call Backbone.history.start(). This will setup a listener for hash changes and call the correct route functions when needed.
You can find more information here: http://documentcloud.github.com/backbone/#History