how to add many controller for state in ui-router ?
I have searched long and hard but found nothing that helped yet. Where I wrong? I really do not know what to do. I wrote all the details below. I've tried and did not succeed.
angular.module('uiRouterApp.typeNews', [
'uiRouterApp.typeNews.Service',
'ui.router'
])
.config(
[
'$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('detail', {
url: '/typeNews/detail/typeNewsId=:typeNewsId',
templateUrl: '/Scripts/ui-route/typeNews/typeNews.detail.html',
controller: [
'$scope', '$stateParams', '$state', 'typeNewsService',
function($scope, $stateParams, $state, typeNewsService) {
typeNewsService.readTypeNews($stateParams.typeNewsId)
.success(function(data, status, headers, config) {
$scope.typeNews = data;
});
}
]
})
.state('typeNews', {
url: '/typeNews/PageSize=:PageSize/PageIndex=:PageIndex',
templateUrl: '/Scripts/ui-route/typeNews/typeNews.html',
controller: 'pagerCtrl'// need two many controller like controller: 'pagerCtrl' , 'gridCtrl'
});
}
]
);
Every state in your state provider maps to a single controller.
Having multiple (many) controllers handling one state is not possible and goes against the basic concept of the framework.
I don't understand what you want to achieve with multiple controllers in one state.
What is your idea and how should this function? Can you explain?
You can have parent and child states and they can have their own controllers. Maybe that is what could help you out.
Based on your comment:
controller: 'pagerCtrl'// need two many controller like controller: 'pagerCtrl' , 'gridCtrl'
I think you want controllers for ui elements. You probably want to use or create a Directive. Have a look at ngTable and ng-Grid for example.
Yes u will need this if u reference some other controllers info....just add one controller to the state and the other one u can nest it as a ng-controller in the html.
JS snippet
angular.module('xTimeApp').config(function ($stateProvider, $httpProvider) {
$stateProvider.state('editpayhistory', {
url: '/payhistory/:id/edit',
templateUrl: '../PayHist/PayHist-edit.html',
controller: 'PayHistEditController' //<-Controller 1
});
HTML snippet:
<div class="form-group">
<div data-ng-controller="AttendanceListController"> <!--Controller 2 -->
<label for="title" class="col-sm-2 control-label">Attendance</label>
<!--Need to compare with same object type, so create an empty _attend object with the payhist id as the _attend id field-->
<div class="col-sm-10">
<select ng-init="payhist._attend = {AC_CODEID: payhist.AC_CODEID}"
ng-change="payhist.AC_CODEID = payhist._attend.AC_CODEID"
ng-model="payhist._attend"
ng-options="attend.AC_CODEID as attend.AC_NAME for attend in attends track by attend.AC_CODEID"></select>
</div>
</div>
.state('app.somestate', {
url : '/someurl',
views:{
'menuContent': {
templateUrl: 'part1.html',
controller: 'ACtrl'
},
'otherContent': {
templateUrl: 'part2.html',
controller: 'BCtrl'
},
'someotherContent': {
templateUrl: 'part3.html',
controller: 'CCtrl'
}
}
})
Related
I'm trying to learn how to use resolves with UI-Router, and I think there's a piece of information I'm missing, because I can't figure out how to make them work.
I have a state set like this:
app.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('testState', {
url: '/testRoute',
controller: 'TestContoller',
views: {
"body": {
templateUrl: "testHtml.html"
}
},
resolve: {
test: function(){
return {value: "test"};
}
}
})
}]);
And then I have a controller:
app.controller("TestController", ["$scope", "test", function($scope, test) {
console.log(test);
}]);
then I have a testHtml.html partial file that doesn't have anything in at the moment:
<div ng-controller="TestController">
Test content
</div>
And that gets loaded into the ui-view in index.html:
<div ui-view="body" autoscroll></div>
I've been fiddling around with this for an hour or so now and googling around and I can't quite figure out what I should be doing to get the resolve to do something and pass the result into the controller.
When you mention views properties on state level options, it ignores templateUrl & controller on that state. It only take controller & template/templateUrl from one of view.
Code
views: {
"body": {
templateUrl: "testHtml.html",
controller: 'TestContoller' //moved it to named-view level
}
},
I'm trying to refactor our current layout to add in a dynamic show/hide section above the header on our page. We are using Angularjs 1.4.2 with ui-router and currently we are using separate route files, although we have a main route. The main.html section of the screen, up to now, was the only section with the dynamic content. In order to get my new route working, I'm having to add it and main to each of the existing routes.
My question is, would a better design be something along this line of a single parent route to handle a resolve, with nested states for the dynamic main content and a new view for my new route and content? As the application grows, do you still continue to put the new routes into the parent route or is there a better way to organize it like we were doing with individual route files or a combination of both?
This is what I'm trying, but it's not working yet, as I'm getting a blank page, but the design for future growth with the application is what I'm trying to get right here:
(function () {
'use strict';
angular
.module('myApp')
.config(config);
config.$inject = ['$stateProvider'];
/* #ngInject */
function config($stateProvider) {
$stateProvider
.state('root', {
template: '<ui-view></ui-view>',
abstract: true,
resolve:{
myLoader: ['myLoader', function(myLoader){
// $http returns a promise for the url data
return myLoader.load();
}]
}
}
.state('main'), {
url: '/?param1¶m2¶m3¶m4',
templateUrl: 'app/routes/main/main.html',
controller: 'MainCtrl',
redirectTo: 'main'
})
$stateProvider
.state('basicsearch', {
url: '/basic-search',
templateUrl: 'app/routes/basicsearch/basicsearch.html',
controller: 'searchQuickCtrl'
})
$stateProvider
.state('advancedsearch', {
url: '/advaned-search',
templateUrl: 'app/routes/advancedsearch/advancedsearch.html',
controller: 'advancedSearchkCtrl'
})
$stateProvider
.state('anothersearch', {
url: '/another-search',
templateUrl: 'app/routes/anothersearch/anothersearch.html',
controller: 'anotherSearchCtrl'
})
.state('myChange', {
url: '/myChange?param5¶m6¶m7¶m8',
views: {
"myChangeView": {
templateUrl: '/app/routes/myChange/myChange.html',
controller: 'myChangeCtrl'
}
}
});
}
})();
Here is a basic layout of what we have:
I am not sure about the issue. But I created working plunker, to show you how we can use state nesting and resolve.
So this is our parent controller for root state and the factory used in resolve
.factory('myLoader', function(){
return {
load: function () {return [1,2] }
};
})
.controller('ParenCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {
$scope.myLoader = myLoader;
}])
.controller('MainCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('searchQuickCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('advancedSearchkCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('anotherSearchCtrl', ['$scope', function ($scope) {}])
Next, we will use controller for abstract root state, to assign result into scope:
.state('root', {
template: '<div ui-view></div>',
abstract: true,
controller: 'ParenCtrl',
resolve:{
myLoader: ['myLoader', function(myLoader){
// $http returns a promise for the url data
return myLoader.load();
}]
}
})
And all states will use that as their parent, and inherit the scope:
.state('main', {
parent: 'root',
url: '/?param1¶m2¶m3¶m4',
templateUrl: 'app/routes/main/main.html',
controller: 'MainCtrl',
//redirectTo: 'main'
})
.state('basicsearch', {
url: '/basic-search',
templateUrl: 'app/routes/basicsearch/basicsearch.html',
parent: 'root',
controller: 'searchQuickCtrl'
})
.state('advancedsearch', {
url: '/advaned-search',
templateUrl: 'app/routes/advancedsearch/advancedsearch.html',
parent: 'root',
controller: 'advancedSearchkCtrl'
})
.state('anothersearch', {
url: '/another-search',
parent: 'root',
templateUrl: 'app/routes/anothersearch/anothersearch.html',
controller: 'anotherSearchCtrl'
})
And properly use it inside of this view:
<div >
<h3>current state name: <var>{{$state.current.name}}</var></h3>
<h4>resolved in parent</h4>
<pre>{{myLoader | json }}</pre>
<h5>params</h5>
<pre>{{$stateParams | json}}</pre>
<h5>state</h5>
<pre>{{$state.current | json}}</pre>
</div>
while all these controller do not use that resolved value
.controller('MainCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('searchQuickCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('advancedSearchkCtrl', ['$scope', 'myLoader', function ($scope, myLoader) {}])
.controller('anotherSearchCtrl', ['$scope', function ($scope) {}])
So, each controller can be provided with stuff resolved in parent (as parameter). That is shown above. But also, becuase parent already used that and assigned that to some $scope variable... all is alraeady in place.
Also check:
Angularjs ui-router abstract state with resolve
How do I share $scope data between states in angularjs ui-router?
Here is an example to check http://embed.plnkr.co/uVMlkk/preview
When we navigate to 'page2' route there is a 'hey, I'm a subroute' note.
But once we navigate anywhere else that note will disappear forever.
The goal is to make some nested states to be shown right away (as a default ones).
I assume there should be some cases using $state.go(), but can't figure it out so far. Any help is highly appreciated.
State definition snippet:
.state('root.page2.tab', {
url: '/:tabId',
templateUrl: 'tpl.page2.tab.html',
controller: 'Page2TabController'
})
.state('root.page2.tab.subroute', {
url: '',
templateUrl: 'tpl.page2.tab.subroute.html'
})
the content of the 'tpl.page2.tab.subroute.html':
hey, I'm a subroute
related controller:
.controller('Page2TabController', ['$scope', '$state', function($scope, $state) {
$scope.tabId = $state.params.tabId;
$state.go('root.page2.tab.subroute');
}])
There is a fixed version.
I removed the url from the 'root.page2.tab.subroute'
.state('root.page2.tab.subroute', {
//url: '',
templateUrl: 'tpl.page2.tab.subroute.html'
})
And because the parent has defined paramater tabId:
.state('root.page2.tab', {
url: '/:tabId',
templateUrl: 'tpl.page2.tab.html',
controller: 'Page2TabController'
})
We have to pass that param inside of the redicrection:
.controller('Page2TabController', ['$scope', '$state', function($scope, $state) {
$scope.tabId = $state.params.tabId;
// instead of this
// $state.go('root.page2.tab.subroute');
// we need this
$state.go('root.page2.tab.subroute', $state.params);
}])
Check the working, fixed version here
ANOTHER approach - using redirectTo - there is a working plunker
One way, inspired by this:
Redirect a state to default substate with UI-Router in AngularJS
could be to add a very smart but small redirect code snippet:
.run(['$rootScope', '$state', function($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function(evt, to, params) {
if (to.redirectTo) {
evt.preventDefault();
$state.go(to.redirectTo, params)
}
});
}])
And adjust our state like this:
.state('root.page2.tab', {
url: '/:tabId',
templateUrl: 'tpl.page2.tab.html',
controller: 'Page2TabController',
redirectTo: 'root.page2.tab.subroute',
})
Check it here
There is a trick how to handle scenarios:
Parent should trigger some action in case that
it is accessed, or
its reached again, when navigating back from child in a parent state
In that case, we can use the "target (ui-view) for a child" as a place where sits the special view, with special controller. This will be
injected into that position once parent is created and
re-injected into that position again, once child is left. In that case, it will be re-init.
Enough explanation. There is a working plunker. There is adjusted state:
.state('root.page2', {
url: '/page2',
views: {
'content#root': {
templateUrl: './tpl.page2.html',
controller: 'Page2Controller'
},
'#root.page2': {
template: '<div></div>',
controller: 'RedirectorController'
}
}
})
So, now we can do some magic inside of our 'RedirectorController'
.controller('RedirectorController', ['$scope', '$state',
function($scope, $state) {
$state.go('root.page2.tab', { tabId: $scope.activeTabId });
}])
Check it in action here
Read more about what that new view/controller get from the other (Scope Inheritance by View Hierarchy Only) one here
Nested states or views for layout with leftbar in ui-router?
How do I share $scope data between states in angularjs ui-router?
I have the following setup in my code
.config(function config($stateProvider)
$stateProvider
.state('home', {
url : '/home',
views : {
'main' : {
controller : 'HomeCtrl',
templateUrl : 'home/home.tpl.html'
}
}
})
.state('home.details', {
url : '/details',
views : {
" " : {
template : "<h1>hello</h1>",
controller : function ($scope, $http, $state) {
//do some stuff here
//does not seem to reach code in here
}
}
}
});
})
.controller('HomeCtrl', function ($scope, $http, $state) {
//on a button click do $state.go('.details');
});
When I do this , the button click on my HomeCtrl seems to take me to /home/details but it does not seems to go inside the controller in that particular route at that point. (I checked by putting a break point inside the controller for the details.) Is there something wrong with my setup? I'm trying to do something similar to this sample app shown in the ui-router webpage.
The solution here would in a named-view (not) matching. Here is the working plunker.
We have to place the named ui-view inside of the parent view (or use more precise naming, see for example here)
So, the parent, home template should contain the named ui-view, e.g. nameOtherThanSpace
<div ui-view="nameOtherThanSpace" ></div>
And the child defintion must target that view, the complete snippet is:
$stateProvider
.state('home', {
url: '/home',
views: {
'main': {
controller: 'HomeCtrl',
template: '<div>' +
'<h1>hello from parent</h1>' +
'<hr />' +
'<div ui-view="nameOtherThanSpace" ></div>' +
'<div>',
}
}
})
.state('home.details', {
url: '/details',
views: {
"nameOtherThanSpace": {
template: "<h2>hello from a child</h3>",
controller: function($scope, $http, $state) {},
}
}
});
How to use more specific view names:
View Names - Relative vs. Absolute Names
UI-Router isnt rendering childs correctly with templateurl in Asp.net Mvc
The working plunker using the name nameOtherThanSpace, instead of " " (space)
Try registering your controller on the app instead of on your $stateProvider. e.g.
var app = angular.module('myApp', []);
app.controller('HomeCtrl', function ($scope, $http, $state) {
//on a button click do $state.go('.details');
});
Update 1:
You should only need to specify a view if you have multiple views in which case the view probably needs to have a name. But you only have one view for that state so I would just do this.
.state('home.details', {
url : '/details'
template : "<h1>hello</h1>",
controller : function ($scope, $http, $state) {
//do some stuff here
//does not seem to reach code in here
}
}
Before use angular-ui-router, one controller always support several router&views, such as:
$routeProvider.
when('/posts', {
templateUrl: 'views/posts/list.html'
}).
when('/posts/create', {
templateUrl: 'views/posts/create.html'
}).
all views for one object share one controller:
app.controller('PostsCtrl', function($scope) {
$scope.create = function() {
// ...
}
$scope.list = function() {
// ...
}
});
and init data in view:
<div data-ng-controller="PostsController" data-ng-init="list()">
...
</div>
But in angular-ui-router, we use state, so we need create several controller for each state, such as:
$stateProvider
.state('posts', {
abstract: true,
url: '/posts',
templateUrl: 'views/posts/list.html',
controller: 'PostsCtrl'
})
.state('posts.detail', {
url: '/:postId',
templateUrl: 'views/posts/detail.html',
controller: 'PostsDetailCtrl'
})
Separating controller seems not a good design pattern.
So, is there any better suggestion to structure controllers?
It is a little late for an answer, but as I was struggling with it and found an answer, I might as well post it.
I agree with Nate's comment that in most circumstances you should keep the controllers as small as possible, i.e. you should write a separate controller for every state. However, if you find yourselves in a situation that really think that using the same controller for both the parent and child views is preferable you can simply omit the controller option in the child view. This view will then use the state of the parent. This can be read in the documentation. A little example:
app.js
app.controller('testController', ['$scope', function($scope){
$scope.message= 'This comes from testController';
}]);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider ,$urlRouterProvider) {
$stateProvider
.state('test', {
url: '/test',
templateUrl: 'partials/test.html',
controller: 'testController'
})
.state('test.child', {
url: '/child',
templateUrl: 'partials/test2.html'
});
}]);
partials/test.html
<div>
Test.html: {{message}}
<div data-ui-view></div>
</div>
partials/test2.html
<div>
Test2.html: {{message}} as well.
</div>
This will produce the following (with url #/test/child)
Test.html: This comes from testController
Test2.html: This comes from testController as well.
I hope this helps anyone