We are developing an single page application using angular JS and I am using state provider for configuring routes. Basically there is a global navigation view and a dashboard view. I have to pass few params from navigation to make a service call and then display the dashboard accordingly.I have split the states as two, one for navigation and other for dashboard. THe thing which i am not able to figure out is that where should i make ajax call to fetch dashboard data. Should i make it in navigation itself and pass it through resolve. or should i just pass the data to dashboard controller and make ajax call from there. Below is my state
$stateProvider
.state('home', {
url: '/',
templateUrl: 'templates/home.htm',
controller: 'homeController',
})
.state('dashboard', {
url: 'contact',
templateUrl: 'templates/dashboard.htm',
controller: 'dashboardController'
})
.state('state3', {
url: '/articles',
templateUrl: 'templates/state3.htm',
controller: 'state3Controller'
});
$urlRouterProvider.otherwise('/home');
This entirely depends on how you want the user experience to play out.
If you want to do all the data fetching before transitioning to the dashboard state, use a resolve state configuration
.state('dashboard', {
url: '/contact',
templateUrl: 'templates/dashboard.htm',
controller: 'dashboardController',
resolve: {
someData: function($http) {
return $http.get('something').then(res => res.data);
}
}
}
then your controller can be injected with someData, eg
.controller('dashboardController', function($scope, someData) { ... })
This will cause the state transition to wait until the someName promise has been resolved meaning the data is available right away in the controller.
If however you want to immediately transition to the dashboard state (and maybe show a loading message, spinner, etc), you would move the data fetching to the controller
.controller('dashboardController', function($scope, $http) {
$scope.loading = true; // just an example
$http.get('something').then(res => {
$scope.loading = false;
$scope.data = res.data;
});
})
Related
I have site navigation that doesn't exist in any particular state. It's always available along the top of the page, regardless of which state the application is in.
I need to hide/show certain menu options depending on who the user is. I'm using windows authentication so a trip to the server is a necessity. The problem is since the nav bar doesn't belong to any particular state I don't know where to put the resolve.
Is there something like a global state which would be resolved first before any other states where I could put the resolve?
Something like:
.state('$global', {
url: '/',
templateUrl: 'partials/navigation/navbar.html',
controller: 'NavCtrl',
resolve: {
navData: ['$http', 'SettingsFactory', 'ViewMatrixService', function($http, SettingsFactory, ViewMatrixService) {
return $http.get(SettingsFactory.APIUrl + 'api/nav', { withCredentials: true }).then(function (response) {
ViewMatrixService.GenerateHomeViewMatrix(response.data.CurrentUser);
return response.data;
});
}]
}
})
I considered using $broadcast but then I'd need to make sure every possible point of entry to the application gets the information the nav bar needs from the server and broadcasts it which contaminates all my other controllers with nav bar responsibilities.
I was unable to find a way to use resolves outside of states, but I did find a solution.
I created an abstract parent state called app. I have a resolve in app that gets the user's profile. Then it can be injected into any child state controllers.
Since the nav bar is stateless, in the same resolve I pass the currentUser object to a service which is used across the app to store visibility and disabled flags for all controls.
It looks like this:
$stateProvider
.state('app', {
abstract: true,
template: '<ui-view/>',
resolve: {
currentUser: ['$http', 'SettingsFactory', 'ViewMatrixService', function($http, SettingsFactory, ViewMatrixService) {
return $http.get(SettingsFactory.APIUrl + 'api/users/current', { withCredentials: true }).then(function (response) {
ViewMatrixService.GenerateHomeViewMatrix(response.data);
return response.data;
});
}
]}
})
.state('app.home', {
url: '/',
templateUrl: 'partials/home.html',
controller: 'HomepageCtrl',
resolve: {
homeData: ['$http', 'SettingsFactory', function($http, SettingsFactory) {
return $http.get(SettingsFactory.APIUrl + 'api/home', { withCredentials: true }).then(function (response) {
return response.data;
});
}]
}
})
.state('app.userProfiles', {
url: '/admin/users',
templateUrl: 'partials/admin/user-profiles.html',
controller: 'UserProfilesCtrl'
})...
I setup a small example based on the article posted on setting up UI routing.
I modified the example to test explicitly navigating from a child nested state to sibling state using $state.go(). any ideas what I am doing wrong?
I keep getting the error below:
Error resolving State. Could not resolve .paragraph from state home.list
$stateProvider
// HOME STATES AND NESTED VIEWS ========================================
.state('home', {
url: '/home',
templateUrl: 'partial-home.html'
})
// nested list with custom controller
.state('home.list', {
url: '/list',
templateUrl: 'partial-home-list.html',
controller: function($scope,$state) {
$scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
$scope.navigateToNextState = function () {
$state.go('.paragraph');
};
}
})
// nested list with just some random string data
.state('home.paragraph', {
url: '/paragraph',
template: 'I could sure use a drink right now.'
})
http://plnkr.co/edit/Nae6xz9qcBp3IRYi0wcD?p=preview
Controller should be adjusted like this:
controller: function($scope, $state) {
$scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
$scope.navigateToNextState = function () {
$state.go('^.paragraph');
};
}
We need a $state as one of function params. And we are in a child, so we have to use '^.paragraph' (instead of '.paragraph') to ask for another child of our parent
Checked working fixed version here
I am learning angularJS and creating a web application which uses ui-router.
I have defined states as follows:
angular.module('test', [])
.config(function($stateProvider){
$stateProvider.
state('root',{
url: '/',
abstract:true,
templateUrl: '/root.html',
controller: 'MyController'
})
.state('root.route1',{
url: '/route1',
parent: 'root',
views:{
'':{
templateUrl: '/route1.html'
}
'estimatedCost#':{
templateUrl: '/esitmatedCost.html'
}
}
})
.state('root.route2',{
url: '/route2',
parent: 'root',
views:{
'':{
templateUrl: '/route2.html'
}
'estimatedCost#':{
templateUrl: '/esitmatedCost.html'
}
}
})
});
While navigating back and forth between route1 and route2, I want to share scope variables from MyController. When I navigate to route2 from route1, it is loosing value of scope variable.
I am not sure what I am doing wrong.
Can anyone help me?
Thanks in advance.
I have yet to work with the ui-router, but I have worked with AngularJS for the last couple of years and this is how the language generally has worked in the past.
A controller's main purpose is to control the data on a single template. These controllers can communicate to each other through an AngularJS factory, often known as a service. In your case, you probably want to use a service as the controllers are getting destroyed on successful route change.
angular.module('test', [])
.factory('myFactory', function() {
var info = "Hello World";
return {
info: info
};
})
.controller('CtrlOne', function($scope, myFactory) {
$scope.info = myFactory.info;
})
.controller('CtrlTwo', function($scope, myFactory) {
$scope.info = myFacotry.info;
});
You can then use the two controllers on the two different views and they share the variables from the service that connects them.
Use $stateParams to pass parameters between two states.
Fallow the below steps :
Define your state with params object.
.state('route.route1', {
url: 'your url name',
params: {
nameOfParamObj: null
},
controller: 'your controller',
templateUrl: 'your template url',
})
From the controller where you want to send scope data use as fallows
$state.go(toState, params, options);
In toState controller catch state params using $stateParams
$stateParams.yourParamObjectName
Make sure $stateParams, $state services as dependency in your regarding controller
Have a look into the official ui-router documentation Here.
What is the best way to stop a user from going to a state if there is no data for that state? I need to redirect the user to /featured if there is no data in one of the other states.
I thought that I could check the controller for data first and then redirect using $state.go if the data was null, but I can't find an example of how to access data in a controller inside of an onEnter event
I think a code example should sum it up:
fseControllers.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/featured");
$stateProvider
.state('featured', {
url: "/featured",
templateUrl: "featured-template"
})
.state('friends', {
url: "/friends",
templateUrl: "friends-template",
controller: 'FseController',
onEnter: function(){
// Here I want to redirect to /featured if no data in controller
}
})
.state('stories', {
url: "/stories",
templateUrl: "stories-template"
})
.state('events', {
url: "/events",
templateUrl: "events-template"
})
})
You could refactor your controller to use a service , that can be injected into onEnter function and to the Controller.
AngularJS Services
onEnter:function(FriendsService, $location){
if(!!FriendsService.getFriends()){
$location.path("/featured");
}
}
In state llantas.ordenes I have a jqGrid and other controls as well as in llantas.inventarios
so whenever I switch from #/llantas/ordenes to #/llantas/inventarios I loose the controls data and the jqGrid table is being redraw, so the question is if its possible to keep view data when switching from route to route?
This is my router.js:
ng.route(function($stateProvider, $urlRouterProvider){
// Now set up the states
$stateProvider
.state('llantas', {
url: "/Llantas",
templateUrl: "templates/llantas/index.html"
})
// Ordenes
.state('llantas.ordenes', {
url: "/ordenes",
templateUrl: "templates/llantas/ordenes/index.html",
controller: function($scope, $injector) {
require(['js/controllers/llantas/ordenes/index'], function(llantasOrdenesIndexCtrl) {
$injector.invoke(llantasOrdenesIndexCtrl, this, {'$scope': $scope});
});
}
})
// Inventarios
.state('llantas.inventarios', {
url: "/inventarios",
templateUrl: "templates/llantas/inventarios/index.html",
controller: function($scope, $injector) {
require(['js/controllers/llantas/inventarios/index'], function(llantasInventariosIndexCtrl) {
$injector.invoke(llantasInventariosIndexCtrl, this, {'$scope': $scope});
});
}
})
});
I removed the UI Router and set reloadOnSearch = false on every route.