child state controller not being reached in angular-ui-router - angularjs

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
}
}

Related

How can I check the AngularJS controller from within the template?

I'm using Angular's UI-Router as shown below to do URL-routing in my web-app as shown below:
$stateProvider.state('myState1', {
url: '/state1',
templateUrl: 'state1.html',
controller: 'MyState1Ctrl'
});
$stateProvider.state('myState2', {
url: '/state2',
templateUrl: 'state2.html',
controller: 'MyState2Ctrl'
});
Within each of the two template files state1.html, state2.html, I have my navigation bar directive: <myapp-navigation-bar></myapp-navigation-bar>
But I want the navigation bar to behave differently based on weather it is in myState1 or myState2. How can I detect from within the controller of the navigation bar (or its template) which state it is in?
According to the documentation, You can do this,
.controller('MyState1Ctrl', function($scope, $state) {
$scope.statecurrent = $state.current;
});
You can also check like this -
.controller('MyState1Ctrl', function($scope, $state) {
if ($state.is("myState1")) {
//Add Logic
}
});

angular ui-router state how is controller being called

I have see people post things online about using Angular ui-router and i see these types are code that do not explicitly even show an controller name, so how is it even suppose to know how to call the controller?
state('new-rp', {
url: '/new/:portfolioId',
templateUrl: 'new.html',
controller: function($scope, $stateParams) {
$scope.portfolioId = $stateParams.portfolioId;
}
})
Code of mine that I TRIED that does NOT list the controller like above
.state("deviceDetail", {
url: "/devices/:DeviceId", // param is required which specific device id
templateUrl: "app/devices/deviceDetailView.html", // ui elements
controller: function($scope,$stateParams) {
$scope.DeviceId = $stateParams.DeviceId;
}
});
Problem is the controller is NOT hit
But this code explicitly uses the controller name and the controller gets hit, thus i'm having trouble with how this type of code would hit a controller
controller: function($scope,$stateParams) {
$scope.DeviceId = $stateParams.DeviceId;
}
HOW does it know? (doesn't work for me )
You can declare controllers in two ways in UI router states:
//1
.state('new-rp', {
url: '/new/:portfolioId',
templateUrl: 'new.html',
controller: function($scope, $stateParams) {
$scope.portfolioId = $stateParams.portfolioId;
}
})
//2
.state('new-rp', {
url: '/new/:portfolioId',
templateUrl: 'new.html',
controller: PortfolioController
})
.
.
.
//PortfolioController definition
In method #1, your controller is created for you inline, while in #2, UI Router is looking for an explicitly declared controller called PortfolioController

Angular ui.router. Deep nested routes

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?

Controller loaded twice using ui-router + custom directive

I am trying to bring to my homepage a custom directive which will print me some output.
In the network tab in my devtools I just saw that my controller loads twice.
controller:
var homeController = function($log,leaguesFactory){
var self = this;
self.leagues = [];
leaguesFactory.loadLeagues()
.then(function(leagues){
self.leagues = leagues.data.Competition;
});
self.message= 'test message';
};
directive:
var leaguesTabs = function(){
return {
restrict : 'E',
templateUrl : 'app/home/leagues-tabs.tpl.php',
scope: {
leagues: '='
},
controller: 'homeController',
controllerAs: 'homeCtrl'
};
};
ui-router states:
$stateProvider
.state('home',{
url : '/',
templateUrl : 'app/home/home.tpl.php',
controller : 'homeController',
controllerAs: 'homeCtrl'
})...
I just want to use my homeCtrl in the directive, but it seems that the state provider loads it also and make it load twice. If I remove the controller from the directive then I don't get access to the homeCtrl, if I remove the homeCtrl from the stateprovider than I don't have access in the home.tpl.php
home.tpl.php:
<div>
<leagues-tabs></leagues-tabs>
</div>
any idea?
Actually problem related to next steps:
ui-router start handling url '/'
ui-router create an instance of 'homeController'
ui-router render the view 'app/home/home.tpl.php'
Angular see usage a custom directive - 'leagues-tabs'
'leagues-tabs' directive create an instance of 'homeController'
'leagues-tabs' render the view 'app/home/home.tpl.php'
You can follow any of next possible solutions:
Change controller for 'leagues-tabs' to something special
Remove controller usage from ui-router state definition
You can try this one http://plnkr.co/edit/LG7Wn5OGFrAzIssBFnEE?p=preview
App
var app = angular.module('app', ['ui.router', 'leagueTabs']);
UI Router
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/leagues');
$stateProvider
.state('leagues', {
url: '/leagues',
templateUrl: 'partial-leagues.html',
controller: 'LeaguesController',
controllerAs: 'ctrl'
});
}]);
Controller
app.controller('LeaguesController', ['$http', function($http) {
var self = this;
$http.get('leagues.json').success(function(data){
self.leagues = data;
})
}]);
View
<div>
<league-tabs leagues="ctrl.leagues"></league-tabs>
</div>
Directive
var leagueTabs = angular.module('leagueTabs', []);
leagueTabs.directive('leagueTabs', function(){
return {
restrict : 'E',
templateUrl : 'partial-league-tabs.html',
scope: {
leagues: '='
},
controller: 'LeagueTabsController',
controllerAs: 'leagueTabs'
}
});
leagueTabs.controller('LeagueTabsController', function($scope){
var self = this
$scope.$watch('leagues', function(leagues){
self.leagues = leagues;
})
})
Directive View
<div>
<ul ng-repeat="league in leagueTabs.leagues">
<li>{{league.name}}</li>
</ul>
</div>

Controller on nested state not getting executed

I have nested states, with the parent and child state having a separate controller. But only the parent state is getting executed.
I have the url structure: #/restaurant/2/our-food
So, I want it to load the restaurant with the ID 2 and then the child controller load the 'our-food' content and take care of some other functions.
My code is:
var app = angular.module("app", ['ui.router']);
app.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('home', {
url: '/',
controller: function($scope, $stateParams) {
$scope.setRestaurant(0);
}
})
.state('restaurant', {
url: '/restaurant/:restaurantId',
controller: function($scope, $stateParams, $state) {
console.log('first');
if($stateParams.restaurantId > 0) {
$scope.setRestaurant($stateParams.restaurantId);
}
else {
$state.go('home');
}
}
})
.state('restaurant.our-food', {
url: '/our-food',
templateUrl: 'templates/our-food.html',
controller: function() {
console.log('second');
}
});
});
The controller for your 'restaurant.our-food' state is not being executed because its parent state has no template. This means there is no ui-view directive for it to attach its own template and controller. Even if your parent directive doesn't do anything other than setup some state, it needs to provide at the very least a minimal template.
Try adding the following to your 'restaurant' state and see if that makes it work for you:
template: "<div ui-view />"
This is documented in the ui-router docs:
Remember: Abstract states still need their own for their children to plug into. So if you are using an abstract state just to
prepend a url, set resolves/data, or run an onEnter/Exit function,
then you'll additionally need to set template: <ui-view />'.
https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views

Resources