I'm thinking about the optimal way to structure my Backbone application. The problem is that I have various complex states, each made by some views showing while all the others are hidden.
What is the canonical way to handle this in Backbone? Two things that I've thought are either controlling the state by the router (calling views hide / show methods) or making the views listen for route event.
The problem with the first method is that the router must be aware of all the views existing in the application.
The problem with this second solution is that I have to make all the views listen to all the events and hide for any of them but a couple that make them show.
Thanks for pointing me to a lean solution.
I use a FSM machine to change the state of the application. Each states shows and hides the appropriate view. My views use transition to animate in and out, so changing the state is more complex, then simple show/hide - it animates in and out from one state into another. I have forked https://github.com/fschaefer/Stately.js to fit my needs.
I can share my personal experience with such a problem. I don't know if it's the best solution, but it worked for me.
My problem was even worse because I had several routers and each of them should hide/show views that belong to it. The solution I chose was similar to the first option you consider.
In my router there is an array which holds all existing views. When the state changes and route callback executes all other views are hidden with this simple code view[i].hide() and the proper one is shown. You can make View model and Views collection if you would like to have more control.
I think it's a better solution, because when you add a new route, you don't have to add route events to all views. Moreover, your views stay decoupled from the router, they may even don't know it exists.
Related
So I'm trying to make a single page web application that makes use of AngularJS's routing mechanism and so far it's working brilliantly, but I noticed that when the route changes, the view seems to be completely deleted and replaced with the new one.
Is there any way to keep the old view around so that it doesn't have to re-download all of its data when the user goes back to it? Also it would be nice so that their scroll position would be retained.
Any ideas?
Using UI-Router I solved this problem with the angular ui router extras. Here you will find the sticky states that will perhaps do what you want. Quote from the website:
Sticky States allows a tree of states to be long-lived, running
concurrently alongside one or more other state trees. This is also
sometimes referred to as Parallel States. The basis for this
functionality is to implement a "tabs" use case. Using UI-Router
Extras Sticky States, a single page angular app can implement
independent tab state trees, which operate in parallel to each other.
Does that help?
I have a parent view with a nested view in the middle.
On a state change, the nested view seems to stick for a second or two before loading the next state. It's as though the nested view is lagging behind or something.
For example, after logging in, the login form is still visible for a second or two in the middle of the page after the state change. The parent view changes instantly, but that nested view just seems to stick.
I've been pretty careful about items on the watch list, and use one-time binding wherever possible.
But I really don't think it has to do with that, because this happens even early on in the application (from login to the main page), and other than this issue, application performance is fine.
I've googled a lot about this, but haven't turned up anything useful.
Any ideas on what to check or how to debug this?
You say it only happens the first time you transition after loading the app. So it could be you are injecting a service into the child view that you are using the first time in your app. This service is taking some time to instanciante. Servises are singletons, so this lag is only visible the first time.
Look at the answer in this thread for a possible solution, somebody had the exact some problem:
How to instantiate a service dynamically?.
Another solution might me to inject that service into the parent view as well, so you get the lag while loading the app not on first transition.
In the application we are developing, we have a CollectionView whose every ItemView contains a link to the item-details page. Also, every ItemView contains a checkbox, because items can be selected in the CollectionView to perform bulk actions on them.
When switching to the ItemDetails view, we want to keep the state of the CollectionView, ideally without having to redraw it (a bit like GMail when switching from inbox to mail and back). Our solution is to render the two views in two different regions and to hide one when switching from one to the other.
My perplexity about this solution is that
Marionette doesn't seem to be meant for this kind of use.
It is not very memory-friendly, since all the DOM elements are never deleted.
Is there any better solution to achieve this goal?
Storing the state somewhere, close the CollectionView and redraw it later is another possible solution, but would it imply a heavy computation overhead? (we are quite scared about redrawing views).
Marionette allows for showing a view in a region without closing the view that was already rendered. You simply pass in {preventClose: true} to the show() method of the region. You would still need to maintain a reference to the collection view though so you can later re-show it or close it yourself.
https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.region.md#showing-a-view
Unless your collection is very large, there's not a problem with just rerendering the collectionView when switching back from a itemDetail view. You do need to store the state of the checkboxes indeed.
However, I don't see what's really wrong with your other approach. It's probably even faster and there's nothing wrong with just hiding one region and showing another. If that works for you, go ahead.
Regarding the memory issue, as long as you're looking at the collection or itemDetail there isn't much to gain by closing either one of the views (especially if your itemDetail views are not very large). Once you move away from that section (thus not looking at the collection or itemDetail view anymore) you could just close the layout that contains these two regions. That'll free up any memory used by these regions.
I need to use two states in parallel, one for my page and an other for a modal with several sub states.
Right now calling the modal state will wipe out my page since the page state changed.
Create a child state child of my page wouldn't be a solution since the modal will be used on several pages.
Example:
$stateProvider
.state('user', {}) // page
.state('bookshelf', {}) // page
.state('books', {}) // modal
.state('books.read', {}) // sub state of modal
So if I'm on user and open my modal then the state would change to books, my modal would then have the content but the page content will be wiped out.
How do I fix it?
I believe the way you're looking to do this is not possible with UI.Router currently. What you're describing is a modal component (which would ideally be written as a directive), which tracks it's state independently from the main state.
The way to think about it, is that UI.Router works by creating a state tree. At any given time you can only be looking at one branch of the tree. You can go deeper down a branch (ie: book, book.open, book.open.checked), but you can't be in two places at once.
Another issue with the problem above is how do you serialize the state of the two different trees into one url? It's not to say it can't be done, it's just a hard problem to solve.
Checkout these issues:
https://github.com/angular-ui/ui-router/issues/119
https://github.com/angular-ui/ui-router/issues/384
https://github.com/angular-ui/ui-router/issues/475
Also checkout these repos, they might be further along the lines of solving the problem.
https://github.com/afterglowtech/angular-detour
https://github.com/stu-salsbury/angular-couch-potato
As far as solving your immediate problem, I think the 'easiest' way would be to ditch controlling the state of the modal inside your state config.
Instead, I would add some sort of root or abstract state, and then track whether the modal is open there. Then, you can communicate between controllers using events as shown here. Note: There are performance implications with listening to $rootScope, so be sure to research those. However (someone feel free to correct me), the implementation here doesn't have those problems, because the AppCtrl is never destroyed.
Jan 15, 2015 Edit
Turns out this is a pretty popular use case, and one of the core contributors to UI Router maintains a plugin/addition called UI Router Extras
It also includes utilities for lazy loading, called "Future States" which are very helpful.
That being said, one feature I'm hoping to get time to work on is maintaining all state within the URL (or perhaps, local storage) and allowing for reusable state "components". The latter is in the UI Router roadmap as well.
I'm starting to learn Backbone.js and can't figure out one thing: In a typical rails app, I have a layout view and a nested view. My layout usually contains navigation links that are processed by rails routing.
How do I do the same with Backbone? I'm using Rails 3.2 and eco templates.
Should I create nested templates in eco?
Should my navigation links be plain html links with a href or should the navigation be event driven?
For example I have a list of categories on the left, and a category items on the right. I want my categories to be shown on every view and the corresponding category items (with a URL in browser corresponding to selected category) too.
Please point me to right direction, because most tutorials on the Web are 'todo' style applications with no navigation at all.
Thank you.
UPDATE
Turns out, my question wasn't clear, so let me narrow it down.
How can the concept of RoR layouts be applied to backbone.js applications?
And I'm curious about two possible ways of navigation:
create
%a{:class => "customers", :href => "#customers"} Customers
handle ".customers click" event in my view
Which way is better?
And I'm curious about two possible ways of navigation:
create %a{:class => "customers", :href => "#customers"} Customers
handle ".customers click" event in my view
Which way is better?
neither is better until you know the specific context in which you are working. they are simply options for achieving your goal and desired functionality.
there are times when both should be used, as well. for example, if you are supporting search engine optimizations and accessibility.
Here are some better questions to ask:
Which of these will be the simplest thing that can get the job done? will it be more code to write the route handler, or the click handler?
Will the route handler tightly couple the functionality to a router, when I don't need that?
Do I really need a route for this link, so that people can bookmark it and come back to it directly?
Will a click handler cause my code to jump through several hoops of calling other objects that aren't directly related, or can I use a simple event to cause the other objects to run?
There isn't a single correct answer to any of these questions. I recommend trying many different ideas when you can. Keep in mind that you will likely change your answer as you develop new features. The answer that you want, today, won't be what you need tomorrow. But that shouldn't stop you from picking an answer now and moving on. Pick one, put it in place, and when you need to change it, change it.