How to work with ui.router's $stateChangeStart in angularjs? - angularjs

In my angularjs mobile app, I need the certain requirements. Initially, the app is in the home page. If the user needs to go to the next page in that app, he has to click on that certain content. And it will redirect them to the next page of that particular content. After that, If he wants to go to the previous(back) page, he has to click on the back button.
What I need is, when user is going back to the previous page where he was in, that page is reloading. Instead of reloading that previous page, he should be taken to the same place where he was before. So, how to disable the reload. And I think it can be achieved using 'ui.router' and its $stateChangeStart. For url routing I use '$routeProvider'. So how to achieve this one? Please help me out.
For homepage the url is -
'/myapp/landingpage'
For the next page (when he clicks on the particular content) the url is -
'myapp/content/:contentId'

If you home page is a plain static page - then nothing is required - the template for the home page is already cached in the $templateCache.
Else if your home page has some data that is fetched from some service, I would suggest you to cache the data in your angularjs service layer itself.
You can explore $cacheFactory for more details.
If you do not want to use $cacheFactory directly, you can simply use 'cahce:true' property in yout $http.get request :-
$http.get(url, { cache: true}).success(...);
From the $http docs:-
cache – {boolean|Cache} – If true, a default $http cache will be used
to cache the GET request, otherwise if a cache instance built with
$cacheFactory, this cache will be used for caching.
The idea is not to mess up view/states for caching and move that responsibility to your service layer.

Related

routing in angular js application with ui-router

When developing a web application with angular js, a part of time that developers spend is the time for implementing routing.
When using ui-router in a application, there are two "phases" to consider with regards to routing:
user navigates inside application: when click is made on some button, user is transfered to another state by using $state.go("somestate"). Parameters can be send etc. And url is changed accordingly.
user navigates directly via url.
Lets say application has such route:
/mythings/{thingid}/mysubthings/{mysubthingid}
If user navigates to that url directly by pasting it into browser window, application needs to handle it. My question is what's the best practice to do it?
What I'm thinging is: if looking at url example above what needs to be done when user enters that url in browser:
get {thingid} from url (from $stateParams), then get {mysubthingid} also from $stateParams (probably by using resolve (ui-router feature) when defining state), then inject what was resolved to controller and then make a query to api and get data about "subthing" and present view in ui with that data. So that should work with both "types of navigations": when user clicks and is transfered to state, or when user enter url directly into browser. Is this the right path to go?
And I suppose that any url you go to when you click something in application, you should be able to take that url and just paste it into browser and see the same results without being redirected to anywhere else. If application cannot handle every url in such way, maybe application' architecture needs to be reconsidered?
Thanks
In UI-Router, the main component of the routing is the state. The URL is essentially just an address that points to a specific state of the app. I don't think it's necessarily productive to think of the two ways of navigating as separate; they're just two sides of the same coin. There should be no URL that isn't handled by a state. Any URL that doesn't match a state should be caught by the otherwise definition on the $stateProvider and probably redirect to your home page or a 404 page.
In your example, the thing/:thingId/subthing/:subthingId url should map to a predefined state just like any other state. Let's say that state is main.subthing. The process for loading the data, initiating the controller and rendering the UI should be exactly the same whether you get there by calling $state.go('main.subthing', {thing: 123, subthing: 456}) or ui-sref='main.subthing({thing: 123, subthing: 456})' or you paste myapp.com/thing/123/subthing/456 into the browser. They'll all end up at exactly the same place with exactly the same data by calling the exact same logic (most likely loading thing 123 and subthing 456 in resolves and injecting those into a controller).
You're right that if a url can't be handled by the application, that's a sign that something is wrong. Like I said, bad urls should be handled by defining otherwise when setting up states. But pasting a URL into a browser shouldn't require any extra work if your states are defined correctly. URL handling is baked into UI-Router by default.
I partially agree with you. The point where I disagree is when the URL is obtained by a user who is not supposed to access it. Quite often some websites implement authorization code to check if the user who is currently accessing the page is authorized to do so.
Also I think the route could be a bit different. Something like:
{thingid}/{mysubthingid}
This, I think is a cleaner URL and the parameters could be handled in the same way as the one in your example.
I suggest this in order to make it a bit difficult to unauthorized users.
Anyway, it definitely depends on the kind of application you are implementing. If the app's requirement is to be able to access the page by pasting the URL in the browser then I guess your approach is much better.

In AngularJS, what if you are routing to the same page (same URL and hash)?

In an AngularJS Single Page Application, if you are in the main page and now you route to the second page, and use the "service" (as a global), and use myService.cityName to pull data using AJAX (by using $resource), that's fine.
But what if on your page header, you also have an input text box, and you can type in a city name, and when the user press Enter, you are routing to the same page (the second page). So I think browser didn't do anything because it is the exact same URL. How would you solve this issue or what is the AngularJS's way of handling this?
(Update: it is not to refresh or reload a page... it is about, say, you get weather data for a city, and you are on the forecast subpage, with a path of #/forecast. Now at the top search box on the page, you enter a different city and click "Submit" to go to the same path #/forecast then nothing will happen)
Update2: essentially, in my controller code:
$scope.submit = function() {
$location.path("/dataPage");
}
and my form is <form ng-submit="submit()">.
so if I am already on the dataPage, then now browser wants to go to dataPage to show new data, but since the path and hash is exactly the same, the browser just will not do anything. (I suppose due to no hashchange event happened, so nobody will notify AngularJS to do anything)
Your question is unclear, if you want a page refresh like F5, one solution is at "Solution for refresh page". But if you just want to reload things, you could follow "Solution for reload page".
Solution for refresh page:
To update a page, all that you need to update the model data of controller and the page will be updated accordingly. Ideally, you should never refresh an entire page in AngualrJS . If you need to do so, you are not using AngularJS correctly .
The $location service does not provide access to refreshing the entire page. If you need to refresh the entire page, you need to use the $window.location object.
$window.location.href = "YOURPAGEPATH";
Solution for reload page:
just want to reload things under ng-view, re-initialized the current route controller, you could do like below.
$route.reload("YOURPATH");
$route.reload() will reinitialise the controllers but not the
services.
If you want to reset the whole state of your application you can use:
$window.location.reload();
This is a standard DOM method which you can access injecting the $window service
Also see: $route documentation
According to $route documentation:
Causes $route service to reload the current route even if $location
hasn't changed. As a result of that, ngView creates new scope,
reinstantiates the controller.
Well you can just save data from request on your global service. And in controller you can bind that data, so if you press submit from header that function should just refresh your data in service.

Server-side redirect to angular SPA view

How do you get a server-side redirect to go to a certain view in an angular app? I am guessing it has something to do with the redirect not triggering the part after the hash, but can this limitation be beat?
More info
I'm redirecting from an MVC controller to a page with an angular app. I'm using ui-router. The page containing the ui-view gets rendered, but processing stops there. If I refresh twice or go to the URL manually the page works as expected.
The MVC controller is called from a form which posts a file to the server and asynchronously populates a database and redirects when it's finished.
I don't see the problem, you can send a full url with hash and all on the location header and the browser will follow it, just check the response headers for this:
http://web-cf8f140d-22d3-4acd-b7a5-f9fa4e15e094.runnablecodesnippets.com/
What you can't do is getting the hash part directly from the request on the server side.

Angular.js: prevent view loading when browser loads for the first time?

How can I prevent route loading when I hit refresh or when I load the page for the first time?
My backend respond with a chunk of HTML when ajax request and the whole page when normal request. If a normal request was made, I don't want angular to made the same request all over again to retrieve the same page but with a chunk of html instead of the whole page when the page loads.
How can I configure this?
May be you can't do that.
If you don't want the page to blink when angular load the whole page again, you can use the resolve attribute in angular-route or angular-ui-router.
You know, if angular don't load the page again, it can't handle user click or something else.
I have wrote a blog about this, researching the same problem with you, but in chinese. In the last, I decide to use resolve.
My blog url is http://isay.me/2014/06/angular-prerender-seo-and-use-resolve-for-page-flicker.html
Maybe you can have a look or not :)

Angular reload current route and reload the current template

When a use visits a private page unauthorized, say profile, my backend 302 redirects to a controller action that serves up the login partial in place of the profile partial. Since it 302 redirects to an action that returns a partial, the url address bar doesn't change from the page the user was trying to access ("/profile").
I was going to "fix" that but actually I think it makes a good user experience instead of dealing with return urls as query params.
The idea is once they log in I just want to reload the current route aka do a GET request for the profile partial via "/profile" and switch it back in instead of the login partial.
However, I can't get this "reload current route" to work. I tried all of the following:
$location.$$compose();
$location.absUrl($location.path());
$location.url($location.path());
$location.path($location.path())
$route.reload();
But none work. $route.reload() seems to be the definite way but it also doesn't work. It goes through the route cycle, reinstantiates the controller, but does not do GET request to reload the template
The only thing that works is a hard refresh via location.reload() but that is not ideal.
How can I force angular to reload the template for the current route?
Ok I found the solution provided by lgalfaso on Github (exact paste):
Templates are
cached, if a user does not have the permissions to be in a page, then
this check should be done before it reaches the controller or after,
within the controller, but not on the template retrieval
If this is the way you want to follow, then you need to remove the
template from the $templateCache before you call reload
So that worked for me because login template actually gets cached as the template the user was trying to access. So removing it and letting angular re-fetch the correct one for the current route worked like a charm.
var currentPageTemplate = $route.current.templateUrl;
$templateCache.remove(currentPageTemplate);
$route.reload();
I have noticed that $route.reload() method re-instantiates everything that is setup on your $routeProvider.when("/someUrl",{controller:'SomeController',templateUrl:'SomeView.html'}) template,controller and/or any resolved promises you may have passed within the .when() method.
Therefore; when you build your app, if you want $route.reload() to reload all the page and re-instantiate controllers you must put everything under your <div ng-view></div> container and include any menus or footers inside the templateUrl file.

Resources