In the route definition below if I go to #/systemadmin/edit/Testing it brings up the SystemAdminController but not the one defined in the child route I am using. I am missing something.
$stateProvider.state('systemadmin', { url: '/systemadmin', controller: 'SystemAdminController', templateUrl: 'app/templates/SystemAdmin.html?v=' + dl.buildDate })
.state('systemadmin.edituser', { url: '/edit/:selectedUser', controller: function ($scope, $stateParams) { debugger; }, templateUrl: 'app/templates/SystemAdmin.html?v=' + dl.buildDate });
A couple of things:
Your child state controller is missing a $scope, all controllers need an $scope in angular.
When you go to edituser, the systemadmin controller will also execute, as well as the edituser controller.
EDIT
Also, you need to define your parameter with curly braces in your route definitions, not with colons, that's ng-route syntax not ui.router:
{ url: '/edit/{selectedUser}' }
Another thing which is suspect you may be missing out, as I have many times, is that the view of your parent state needs to have a ui-view itself, see this working plunk.
Related
Edit: Here is the complete code at Plunker. Though I can not c anything in execution but same code working at local. However gives a console error though
It all works perfect. But due to :id in /news/:id/, i am getting jquery/angular errors in console which can not be tracked anywhere in my code
I can not c What i am doing wrong.
Edit: Solved plunker https://plnkr.co/edit/FWcuBgGpVdMj3CroFrYJ
First of all you are trying to use ui-router but you're including ngRoute script in your plunker. Change it to
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.min.js"></script>
Then everything should work fine!
I suggest you a few changes...
1. Use ui-sref instead of href because it's much easier to define
ui-sref="post({id:1})" which turns into href="#/news/1"
If you would like to change url some day, then you will have to just change your route file, not each href.
$stateProvider
.state('post', {
url: "news/:id"
or
$stateProvider
.state('post', {
url: "archive/:id"
or
$stateProvider
.state('post', {
url: "whatever/:id"
2. Use abstract state
In your example it's a way better to define abstract state which holds header, content and footer - it's a typical use case.
ui-router
Abstract States
An abstract state can have child states but can not get activated
itself. An 'abstract' state is simply a state that can't be
transitioned to. It is activated implicitly when one of its
descendants are activated.
Some examples of how you might use an abstract state are:
To prepend a url to all child state urls. To insert a template with
its own ui-view(s) that its child states will populate. Optionally
assign a controller to the template. The controller must pair to a
template. Additionally, inherit $scope objects down to children, just
understand that this happens via the view hierarchy, not the state
hierarchy. To provide resolved dependencies via resolve for use by
child states. To provide inherited custom data via data for use by
child states or an event listener. To run an onEnter or onExit
function that may modify the application in someway. Any combination
of the above. 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:
"".
Here's a plunker which shows how I would do it.
https://plnkr.co/edit/5FvJaelyxdl5MuALt5VY?p=preview
Hope it helps.
Look at the documentation for ui router named views,
You can use following syntax for using multiple views
$stateProvider
.state('state',{
url: '',
views: {
'header': {
templateUrl: 'views/header.html',
controller: 'headerCtrl'
},
'content': {
template: '<div ui-view=" "></div>', //<-- child templates loaded to here
},
'footer': {
templateUrl: 'views/footer.html',
controller: 'footerCtrl'
}
}
})
.state('state.post', {
url: 'news/:id/:KeyWords'
templateUrl: 'views/post.html' //<-- This goes into content's ui-view
});
I'm guessing you want to keep the header and footer and change content views.
You can achieve this by making this state as parent to all other states
suppose
.state('main',{
abstract: true,
views: {
'header': ... ,
'content': {
template: '<ui-view></ui-view>',
}
'footer': ...
}
})
then all the child views will load their views in the ,
ex: in main.child etc, your template will load in the content's <ui-view></ui-view> tag
If you need to use a custom template depending on keywords you can do the following:
.config(['$routeProvider',
function($routeProvider, $routeParams) {
$routeProvider
.when('/news/:id/:keyWords', {
template: '<div ng-include="url"></div>',
controller: "exampleController"
})
then in the exampleController
function($routeParams, $scope) {
$scope.url = $routeParams.keyWords;
}
I have this code:
var home = {
name: 'home',
template: '<div data-ui-view></div>',
url: '/',
templateUrl: 'app/access/partials/home.html',
controller: ['accessService', function (accessService: IAccessService) {
this.ac = accessService;
}],
controllerAs: 'home'
};
var homeAccess = {
name: 'home.access',
url: 'Access',
templateUrl: 'app/access/partials/webapi.html',
controller: ['accessService', function (accessService: IAccessService) {
this.ac = accessService;
}],
controllerAs: 'homeAccess',
resolve: {
abc: ['accessService', function (accessService) {
return accessService.getAbc();
}],
def: ['accessService', function (accessService) {
return accessService.getDef();
}]
}
};
Now that I am using controllerAs is there a way that I can simplify this code so as to eliminate adding the accessService into both of the controllers and into the two parts of the resolve? Also if I did this then how could I get to the access service inside the home.html and also the webapi.html?
There are probably a few different ways, just thinking out loud here.
Your home state is the parent of home.access and it uses a ui-view to show the child state. As such the template for the child state can reference the controller in the parent state. This is just the regular inheritance of $scope in Angular views, although it's much cleaner b/c you are using the controllerAs syntax.
For example, your views might end up looking like this:
<home-template>
<p>{{home.someValue}}</p>
<!-- included by the ui-view -->
<home-access-template>
<p>{{homeAccess.anotherValue}}
<!-- this works b/c home is on the parent scope -->
<p>{{home.someOtherValue}}</p>
</home-access-template>
<home-template>
So if it makes sense in your scenario, you only need to inject your accessService into the parent controller. The child views will use the service through methods of the parent controller.
A similar thing can also be done with the resolves: by declaring them on the parent state, they are available to the child states. This is more useful when there are many child states for a given parent.
I want to make tabs with tab-content.
tab-content has it's own view.
Here is code sample
(function () {
angular
.module('infirma.konfiguracja', ['ui.router'])
.config(routeConfig)
;
routeConfig.$inject = ['$stateProvider'];
function routeConfig($stateProvider) {
$stateProvider
.state('app.konfiguracja', {
url: 'konfiguracja/',
views: {
'page#app': {
templateUrl: 'app/konfiguracja/lista.html',
controller: 'konfiguracjaListaCtrl',
controllerAs: 'vm'
}
},
ncyBreadcrumb: {label: "Ustawienia systemu"}
})
.state('app.konfiguracja.dzial', {
url: '{dzial:.*}/',
views: {
'dzial#app.konfiguracja': {
templateUrl: 'app/konfiguracja/dzial.html',
controller: 'konfiguracjaDzialCtrl',
controllerAs: 'vm'
}
},
ncyBreadcrumb: {label: "{{vm.nazwaDzialu}}"}
})
;
}
})();
I want to mark selected tab which is in parent state (app.konfiguracja).
Problem is that when entering url like /konfiguracja/firmy/ there is no $stateParams.dzial in app.konfiguracja controller
How to fix it?
I created working example for your scenario here. I would say, that there at least two ways.
The first, general way, how we should use the UI-Router and its selected params in parent views (to mark selected tab/link), should be with a directive **ui-sref-active**:
ui-sref-active="cssClassToBeUsedForSelected"
So this could be the usage:
<a ui-sref="app.konfiguracja.dzial({dzial: item.id})"
ui-sref-active="selected"
>{{item.name}}</a>
The second approach (my preferred) would be to use a reference Model, created in parent $scope, and filled in a child:
.controller('konfiguracjaListaCtrl', ['$scope', function ($scope, )
{
$scope.Model = {};
}])
.controller('konfiguracjaDzialCtrl', ['$scope', function ($scope)
{
$scope.Model.dzial = $scope.$stateParams.dzial;
// we should be nice guys and clean after selves
$scope.$on("$destroy", function(){ $scope.Model.dzial = null });
}])
usage could be then like this
<span ng-if="item.id == Model.dzial">This is selected</span>
How is the second approach working? check the DOC:
Scope Inheritance by View Hierarchy Only
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).
It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.
Check that all in action here
I am trying to implement a tabbed interface akin to this: http://odetocode.com/blogs/scott/archive/2014/04/14/deep-linking-a-tabbed-ui-with-angularjs.aspx
However, on my state change, the controller of the parent state seems to be reinitialized (or a new $scope is created?)
There are two major differences between the example plunkr and my project.
I use a parameter in my url
I resolve different data on the state change for each tab (removing this does nothing).
I am not using ui-bootstrap for the tabs but am triggering a $state.go on ng-click of the tab.
I experimented with the above plunkr and added a dropdown to the parent state; however the parent dropdown values seem to persist when the child states change. I am not too concerned with the child states and will probably end up using sticky states anyways.
I am using wondering if I am doing something fundamentally wrong before I try and add another package to my project.
here is a rough plunkr of what I am trying to do: http://plnkr.co/edit/TmRQN5K8OEc8vHG84G5z?p=preview
here is my config:
app.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.when('/main',
function ($state) {
$state.go('parent.tab1', { main_id: '00008' });
});
$stateProvider
//Handle States Here
.state('parent', {
abstract: true,
url: '/parent?main_id',
templateUrl: "main.html",
controller: 'Main_Controller',
resolve: {
//Calls to API
}
})
.state('parent.tab1', {
url: "/applications",
templateUrl: "tab1.html",
controller:'Tab1Ctrl',
resolve: {
//Get some different data from an API
},
})
.state('parent.tab2', {
url: "/phasing",
templateUrl: "tab2.html",
controller: 'Tab2Ctrl',
resolve: {
//More API Data
}
});
});
I've made your plunker working here
$urlRouterProvider
//.when('/main',
.when('',
function ($state) {
$state.go('parent.tab1', { main_id: '00008' })
});
Also there is a change in main.html, which does not use ng-controller any more. We just have to pass the proper Controller name
$stateProvider
//Handle States Here
.state('parent', {
abstract: true,
url: '/parent?main_id',
templateUrl: "main.html",
controller: 'MainController',
resolve: {
//Calls to API
}
})
...
// MainController
// these two names should fit
app.controller("MainController", function($rootScope, $scope, $state) {
So now, it is working, and let's discuss
I use a parameter in my url
I resolve different data on the state change for each tab (removing this does nothing).
I am not using ui-bootstrap for the tabs but am triggering a $state.go on ng-click of the tab.
Quick answers:
parameter in url exists, e.g. #/parent/tab1?main_id=8000
resolve is trigerred for each controller if controller is reinstantiated. That happens when we navigate to that state (among tabs)
no need to use $state.go, I used:
a snippet:
<a ui-sref="parent.tab1({main_id:'00008'})"> go to tab1 with main_id '00008'</a><br />
<a ui-sref="parent.tab2({main_id:'00008'})"> go to tab2 with main_id '00008'</a><br />
<a ui-sref="parent.tab3({main_id:'00008'})"> go to tab3 with main_id '00008'</a><br />
Check it here
Based on these set-ups (Angular UI-Router testing scope inheritance, Angular ui-router - how to access parameters in nested, named view, passed from the parent template?), I did the following (the third holds the relevant issue):
.state("patients", {
url: "/dashboard/patients",
templateUrl: 'patients/index.html',
controller: "patientCtrl"
})
.state("sharedPatients", {
url: "/dashboard/patients/shared",
templateUrl: 'patients/shared_patients.html',
controller: "patientCtrl"
})
.state('showPatient', {
url: "/dashboard/patients/:id",
templateUrl: 'patients/show.html',
controller: ("patientCtrl", ['$scope', '$stateParams', function($scope, $stateParams) {
$scope.patient_id = $stateParams.id;
}])
})
Patients and sharedPatients work without a problem. I can also go to showPatient and access the variable patient_id. However, I cannot access any of the functions or variables established in patientCtrl. Thoughts?
The controller scope inheritance has nothing to do with the state inheritance.
Your controllers only inherit from each other if their views are nested in the DOM.
Also, the syntax you're using there is misleading. controller: ("patientCtrl", [...]) will just ignore that first part. It'll only use the controller inside the array.