Reload data after state transition - angularjs

I have a problem after calling the $state.transitionTo method.
$state.transitionTo('example', {}, { reload: true});
When I access my view in the first time, the data is correctly loaded. If I manually go back in my application and try to access the same page, the data is reloaded.
But, when I call the $state.transitionTo, the code inside the controller is not executed and myList is not updated.
Here is the code for the controller.
.controller('ExampleCtrl', function
($scope, $state, $stateParams, Service) {
$scope.myList= {};
var exampleCtrl = $scope;
ProdutorService.findAllData($scope.example.idExample).then(function(data){
exampleCtrl.myList = data;
});
}
I think that it might be related to this bug in the $state.reload():
(bug with controllers reinstantiating right now, fixing soon).
https://github.com/angular-ui/ui-router/wiki/Quick-Reference#statereload
Is there any option to enforce executing the controller code or to call a controller function after the state transition?
I am using angularjs with ionic and cordova. It is a mobile application.

I found the solution.
The responsible for the caching was the ionic framework.
By default, Ionic caches all the views for performance. In order to avoid that, it is needed to add a cache:false in the state definition.
.state('example', {
url: "/example",
templateUrl: 'templates/example.html',
controller: 'ExampleCtrl',
cache: false
})
This way, my data is being refreshed.

you can also use the $ionicView.enter listener to update the data, this way the whole view will not need to be recreated.
Scroll Down to View Lifecycle in this blog post

Related

Running ui.router in modal and ui-view

So not certain if this is possible with ui.router, but thought I would add a nice little modal that launches and says welcome to the site, give the user a chance to enter some details and so forth.
Setup is that the base view have two ui-views (mainview and menuview), and I added formview in the modal. I can get it to work so that when the modal opens it loads formview
.state('form-welcome', {
views: {
formview:
{
templateUrl: "/modals/form-welcome",
},
},
//parent:"index"
})
Didn't actually think it would work that easy, but it did, the problem is that as soon as it has loaded, it resets mainview and menuview (and as it is a modal, that means the background goes grey).
I tried to make form-welcome a child of index (the initial view), however, that breaks the whole thing. (index looks as follows)
.state('index', {
url:"/int/index",
views: {
mainview: {
templateUrl: "/pages/index",
controller: "sketchMagController"
},
menuview: {templateUrl: "/pages/top-menu"},
},
})
I can reload all three views (mainview, menuview and formview), and other than a flickering screen its not to bad. But is there a way I can limit state, so that it only changes formview but leaves the other ones alone.
Reason is that I want to change formview through five different screens, and hate flickering pages:)
It seems to me like it should be possible, but I may have missunderstood how it works
UI-router is for changing the application state, not nesting views together. For that purpose you have angular#component.
var app = angular.module('app', []);
app.component('myModal', {
template: '<div ng-show="isShowing">Hello User</div>',
controller: 'modalCtrl'
});
app.controller('modalCtrl', ['$scope', 'modalService', function($scope, modalService) {
//Save showing state in a service (default to false) so you don't popup your modal everytime user visit homepage
$scope.isShowing = modalService.getShowStatus();
$scope.pressButton = function() {
$scope.isShowing = false;
modalService.setShowStatus(false);
}
});
Then using ui-router, declare your index state, with its template as follow
<-- INDEX.HTML -->
<my-modal></my-modal>
<div ui-view='mainview'></div>
<div ui-view='menuview'></div>
The power of ui-router is the ability to replace the ui-views by different template each different state
stateIndex: abstract
stateIndex.stateA: mainview: /home.html
stateIndex.stateB: mainview: /information.html
So ask yourself, will menuview gonna change to different templates in future states? If not, make it a component.
I would try a different approach, and not having this "welcome" modal part of UI router. It doesn't sounds it should be a "state" in an app, where you can navigate to etc.
I would just pop up this welcome modal after your app finished to bootstrap (e.g. in your run() method or after w/e logic you have to start your app), based on your business logic (e.g. show it only one time).
Why not try $uibModal, part of ui-bootstrap? https://angular-ui.github.io/bootstrap/
It sounds like its everything you need, It pretty much has its own state (includes its own view and controller) so piping data in is easy. You can even pass on data captured within the modal with a simple result.then(function(){} . We use it at work and it doesn't reload the state its on.
You'll probably just want to have it be a function that runs automatically in your controller. If you want to limit how often it pops up you can even have some logic for determining when it pops up in the resolve, pass it to your controller and open the modal based on the resolve.
I think the best way to accomplish what you want is to listen on state changed event and fire the modal. The idea is you fire the welcome modal when the first view is opened and then you set variable in localStorage or some service (depends what you want to achive). Here is an example
$rootScope.$on(['$stateChangeSuccess', function(){
var welcomeModalFired = localStorage.get('welcomeModalFired');
if(!welcomeModalFired) {
//fire modal
localStorage.set('welcomeModalFired', true);
}
})

Resolve must contain all promises even from controller?

Probably it's just as easy as I think it is, but I cannot really find an answer to my question on the internet, so I hope you guys know the answer just by looking at a small piece of my code.
Problem: I'm using the UI router in Angular and it loads the template before all the data is loaded. So all input fields receive the correct values AFTER the template is already loaded. So the input fields are empty for a second or two....
I think my resolve is not as it should be:
So my ui-router code looks something like this (check the resolve object):
$stateProvider.state('teststate', {
url: '/test/',
templateUrl: 'app/page/template.html',
controller: 'testCtrl',
resolve: {
access: ["Access", function(Access) { return Access.isAuthenticated(); }],
UserProfile: 'UserProfile'
}
});
Now the controller contains the promise to get some data from an API url:
function TestCtrl($scope, $state, $stateParams, TestService) {
TestService.get($stateParams.id).then(function(response) {
$scope.data = response;
});
}
Now the service (which connects to the API) should return the promise to the Controller:
TestService.factory('TestService', ['Restangular', function(Restangular) {
var factory = {};
factory.get = function(id) {
return Restangular.one('api/test', id).get();
}
return factory;
}]);
Now, could the problem be, that because the TestService.get() (which connects to the API) within the Controller, gets executed NOT before the template is loaded, because it's not inside the resolve object? So the UI router doesn't resolve the call to the API? I'm just curious or I should move all methods which make API calls, to the resolve object of each stat inside the $stateProvider.
I could run a lot of tests, but if someone just directly knows the answer by just looking at this question, it helps me a lot.
Your assumptions are all correct.
If you resolve the TestService.get in routing config the data would be readily available to controller as an injectable resource
If you don't want your controller to run and your template to show before all your API calls are finished, you have to put all of them inside ui-routers resolve.
However, if API requests can take a little while it seems better UX to transition to the new page immediately and show some kind of loading indicator (e.g. block-ui) while your API call is running.

Close socket.io connection from client on Url change

I am using Socket.IO to pull data for plotting graphs, on
var socket = io.connect();
my Node js server's io.on('connection', function(socket){}) starts a function, on interval of every 1 sec it emits the data to the clients connected.
I want the socket to close on change of Url (away from app) from client and server should stop emitting data.
As we discussed in the comments to the question, you're looking to disconnect from the socket when your Angular ngRoute controller partial route changes away from a given state; this is different as a partial routing single-page application doesn't trigger a fully new page, but rather loads up partial content (defined in partial/template files or script definitions). This is key to why Furkan Başaran's answer won't work for you out of the box for changes between Angular routed states within the same SPA.
I've prepared a plunker that is a fairly basic AngularJS app; it has three html partials that it shows at three corresponding routes using ngRouter and invokes a function to <do-something> when the route change occurs. In this case, it's throwing an alert function, whereas in your case you may which to check for whether or not the route is to anything not the route you want to provide the socket functionality on.
Note: there is a change on every time the Angular app registers a route changes, so the initial establishment will register with a blank value for the / state, then every time it changes from the load, including the otherwise.redirectTo('/destination1').
Plunker (embedded) link:
http://embed.plnkr.co/ayjgYCsox7RGl5OjyGsV/
Quick break down:
I start by defining my handling function to be triggered on ngRouter changes. The passed value is the registered route (after the / or /index.html), such as /potatoes as is a case in my example. This is where you should perform your socket.disconnect();.
function changedMyPartialRoute(val){
alert('url has changed to: ' + val);
}
After I defined my app (angular.module('appName', [...), I define the config for my ngRouter setup.
.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/meat', {
templateUrl: 'meat.html',
controller: 'MeatCtrl',
controllerAs: 'meat'
})
.when('/potatoes', {
templateUrl: 'potatoes.html',
controller: 'PotatoCtrl',
controllerAs: 'potato'
})
.when('/bacon', {
templateUrl: 'bacon.html',
controller: 'BaconCtrl',
controllerAs: 'bacon'
})
.otherwise({
redirectTo: '/meat'
});
}])
Finally, I invoke a run block on the app (module) to provide that hook into detecting the change. I'm passing in $rootScope and $location and it performs a $watch on the $rootScope to detect a change in the $location.path(). That whole run block:
.run( function($rootScope, $location) {
$rootScope.$watch(function() {
return $location.path();
},
function(val){
// the handling function from step 1
changedMyPartialRoute(val);
});
})
If you need to do something with $rootScope, as I suspect you might, you'll need to hand off the $rootScope handle through the changedMyPartialRoute function or just define your handling inline in the function callback in the .run block. This HTML5rocks.com tutorial shows their configuration in their Angular app by hooking into $rootScope, as opposed to $scope as you mentioned in the above comments.
I hope that helps!
When client closed connection close automatically, if you want see this, write this code to your node server and look to console when closed your client tab.
io.on('disconnect', function() {
console.log("Connection disconnected");
})

Need to resolve a $http request before the execution of the resolve property inside $stateProvider

I’m building an angular application that is going to run on several domains. Since there are different configurations on each domain I'll need to fetch all the variables by doing a call to the server. The call will return a JSON object that contains different rest urls.
My problem is that I need to do this call before the 'resolve' step inside the $stateProvider, since I already have a task that is dependent on the configuration object from the server.
What should work here is a really great feature $urlRouterProvider.deferIntercept(); documented here:
$urlRouterProvider
The deferIntercept(defer)
Disables (or enables) deferring location change interception.
If you wish to customize the behavior of syncing the URL (for example, if you wish to defer a transition but maintain the current URL), call this method at configuration time. Then, at run time, call $urlRouter.listen() after you have configured your own $locationChangeSuccess event handler.
The code snippet from the API documentation:
var app = angular.module('app', ['ui.router.router']);
app.config(function($urlRouterProvider) {
// Prevent $urlRouter from automatically intercepting URL changes;
// this allows you to configure custom behavior in between
// location changes and route synchronization:
$urlRouterProvider.deferIntercept();
}).run(function($rootScope, $urlRouter, UserService) {
$rootScope.$on('$locationChangeSuccess', function(e) {
// UserService is an example service for managing user state
if (UserService.isLoggedIn()) return;
// Prevent $urlRouter's default handler from firing
e.preventDefault();
UserService.handleLogin().then(function() {
// Once the user has logged in, sync the current URL
// to the router:
$urlRouter.sync();
});
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.listen();
});
And also, related to this question:
AngularJS - UI-router - How to configure dynamic views
There is working example - plunker
To make it clear, suitable for this use case, let's observe the code of the plunker.
So, firstly we can see the .config() phase. It does have access to providers but NOT to their services (e.g. $http). Not yet, services themselves will be available later...
app.config(function ($locationProvider, $urlRouterProvider, $stateProvider)
{
// this will put UI-Router into hibernation
// waiting for explicit resurrection later
// it will give us time to do anything we want... even in .run() phase
$urlRouterProvider.deferIntercept();
$urlRouterProvider.otherwise('/other');
$locationProvider.html5Mode({enabled: false});
$stateProviderRef = $stateProvider;
});
What we did, is set a reference to provider (configurable object), to be used later: $stateProviderRef.
And the most crucial thing is we STOPPED the UI-Router, and forced him to wait for us with $urlRouterProvider.deferIntercept(); (see the doc and cites above)
There is an extract of the .run() phase:
app.run(['$q', '$rootScope','$http', '$urlRouter',
function ($q, $rootScope, $http, $urlRouter)
{
// RUN phase can use services (conigured in config phase)
// e.g. $http to load some data
$http
.get("myJson.json")
.success(function(data)
{
// here we can use the loaded stuff to enhance our states
angular.forEach(data, function (value, key)
{
var state = { ... }
...
$stateProviderRef.state(value.name, state);
});
// Configures $urlRouter's listener *after* your custom listener
// here comes resurrection of the UI-Router
// these two important calls, will return the execution to the
// routing provider
// and let the application to use just loaded stuff
$urlRouter.sync();
$urlRouter.listen();
});
}]);
Most important is, that this .run() was executed just ONCE. Only once. As we require.
We can also use another technique: resolve inside of one super root state, which is parent of all state hierarchy roots. Check all the details here:
Nested states or views for layout with leftbar in ui-router?
There is another way how to solve the:
How to resolve $http request before the execution of the resolve property inside $stateProvider?
In case, that we just need to get some $http result inside of the resolve, we can do it just like this:
resolve: {
myResolve1:
function($http, $stateParams) {
return $http.get("/api/foos/"+stateParams.fooID);
}
}
This is a snippet from documenation of the [$stateProvider][1], section resolve. We can see, that we return the promise of the $http service: return $http.get()
So, to extend that, to asnwer:
But how can I make the ui-router wait until the promise is resolved?
we can just use return $http.get() and then .then(). And inside of it, we have access to returned result - which we can adjust:
myResolve1:
function($http, $stateParams) {
// we still return the promise of the $http.get))
return $http
.get("/api/foos/"+stateParams.fooID)
.then(function(response) {
// but now, the content of resolved property
// will be the first item of the loaded array
return response.data[0];
};
}
}
There is also enahnced solution - in case we need to make this to happen before every state. We just introduce some "root" state as a super parent. It will contain such resolve and all child states will wait until this one is resolved, but just resolved just once. See more here: angular ui-router resolve for parent state

Wait until scope variable is loaded before using it in the view in angular.js

I've seen this and this but it seems like there might be a simpler way.
In my view I have several menu options that are controlled through permissioning - i.e., not everyone can see a "Dashboard" view. So in my menu option in my view I have something like the following:
<li ng-show="validatePermission('Dashboard')">Dashboard</li>
In my controller I have a validatePermission method defined where it is looking at the permissions of the current user. For example:
$scope.validatePermission = function(objectName) {
if $scope.allPermissions......
Also in my controller I'm loading those permissions via an $http call:
$http.get('permissions/' + userid + '.json').success(function(data) {
$scope.allPermissions = data;....
The issue is that $scope.allPermissions doesn't get loaded before the view makes the call to validatePermission. How can I wait for allPermissions to be loaded before the view renders?
You ask:
How can I wait for allPermissions to be loaded before the view renders?
To prevent the entire view from rendering, you must use resolve. You don't have to use the promise library though, since $http returns a promise:
var app = angular.module('app');
app.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl : 'template.html',
controller : 'MyCtrl',
resolve : MyCtrl.resolve
});
});
function MyCtrl ($scope, myHttpResponse) {
// controller logic
}
MyCtrl.resolve = {
myHttpResponse : function($http) {
return $http({
method: 'GET',
url: 'http://example.com'
})
.success(function(data, status) {
// Probably no need to do anything here.
})
.error(function(data, status){
// Maybe add an error message to a service here.
// In this case your $http promise was rejected automatically and the view won't render.
});
}
}
But if you simply want to hide the dashboard <li>, then do as Joe Gauterin suggested. Here's a very simple example plunkr if you need it.
Have the validatedPermission function return false when allPermissions hasn't been loaded. That way the element with your ng-show won't be displayed until allPermissions has been loaded.
Alternatively, put an ng-show="allPermissions" on the enclosing <ul> or <ol>.
You can also specify on your routecontroller a resolve object that will wait for that object to resolve prior to rendering that route.
From the angular docs: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider
resolve - {Object.=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $routeChangeSuccess event is fired. The map object is:
key – {string}: a name of a dependency to be injected into the controller.
factory - {string|function}: If string then it is an alias for a service. Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before its value is injected into the controller.
A google group reference as well: https://groups.google.com/forum/#!topic/angular/QtO8QoxSjYw
I encountered an similar situation, you might also want to take a quick look at
http://docs.angularjs.org/api/ng/directive/ngCloak
if you're still seeing a "flicker" effect.
As per the angularjs documentation:
The ngCloak directive is used to prevent the Angular html template from being briefly displayed by the browser in its raw (uncompiled) form while your application is loading. Use this directive to avoid the undesirable flicker effect caused by the html template display.
Wrapping the code in ng-if fixed the issue for me:
<div ng-if="dependentObject">
<!-- code for dependentObject goes here -->
</div>

Resources