I have a query related to setting controllers at runtime.
I want something like:
.state{'app.thisState',
url: '/thisUrl',
views:{
templateUrl: 'templates/some_template.html',
controller: 'XYZCtrlr' //This is where I want to set different controllers depending on the scenario.
}};
How can we set controllers at runtime?
You could use controllerProvider option of ui-router state
.state ('app.thisState', { //<-- correct syntax here
url: '/thisUrl',
views: {
templateUrl: 'templates/some_template.html',
controller: 'XYZCtrlr',
controllerProvider: function($stateParams) { //<-- add dependencies here
//perform logic here
var ctrlName = $stateParams.type + "Controller";
return ctrlName; //return string name here, which will the name of controller.
}
}
};
Related
My question is simple, but I couldn't find solution anywhere else.
For example I have a normal route state with parameter,
.state('page', {
url: '/page/:pageid',
templateUrl: 'templates/pages.html',
controller: 'pagesCtrl'
})
However for example, if I have 1000 pages, but for the Number 1 and Number 999 pages I have to use another template. How could I simply do this? Something like
.state('page', {
url: '/page/:pageid',
templateUrl: 'templates/pages.html',
controller: 'pagesCtrl'
})
.state('page', {
url: '/page/1',
templateUrl: 'templates/page1.html',
controller: 'pagesCtrl'
})
.state('page', {
url: '/page/999',
templateUrl: 'templates/page999.html',
controller: 'pagesCtrl'
})
Will this work?I tested, the later 2 options are not overriding the original state with parameter.
If I wish to use the same controller, how to load the 1 and the 999 as the pageid parameter in the controller?
Definitely you shouldn't have two more state for separate template. You should use single generic state which will take templateUrl with the help of passed state parameter.
Code
.state('page', {
url: '/page/:id',
templateUrl: function($stateParams){
var template = $stateParams.id.indexOf([1, 1000]) > -1?'pages.html'
:'page'+$stateParams.id+'.html';
return 'templates/'+template ,
}
controller: 'pagesCtrl'
})
I have this controller:
class Ctrl{
constructor($http, $scope, $stateParams){
if(!$scope.items){
$http.get(...).then((res)=> {
$scope.items = res.data;
});
}
if($stateParams.id){
$scope.currItem = $scope.items[$statePrams.id];
}
}
I have 2 states:
.state('dashboard.items', {
url: '/items',
templateUrl: 'items.html',
controller:'Ctrl'
})
.state('dashboard.items.details', {
url: '/:id',
templateUrl: '/ItemDetails.html',
controller: 'Ctrl'
})
I want to be sure that items array already initialized before I show the details of one of them.
How is it possible without code duplicating? (If else and same code inside each)
Thanks.
you can use state resolve on parent level (that is exact answer on your question)
or just use service and make initialisation there. service is singleton.
I've been trying to get my Angular app to populate the controller with data from a database (mongodb) before the page loads. I can't quite get it to work. I'm trying to use the "resolve" property of angular ui-router but it's not working and I can't figure it out!!
Here's the full code for my app:
var freezerApp = angular.module('freezerApp', ['ui.router']);
freezerApp.config([
'$stateProvider','$urlRouterProvider',function($stateProvider,$urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/partials/home.html',
controller: 'freezerCtrl',
});
$stateProvider
.state('freezer', {
url: '/freezers',
templateUrl: 'partials/freezers.html',
controller: 'freezerCtrl',
//not working:
resolve: {
freezerPromise: function($scope){
return $scope.getAll();
}
};
});
$urlRouterProvider.otherwise('home');
}]);
freezerApp.controller('freezerCtrl', ['$scope', '$http', function ($scope,$http) {
$scope.freezer =
{'freezername':'Freezer Name',
'building':'Building',
'floor':'Floor',
'room':'Room',
'shelves': 0,
'racks': 0
};
$scope.add_freezer = function() {
$scope.freezers.push(
{'freezername': $scope.freezer.freezername,
'building':$scope.freezer.building,
'floor':$scope.freezer.floor,
'room':$scope.freezer.room,
'shelves': $scope.freezer.shelves,
'racks': $scope.freezer.racks
}
);
};
$scope.freezers = [
{
}
];
$scope.default_freezer = $scope.freezers[0];
$scope.getAll = function() {
return $http.get('/freezers').success(function(data){
angular.copy(data, $scope.freezers);
});
};
}]);
According to the Ui-Router Resolve Documentation:
You can use resolve to provide your controller with content or data that is custom to the state. resolve is 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 $stateChangeSuccess event is fired.
It looks like you are trying to provide $scope object from your freezerCtrl to your resolve property. This is incorrect.
I would recommend you create a factory like so for your api call.
angular.module.('freezerApp').factory('freezerFact',function($http){
return {
getAll: $http.get('/freezers')
}
});
Then inside of your freezer $state deceleration you could do it like this:
$stateProvider
.state('freezer', {
url: '/freezers',
templateUrl: 'partials/freezers.html',
controller: 'freezerCtrl',
resolve: {
freezerPromise: function(freezerFact){
return freezerFact.getAll;
}
};
});
Then you would pass the freezerPromise object into your freezerCtrl and manipulate the promise after that.
I have this in my app.js:
$stateProvider
.state('actionplans', {
url: "/actionplans",
templateUrl: "pages/actionplans.html",
//controller : 'ActionplansCtrl'
})
.state('actionplans.planning', {
url: "/planning",
templateUrl: "pages/actionplans.planning.html",
//controller : 'ActionplansCtrl'
})
.state('actionplans.summary', {
url: "/summary",
templateUrl: "pages/actionplans.summary.html",
//controller : 'ActionplansCtrl'
})
How can I default load nest view action 'actionplans.summary.html' when called actionplans.html?
There is a working example
The way which will
load some view inside of a parent - and stay on parent
allow child change it when navigating to child
is called Multiple named views:
.state('actionplans', {
url: "/actionplans",
views: {
'': {
templateUrl: "pages/actionplans.html",
//controller : 'ActionplansCtrl'
},
'#actionplans': {
templateUrl: "pages/actionplans.summary.html",
//controller : 'ActionplansCtrl'
}
}
})
.state('actionplans.planning', {
url: "/planning",
templateUrl: "pages/actionplans.planning.html",
//controller : 'ActionplansCtrl'
})
.state('actionplans.summary', {
url: "/summary",
templateUrl: "pages/actionplans.summary.html",
//controller : 'ActionplansCtrl'
})
What we did above, is that we used views : {} object to define two views. First is targeting the index.html (the '') the second is targeting this state view target for children ( the '#actionplans').
views: {
'': { // index.html
...
},
'#actionplans': { // this targets the unnamed view for children
Read more about absolute names here
Another way, is to define some default redirection, but that will disable parent state as a real target (e.g. here Redirect a state to default substate with UI-Router in AngularJS)
Here discuss about AngularJS Routing Using UI-Router, you will get enough idea about nested view and multiple view.
https://scotch.io/tutorials/angular-routing-using-ui-router
I found a simple solution here.
$urlRouterProvider.when('/actionplans', '/actionplans/summary');//<-- Add in this line
$stateProvider
.state('actionplans', {
url: "/actionplans",
abstract: true,/// <-- Add in this line
templateUrl: "pages/actionplans.html",
})
.state('actionplans.planning', {
url: "/planning",
templateUrl: "pages/actionplans.planning.html",
})
.state('actionplans.summary', {
url: "/summary",
templateUrl: "pages/actionplans.summary.html",
})
This will load nest view actionplans.summary.html by default when you call /actionplans. My apology that I did not make this clearer in my question so I post the answer here hopefully it will help someone else with the similar scenario.
I'm using $routeProvider for routing in my Angular app. And for 2 routes I'm using same HTML template and same Controller.
when('/products, {
templateUrl: 'views/products.html',
controller: 'ProductListCtrl'
}).
when('/myProducts', {
templateUrl: 'views/products.html',
controller: 'ProductListCtrl'
}).
Only difference in data that I want to show. I.e. for path products I want AJAX request to myserver:8080/products and for for path myProducts I want to load data from AJAX request to myserver:8080/products/my.
For now I i'm using $location service to distinguish the current page (products or myProducts) and load apropriate data.
Is there some more elegant way to do it? For example using resolve method of $routeProvider?
The best way to reuse controller name in today scenario is to use resolve with $routeparams.
You can modify your code as below
when('/products, {
templateUrl: 'views/products.html',
controller: 'ProductListCtrl',
resolve: {
product: function($http) {
return $http.get('/products')
},
needToShowFilter:function($q){
var showfilter = $q.defer();
showfilter.resolve(false);
return showfilter.promise
}
}
}).
when('/myProducts', {
templateUrl: 'views/products.html',
controller: 'ProductListCtrl',
resolve: {
product: function($http) {
return $http.get('/products/my')
},
needToShowFilter:function($q){
var showfilter = $q.defer();
showfilter.resolve(true);
return showfilter.promise
}
}
}).
And then in your controller you can inject the product into the controller code.
try to add $route in your controller, and log
$route.current
to see what you have inside, i think thats the way to get the information