In a single page application, is there a way of switching back and forth to an AngularJS route and to display it back in the same state as it was shown before?
Usually this would be implemented by binding data in a parent scope. While this is easy to set up for lightweight view, it can be cumbersome when doing it for views holding lots of graphical elements.
Here is an example, where having the previous route state remembered could enhance user experience: on the following page, imagine that
you stand on Item 1 and select Tab 2
then move to Item 2
finally switch back to Item 1: Tab 2 is not selected anymore :-(
http://angular-route-segment.com/src/example/#/section1/1
It seems the views are destroyed/constructed when switching back and forth between routes.
A solution would be about storing the state of the user interface in a parent scope but it has the following pitfalls:
creating an object storing all the little details of the user interface
creating complex logic about -saving and- resetting the UI in the same state as before
storing UI state in a data model does not sound that MVC-ish
Using show/hide of div storing the views saves the state but then no route is used and the switching business must be implemented by hand. I like using routes because 1. of the browser history navigation (hash in the url) and 2. it is easy to set up.
Having the UI state not remembered is like having Chrome to reload pages when switching back and forth between tabs: not very user friendly.
Is there an Angular-way?
Your $routeSegment approach is very interesting. The $routeSegment service could plug into the $routeChangeStart event in order to
Somehow keep a "sub path history" on all paths seen so far, maybe only for those explicitly configured to keep their UI state. In your example for the path "/section1/1" the stored sub path would be "/Y" if tab 2 was selected. Things get interesting, as also dynamic paths with $routeParams might need to be covered.
Use this history for redirecting by using $location.path in the event handler. So a $routeChangeStart event with next.originalPath being "/section1/1" might be redirected to "/section/1/Y"
Related
I'm working on a project using react with flux architecture.
I have several views and a store called ContextStore in which I save the current view in the state, so that when an action to change the view is triggered this store change his state and the Main view listen and change it.
But the problem is that always when I refresh the page with F5 it always goes to the initial Views.
What is more, if I press the back button the View has no change.
I think that my problem is because when I refresh the Main view loads again the initial state. How could I solve it?
Thanks!
You might want to look into using a router to manage your views. Here's the one I use: https://github.com/rackt/react-router. When you transition to different views (routes) it adds to the history stack so you can use the back button. However, I don't have the use case where you need to stay on the current view when you refresh, so you might need to use cookies or session storage in those instances in any case.
You are right, when hitting F5 you are completely reseting all javascript, ergo the store looses its state. You need to use some kind of storage for saving the state. Classical ways are cookies, or you could use IndexedDB which is included into HTML5
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 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 am working on an ember.js (version 1.2) based mobile application and I am trying to find the most idiomatic way of implementing the global menu toggle/back button pattern that is common in many mobile apps. Specifically, its a button that sits on the left side of a fixed-top toolbar, which toggles a hidden drawer main menu when the user is at the app's home/index view, however upon moving into a sub route, the button displays a back arrow, and when clicked, it takes the user back to the previously viewed route (or back to the index route if there is no previous history states, i.e. if the user came into a sub route directly upon loading the app).
Fyi, currently I have my app structured with the fixed-top toolbar and menu toggle/back button in the root application template. Ideally this functionality would work no matter how a routes are being transitioned to, whether via transitionTo(), or {{#link-to}} helpers, ect.
So essentially I want to know if Ember internally maintains any sort of accessible history/log of what routes were transitioned to over the course of the app's lifetime, and also what would be the best way to conditionally change the action that the toggle/back button performs and its display (aka its icon) depending on the current route. And/or is there a way to listen to ember's route change events so I could implement that history myself if need be?
I hate to be the bearer of bad news, but I also hate to leave you hanging.
Ember doesn't keep track of the history, there isn't a general use case for it (especially since the browser keeps track of it for you).
Fortunately you can monitor route changes in the application controller, something like this should get you started (Note I didn't spend time working out an awesome solution that works perfectly, just showed you the basics you need for it, I'll let you figure out the workflow that works well for you)
http://emberjs.jsbin.com/IZAZemEP/1/edit
App.ApplicationController = Em.Controller.extend({
history: [],
hasHistory: function(){
return this.get('history.length')>1;
}.property('history.length'),
watchHistory: function(){
this.get('history').pushObject(this.get('currentPath'));
}.observes('currentPath'),
actions: {
goBack: function(){
// implement your own history popping that actually works ;)
if(this.get('hasHistory')){
this.get('history').popObject();
window.history.back();
this.get('history').popObject(); // get rid of route change here, don't need it
}
}
}
});
Iām new to Knockout JS. Should I use sammy.js to help me with the below?
I am building a Knockout JS App (single page) where as I move from state to state, exactly what sub panels are visible and what they do changes. For example:
pre-login, I have one big panel that invites the user to login.
Right after login, I have a bunch of tabs, a main area for searching and an advertisement at the bottom.
If the user does a search, then I end up in a state where we see tabs, a search area and a search results area.
I imagine doing this via having a state machine that moves from state to state as the user clicks, sort of the way a parser would move from state to state.
I imagine delegating to the current state, an object hanging off of the ViewModel (perhaps) so that my 20 or so different states are almost like 20 very simple different UIs each one separate from the others and simple and clean and easy to understand.
1 - How would I do this, have the UI delegate a sub UI to a sub object hanging off the ViewModel?
2 ā Is there a better way to keep my UI simple even though it has lots of states and different states often look very different from one another in terms of what elements (sub panels) are visible?
3 ā Does anyone have an example were the code repeatedly delegates to a sub user interface?
Am I making sense?
A framework that would probably fit very well is machina.js - should be just what you are looking for.
Here is a presentation on it from jQuery UK - stateful machines
I'm using a scheme where I simply have two separate state observables - one for the total application state and one for the current "page" (tab, or whatever a "page means to you). In coffeescript, as an example:
#currentState('logged-in')
#currentPage('page-foo')
#applicationState = ko.computed => "#{#currentState()}:#{#currentPage()}"
then in your knockout visible bindings, you can match on a specific state or substate:
<div data-bind='visible: applicationState() == "logged-in:page-foo">
or you can do partial matchs:
<div data-bind='visible: applicationState().endsWith("page-foo")'>