Angular ui-router: child state controller not getting called - javascript

In my Angular app, I have the following route structure:
.state('mapping', {
url: '/mapping',
templateUrl: 'app/components/mapping/mapping.html',
controller: 'MapCtrl as map',
abstract: true,
authenticate: true
})
.state('mapping.all', {
url: '',
templateUrl: 'app/components/mapping/partials/all.html',
authenticate: true
})
.state('mapping.project', {
url: '/:projectName',
templateUrl: 'app/components/mapping/partials/project.html',
controller: 'ProjectCtrl as proj',
authenticate: true
})
When accessing the mapping state, mapping.all loads by default. It basically shows a list of projects which link to the mapping.project state, as such:
<a ui-sref="mapping.project({projectId: project.id, projectName: project.name})">...</a>
I want to call the ProjectCtrl when I access mapping.project in order to load the necessary data, but it never even gets loaded. In the snippet below which includes only relevant data, the alert message never pops up:
angular
.module('watera')
.controller('ProjectCtrl', Controller);
Controller.$inject = ['$stateParams', 'UserFactory', 'ProjectFactory'];
function Controller($stateParams, UserFactory, ProjectFactory) {
var proj = this;
activate();
function activate() {
alert('1');
}
}
The js file is correctly linked and the controller is named correctly. The <div ui-view></div> element is placed within mapping.html, as seen below:
<div id="ribbon" class="no-print">
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<strong>Mapping</strong>
<p class="text-muted">In this section...</p>
</div>
</div>
</div>
</div>
<div ui-view></div>
Why is this controller not loading?
EDIT: It gets weirder, I switched the structure to:
.state('mapping', {
url: '/mapping',
templateUrl: 'app/components/mapping/mapping.html',
abstract: true,
authenticate: true
})
.state('mapping.all', {
url: '',
templateUrl: 'app/components/mapping/partials/all.html',
controller: 'MapCtrl as map',
authenticate: true
})
.state('mapping.project', {
url: '/:projectName',
templateUrl: 'app/components/mapping/partials/project.html',
controller: 'ProjectCtrl as proj',
authenticate: true
})
And while MapCtrl keeps working correctly, ProjectCtrl still doesn't.

Found the issue... There were two instances of ProjectCtrl in this project:
Apparently, this causes some sort of silent conflict because there was absolutely no console error. Since the second instance was also linked on index.html, after the one that I wanted to load, it was probably simply replacing the correct version. Debugging nightmare.
Fixed now.

An abstract state cannot exist by definition so having a template URL for the parent and the children (which won't inherit the parent's templateUrl because the children define their own) is highly dubious IMO.
Im also wondering if there is trouble distinguising between ...
url: ''
AND
url: '/:projectName'
(Although it should handle the distinction).
However, to make sure I suggest a step-by-step approach. Kick off with the following mapping I suggest and then enhance it from there ...
.state('mapping', {
abstract: true,
authenticate: true
})
.state('mapping.all', {
url: '/',
templateUrl: 'app/components/mapping/partials/all.html',
controller: 'MapCtrl as map',
})
.state('mapping.project', {
url: '/project/:projectName',
templateUrl: 'app/components/mapping/partials/project.html',
controller: 'ProjectCtrl as proj',
})

Related

Nested states in Ui-Router with AngularJS

I would like to have a common state with all the common views like the header and the sidebar, and a template where I would like to load different views that can change when the state is changing.
I have an index HTML file like this:
...
<div ui-view="header"></div>
<div ui-view="sidebar"></div>
<div class="container">
<div class="wrapper">
<div ui-view="content"></div>
</div>
</div>
...
While the AngularJS config is something like this:
$stateProvider
.state('mainCommonState', {
url: '',
abstract: true,
views: {
header: {
templateUrl: 'app/common/header.html',
controller: 'headerCtrl',
controllerAs: 'vm'
},
sidebar: {
templateUrl: 'app/common/sidebar.html',
controller: 'sidebarCtrl',
controllerAs: 'vm'
},
content: {}
},
resolve: {
apiEnvironment: function ($q, environmentApiService) {
var deferred = $q.defer();
deferred.resolve(environmentApiService.getApiEnvironment());
return deferred.promise;
}
}
})
.state('first-page-content', {
url: '/first-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/first-page-content.html'
controller: 'firstPageCtrl',
controllerAs: 'vm'
}
}
})
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
.state('third-page-content', {
url: '/third-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/third-page-content.html'
controller: 'thirdPageCtrl',
controllerAs: 'vm'
}
}
})
For some reason this is not working: I have an empty view instead of the 3 templates that I would like to show in the content ui-view.
If I define a template (even a blank template) inside the the abstract state, the view that is always showing is the one inside the abstract state mainCommonState.
Where am I wrong?
1st Edit: UPDATE Following the first answer
Following the suggestion from Chris T, I have updated my code, but there still something missing.
I have created a Plunker so you can help me fixing the issues.
2nd Edit
Following the suggestions from Chris T, I have updated the code using the absolute path for the content view and now the contents are switching correctly.
I have updated the Plunker accordingly to that and introduced a new level of nesting view (tabs in the first page), and I would like to have the first tab active when the first page content is loaded.
If I follow these solutions and set empty the url of the first page and set it to the first tab instead, this is not working.
Any suggestions?
Your views are targeting the wrong named ui-view.
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
In this snippet, it targets the ui-view named content from the parent state which is mainCommonState. However, the content ui-view was not created in the mainCommonState. It was created in the root template.
Change your view declarations to target the view at the correct state, for example this targets the content view at the root state (which is named empty string):
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
'content#': {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
In ui-router 1.0 and higher you can also use absolute ui-view names by prefixing with an exclamation
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
'!content': {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
Read more about view targeting in the UI-Router views guide:
https://ui-router.github.io/guide/views#view-name-only

Problems with deep linking to URL with AngularJS ui-router

I've just encountered a problem which seems to have occurred due to me changing the folder structure of my app (but I think this is a "red herring"). I have a small AngularJS application and to tidy things up I moved one section of functionality to its own folder. I updated all <script> tag references, all view templateUrl values in my $stateProvider section... I don't get an 404 errors, all controllers and views are loaded but I have noticed that in my app I can't directly link to a specific URL (I could before). The URL I wish to directly/deep link to is http://myapp.com/an/membership
When I type this into the browser I get a GET http://myapp.com/an/membership/ 403 (Forbidden) error. The route has 4 child states / urls. I can deep link to all these. To make things worse if I have a link in my app (using ui-sref) I can link to my state / url with no problems... here is my state / routing code... I have added some comments to explain my problem...
/* This is the parent state of my membership state !! */
.state('sfm.in', {
abstract: true,
url: '/an/',
templateUrl: '/an/views/member-home/member-home-wrapper.html'
})
/* here the url is http://myapp.com/an/membership - I can link to it using ui-sref but can't deep link, I get a "403 forbidden", everything loads as expected (not sure if I need the abstract). */
.state('sfm.in.membership', {
url: 'membership',
templateUrl: '/an/membership/views/membership.html',
controller: 'MembershipCtrl',
abstract: true
})
/* this child state is a default and has the same URL as the parent - http://myapp.com/an/membership*/
.state('sfm.in.membership.advantages', {
url: '',
templateUrl: '/an/membership/views/membership-advantages.html'
})
/* No problem with deeplinking - http://myapp.com/an/membership/payment */
.state('sfm.in.membership.payment', {
url: '/payment',
controller: 'MembershipPaymentCtrl',
templateUrl: '/an/membership/views/membership-payment.html'
})
/* No problem with deeplinking http://myapp.com/an/membership/account */
.state('sfm.in.membership.account', {
url: '/account',
controller: 'MembershipAccountCtrl',
templateUrl: '/an/membership/views/membership-account.html'
})
/* No problem with deeplinking http://myapp.com/an/membership/data */
.state('sfm.in.membership.data', {
url: 'data',
controller: 'MembershipDataCtrl',
templateUrl: '/an/membership/views/membership-data.html'
});
I have correctly set up the $locationProvider.html5Mode in my app (as I can deeplink, type the url in the browser for other URLS).
Can anyone see a problem here? * UPDATE * I have added the parent state in the routing example, please see my comment from the first answer!
You forgot '/' in your first state:
.state('sfm.in.membership', {
url: '/membership',
templateUrl: '/an/membership/views/membership.html',
controller: 'MembershipCtrl',
abstract: true
})
This seems strange but I think the problem was the browser prepending the url when I type it in. I finally changed the code thus... but there is no real change here... I think the browser was the problem . In the meantime I changed the URL but cleaning the history / cache would have also solved the problem.
.state('sfm.in.membership', {
abstract: true,
url: 'member-ship',
templateUrl: '/an/membership/views/membership.html'
})
.state('sfm.in.membership.advantages', {
url: '',
templateUrl: '/an/membership/views/membership-advantages.html'
})
.state('sfm.in.membership.payment', {
url: '/payment',
controller: 'MembershipPaymentCtrl',
templateUrl: '/an/membership/views/membership-payment.html'
})
.state('sfm.in.membership.account', {
url: '/account',
controller: 'MembershipAccountCtrl',
templateUrl: '/an/membership/views/membership-account.html'
})
.state('sfm.in.membership.data', {
url: '/data',
controller: 'MembershipDataCtrl',
templateUrl: '/an/membership/views/membership-data.html'
})

ui-router accessing the child directly

I've got a nested view ui-router application, whenever I access the child with controller directly, the parent data is not filled (but is called, and I can see logs)
what is wrong?
.state('home.app', {
url: '/apps/:package_name',
abstract: true,
templateUrl: 'app/components/apps/details/app_header.html',
controller: 'AppController',
controllerAs: 'appCtrl'
})
.state('home.app.details', {
url: '/', templateUrl: 'app/components/apps/details/app.html'
})
.state('home.app.images', {
url: '/images', templateUrl: 'app/components/apps/details/images.html',
controller: 'ImagesController',
controllerAs: 'imagesCtrl',
resolve: {
package_name: function($stateParams: IStateParamsService) {
return $stateParams.package_name;
}
}
})
when I access the default child (details child) the parent loads and everything is ok (view is filled with data) but no luck with images, the AppController is being called but the view is not filled
if I'm not allowed to have such structure, what is my alternative? I have some shared data, and some specific data that should load on child states (tho I can load them all in parent controller but I guess that is not the way to go, since parent controller should not load submodels)

angularjs ui router : nested views and nested states

I'm new to angular and I'm trying to understand nested views concept.
Based on the example provided in their documentation: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
//home.html
<body>
<div ui-view="header"></div>
<div ui-view="settings"></div>
<div ui-view="content"></div>
</body>
I have settings.html which has a check box. If it's checked it will load in the view(not named) the advanced settings template if not it will load the basic template
//settings.html
<input type="checkbox" ng-change="change()" ng-model="advancedSettings" />
<div ui-view></div>
so far I have defined something like this:
$stateProvider
.state('home', {
views: {
'header': {},
'settings': {
templateUrl: 'settings.html'
},
'content': {},
}
})
since I have 2 templates basicSettings.html and advancedSettings.html that I need to load in the view from settings.html based on that checkbox, I thought I have to declare something like this:
.state('settings#home.basic',(){
templateUrl: 'basicSettings.html'
});
but it's not working, instead I receive a lot of errors on console. How is the best way to implement this, without removing names from homepage views(header,settings,content), also how do I change the view based on the check box?
Thanks
There is a working plunker
Solution here could be with states defined like this:
$stateProvider
.state('home', {
abstract: true,
url: "/home",
views: {
'header': {
template: "This is HEADER"
},
'settings': {
templateUrl: 'settings.html',
controller: 'HomeCtrl',
},
'content': {
template: "This is CONTENT"
},
}
})
.state('home.basic', {
url: "/basic",
templateUrl: 'basicSettings.html'
})
.state('home.advanced', {
url: "/advanced",
templateUrl: 'advancedSettings.html'
})
we have parent state "home" and two children. These are triggered on change by 'HomeCtrl', e.g. like this:
.controller('HomeCtrl', ['$scope', '$state',
function($scope, $state) {
$scope.advancedSettings = false;
$scope.change = function(){
var childState = $scope.advancedSettings
? "home.advanced"
: "home.basic";
$state.go(childState);
}
}])
So, based on the setting, the view target "settings" and its ui-view="" (unnamed one) is filled with a child state - basic or advanced
Check it here

How to remove Ionic Framework tabs bar+ buttons and simplify routing?

I have an existing app with tabs bar and buttons at the bottom.
I hope to remove tabs completely and simplify the routing. Is it possible?
Im not looking for css or ionic appearence changes like suggested here. This merely hides the bar but routing isnt simplified.
My current routing:
.state('tab', {
url: '/tab', //anyway to remove this?
abstract: true,
templateUrl: 'templates/tabs.html'
})
.state('login', {
url: '/login',
templateUrl: 'templates/login.html',
controller: 'AuthCtrl'
})
.state('tab.posts', { //ideally no more tabs.something
url: '/posts',
views: {
'tab-posts': {
templateUrl: 'templates/tab-posts.html',
controller: 'PostsCtrl'}}
})
.state('tab.newpost', {
url: '/newpost',
views: {
'tab-posts': {
templateUrl: 'templates/tab-newpost.html',
controller: 'NavCtrl'}}
})
.state('tab.posts.view', {
url: '/posts/:postId',
views: {
'tab-posts#tab': {
templateUrl: 'templates/tab-showpost.html',
controller: 'PostViewCtrl'}}
})
I hope to simplify routing by removing the tab abstract state. Cos right now, it can get rather confusing with the view names and all.
I removed tabs state but app returned blank. Same for deleting the html file URL line.
When I revamped the naming convention, the program no longer works (blank screen) but yet devtools doesnt throw any errors.

Categories

Resources