Ember routing design. Desktop like routes - javascript

So, I want to build an app that would have the same effect as a desktop, so I have a load of routes and I want them to open in windows "(modals)", I'm not even sure if this kind of design is possible with EmberJS, but the only way I can think of is to have the URL ending to look like
app.com/#/skype/files/chrome
This ending up having the 3 windows open (skype, files & chrome)
I would love some suggestions on this kind of design.
Thanks

Honestly there is a large problem here, and that's the router is more like a stack than a list. You can't insert/remove arbitrary routes from the current path. You always push/pop from the end.
IE If you were to switch from app.com/#/skype/files/chrome to app.com/#/files/chrome you'd really be popping the three routes then pushing the two new ones which would destroy all of your state, then create it new.

This is certainly feasible with the routing scheme you are suggesting. With this design, I would suggest the following routing scheme:
App.Router.map(function() {
this.resource('firstModal', { path: '/:modal' }, function() {
this.resource('secondModal', { path: '/:modal' }, function() {
this.resource('thirdModal', { path: '/:modal' }, function() {
})
})
})
});
With the scheme, navigating to modal1.index would show only one modal, navigating to modal1.modal2.index would show two modals, and so on.
You can then define the templates for each modal content and based on this Ember.Component, display the corresponding modal. For example:
App.FirstModalRoute = Ember.Route.extend({
model: function(params) {
return params.modal;
},
renderTemplate: function() {
var modalName = this.modelFor('firstModal');
this.send('openModal', modalName);
}
deactivate: function() {
// Remove the modal on exit route
this.send('closeModal');
}
});

Related

Backbone.js route and scroll position

I'm creating a really basic website with a list of projects of which you can click and then view more details about that project.
The problem I'm having is that when you scroll down the list of projects, then go to the project detail view, it doesn't reset the scroll position to the top. So you are at the bottom of the new page, which is annoying.
I'm quite new to Backbone.js – has anyone come across this before?
i found the same problem on my projects (with backbone). The solution is used is that :
App.Router.on("route", function(data){
$(document).scrollTop(0);
});
So, everytime routing is called, before render the new view, the document page return automatic on top. I hope can help.
Remember you need "bind" this on 1 time, not each view ;)
EDIT:
if the routing is not "triggered" when you create the new page, apply this line
$(document).scrollTop(0);
on the correct event :)
EDIT2 :
Watching your code (final part)
var projects = new App.Views.Projects();
var project = new App.Views.Project();
var theRouter = Backbone.Router.extend({
routes: {
'': 'home',
':id': 'project'
},
home: function() {
projects.render();
},
project: function(id) {
project.render({id: id});
}
});
var router = new theRouter();
router.on('route', function() {
// MAKE SURE ROUTING BIND IS CALLED
console.log("Hey, im routing !!!");
$(document).scrollTop(0);
});
Backbone.history.start();
Try this

Smart rendering subviews in Backbone

tl;dr: When moving from page to page, change/destroy only these blocks that need it (not re-rendering whole page). And keep application router as simple as possible.
I'm new to backbone and all of the examples to get started with it that I've seen were about small apps with a single page that changes a little sometimes (adding/removing some elements, but never completely re-rendering). But when I started doing my app which is a little bit more complex, I faced the problem of subviews organization...
Problem: When every page of application consists of subviews (each subview can have another subviews, ie nested subviews), which are responsible for displaying their blocks in the page, a reasonable desire would be not to re-render blocks that not changing when you're navigating through pages. Sometimes you need to re-render whole page, when it's completely different from previous, sometimes just some blocks on the page. But in this case application router becomes monster-object that contains too much logic, so that it's hard to maintain.
What I want: I want my router to be like
define(['jquery', 'backbone'], function ($, Backbone) {
return Backbone.Router.extend({
routes: {
"": "home",
..........
},
home: function () {
require(['views/home'], function (View) {
var view = new View({ el: $("body") });
view.render();
});
},
..........
});
});
So, my goal is to move logic for rendering subviews to views. So that view will decide what to do: render itself including subviews or just ask subviews to decide same question for themselves.
Possible solution: Thinking about it I come with idea of global object that contains tree of views as application state (e.g. first_design(home(about, login)) ). And when we're moving to next page, that has the same main view (ie first_design) we don't render first_design view, but just it's subviews (in this case only home). And for every page in router we now need to manually define this tree of views. For example like this:
define(['jquery', 'backbone'], function ($, Backbone) {
return Backbone.Router.extend({
routes: {
"": "home",
"contacts": "contacts",
..........
},
home: function () {
require(['views/firstDesign', 'views/home', 'views/about', 'views/login'], function (FirstDesign, Home, About, Login) {
new FirstDesign({ el: $("body"), subviews: { new Home({ subviews: { new About(), new Login() } }) } });
});
},
contacts: function () {
require(['views/firstDesign', 'views/contacts'], function (FirstDesign, Contacts) {
new FirstDesign({ el: $("body"), subviews: { new Contacts() } });
});
},
..........
});
});
So, the question is...: I believe i'm reinventing the wheel right now and obviously my wheel is not good enough (possibly even not round :D). So are there any implementations of what I need? Or if not, then how I should do it properly? Thanks!
P.S: I'm using backbone.js with require.js. And sorry for my English, it's not my native language...
There is a Backbone plugin called LayoutManager. From their wiki page:
LayoutManager provides a logical foundation for assembling layouts and
views within Backbone.
I use it and it serves me well.
https://github.com/tbranyen/backbone.layoutmanager/wiki
I realized that my solution is not so flexible. And after all I decided to use Marionette. What really helped me to solve my problem is this question. p.s: Marionette has now a hasView method in Region class.

Using template and controller multiple times on different routes in Ember

In my Ember application I have a categories controller and corresponding template.
I want to use this on various routes: When browsing, when advanced searching and when adding a product.
I tried to put it together as this:
#router
this.resource("categories", { path: "/*scope/select-category" }, function() {
} );
Scope could be 'search', 'products' or 'products/new'. It would make URLs very pretty!
But I'm having difficulties finding back the *scope value from my controller, I found it somewhere nested deeply, but with 'products/new' as the scope the value is 'products'.
Also I already experienced some strange behavior, for example, on a fresh page load for 'products/select-category' it wants to go to 'products.product', trying to find the product with ID='select-category'. Which is of course not what I want.
The categories route is the first route in the list so I assumed it would always pick that first..?
I'm doubting if this is the right way to do this.
You probably should do it this way, by handling the scope on a separate route:
#router
this.resource("scope", { path: "/scope/:scope" }, function() {
this.route("categories", { path: "/select-category" }
} );
This way you can handle scope nicely in the ScopeRoute, with a proper scope parameter.

emberjs using multiple paths / urls for one route

In Ember I can use this:
App.Router.map(function() {
this.route('accomodations');
});
so if one goes to /accomodations it will load that view.
I can also add:
App.Router.map(function() {
this.route('accomodations', { path: '/travel' });
});
so if one goes to /travel, it will go to the same view.
I want to be able to have /accomodations and /travel go to the same view? is this possible?
I know that this:
App.Router.map(function() {
this.route('accomodations');
this.route('accomodations', { path: '/travel' });
});
Will do what I'm asking, but if they go to accommodations, it should show that in the url, it always shows travel. I'm not even sure if the final piece of code is best practice.
You can simply interchange the two route-path definition lines:
App.Router.map(function() {
this.route('accomodations', { path: '/travel' });
this.route('accomodations');
});
The last definition takes the precedence for URL display in {{link-to ...'accomodations'}} and Route#transitionTo('accomodations') in-app transitions, though entering the app by '/travel' will leave the URL as is.
(EmberJS 1.11.3, 2.12.2)
Using redirection
In router.js
App.Router.map(function() {
this.route('accomodations');
this.route('travel');
});
In routes/travel.js
App.TravelRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('accomodations');
},
});
You do not have to put these in separate files (that's just where I would put them).
What this does is register two routes with the router. Pick one of them to be the "main" route, and the other the "alias" route. In this case, accomodation is the main route, and travel is its alias. When the user visits /travel, they get redirected to /accomodation.
This would be the default/ standard Ember way of accomplishing this, and if this sounds good to you, go for this.
Another possible solution
If you do not wish to have redirection happen, for some reason, and want the URL seen by the user to stay the same, but still display the same things, and behave in the same way, this is also possible.
In this case, you would create two of every single Ember unit (route, controller, view, template). The smart way would be to create a base class route, and have both App.TravelRoute and App.AccomodationRoute trivially extend it; create a base class controller, and have both App.TravelController and App.AccomodationController trivially extend it; same for views, if you have them.
Templates, OTOH, are a little trickier, because there is not way to extend them (that I know of). SO what you would need to do, is create either a partial or a component (decided which works better for you), and then reuse that partial/ component in both templates/accomodation.hbs and templates/travel.hbs

Multiple pages with Backbone.js

I am using the Backbone Boilerplate https://github.com/tbranyen/backbone-boilerplate and don't know what's the best way to handle more than one page. I cannot find answer that helps me understand easily. Basically, I am thinking of those options:
Should each page has a different config.js? Like config-userpage.js, config-homepage.js...?
Should I have different router.js for different page instead? Like router-userpage.js or router-homepage.js,...?
Should I just try a different boilerplate like https://github.com/hbarroso/backbone-boilerplate?
You can definitely try a different boilerplate, but I'm not sure that will
help. Multiple pages can be achieved in many different ways.
A good reference example for the Backbone Boilerplate is:
http://githubviewer.org/. I have released the entire thing as open source and
you can View how basic pages are added there.
You may want to get creative and make a Page model that handles what page
you're on and inside of each route set the new page title and which layouts to
use.
A very basic, proof-of-concept, implementation inside of app/router.js might
look something like this:
define([
// Application.
"app",
// Create modules to break out Views used in your pages. An example here
// might be auth.
"modules/auth"
],
function(app, Auth) {
// Make something more applicable to your needs.
var DefaultPageView = Backbone.View.extend({
template: _.template("No page content")
});
// Create a Model to represent and facilitate Page transitions.
var Page = Backbone.Model.extend({
defaults: function() {
return {
// Default title to use.
title: "Unset Page",
// The default View could be a no content found page or something?
view: new DefaultPageView();
};
},
setTitle: function() {
document.title = this.escape("title");
},
setView: function() {
this.layout.setView(".content", this.get("view")).render();
},
initialize: function() {
// Create a layout. For this example there is an element with a
// `content` class that all page Views are inserted into.
this.layout = app.useLayout("my-layout").render();
// Wait for title and view changes and update automatically.
this.on({
"change:title": this.setTitle,
"change:view": this.setView
}, this);
// Set the initial title.
this.setTitle();
// Set the initial default View.
this.setView();
}
});
// Defining the application router, you can attach sub routers here.
var Router = Backbone.Router.extend({
routes: {
"": "index"
},
index: function() {
// Set the login page as the default for example...
this.page.set({
title: "My Login Screen!",
// Put the login page into the layout.
view: new Auth.Views.Login()
});
},
initialize: function() {
// Create a blank new Page.
this.page = new Page();
}
});
return Router;
});
As you can see, this is an opinionated way of creating "pages" and I'm sure
other's have better implementations. At Matchbox, I have a very robust Page
model that does breadcrumbs and figures out which navigation buttons to
highlight based on the state. You can also create Routers inside your modules
to encapsulate functionality and expose the Page model on the app object so
that it's available throughout your application.
Hope this helps!

Categories

Resources