ui-router: Integrating with existing controllers - angularjs

I am in the process of converting my current angular project to use ui-router and I am a little confused. The documentation states I add my controller as such:
$stateProvider.state('contacts.detail', {
url: '/contacts/:contactId',
controller: function($stateParams){
$stateParams.contactId //*** Exists! ***//
}
})
I have defined my old controller in this manner:
xap.controller('DemoCtrl', [$scope, function ($scope, demoService) {
})
where xap is defined as:
var xap = angular.module({ .... })
What is the correct integration method?
Thanks

You can refer to a pre-registered controller by name:
$stateProvider.state('contacts.detail', {
url: '/contacts/:contactId',
controller: 'DemoCtrl'
});
You can add the $stateParams dependency to your controller to access parameters:
xap.controller('DemoCtrl', [
'$scope',
'$stateParams',
'demoService',
function ($scope, $stateParams, demoService) {
$stateParams.contactId //*** Exists! ***//
}
]);
But you can also inline your controllers and therefore not have to come up with unique names for each controller for every state:
$stateProvider.state('contacts.detail', {
url: '/contacts/:contactId',
controller: [
'$scope',
'$stateParams',
'demoService',
function ($scope, $stateParams, demoService) {
$stateParams.contactId //*** Exists! ***//
}
]
});

The controller in the state is not a resolve field.
In the state, you have to put only controller name because when you declare it, it's "injected" into your angular module.
So, you have to put controller name like this :
$stateProvider.state('contacts.detail', {
url: '/contacts/:contactId',
controller: 'ContactsCtrl'
});
If you want to inject some variable, you can add an object in the state like this :
$stateProvider.state('contacts.detail', {
url: '/contacts/:contactId',
controller: 'ContactsCtrl',
myVar: function(...){
return '...';
}
});
So, if you put a function, it's for a resolve field and not for controllers... You can implement it into state but it's better to do it outside state declaration.

Related

Inject a resolve provider from child state to controller

So i'm trying to pull some id from the url parameters.
Here are my states :
$stateProvider
.state('parent', {
url: '/parent',
templateUrl: 'path/to/parent.html',
controller: 'lolController'
})
.state('parent.child', {
url: '/child-profile/:id',
templateUrl: 'path/to/child.html',
resolve: {
someId: function ($stateParams) {
// I cant print the id from here
console.log("PARAMS", $stateParams.id)
return $stateParams.id;
}
},
})
Controller
.controller('lolController',
['$scope', 'someId', function ($scope, someId) {
$scope.someId = someId;
}])
But whenever i'm trying to access the url /parent/child-profile/123abc i'm getting the error Unknown provider: someId See error here..
How do I fix this? Thanks.
EDIT
The answer provided by Jay Shukla helped me get this idea.
The parameter is undefined because I declared the controller on the parent state before actually calling the child state which contains the value from it's url. Here's a simple solution I came up with, with Jay Shukla's help.
$stateProvider
.state('parent', {
url: '/parent',
templateUrl: 'path/to/parent.html',
})
.state('parent.child', {
url: '/child-profile/:id',
templateUrl: 'path/to/child.html',
controller: 'lolController'
})
I removed the controller declaration from the parent state and moved it to the child state. Since in my situation the parent state's template only contains a <div ui-view></div>.
The idea is :
Only state is nested but both html are separate so controller models will not be inherited - Jay Shukla
Each state can have their own controller.
Please add/edit more to improve this question.
Try to inject $stateParams then you will get id in that object.
Like this
.controller('lolController',
['$scope', '$stateParams', function ($scope, $stateParams) {
$scope.someId = $stateParams.id;
}])
You can also defined your parameters in different ways as below
url: '/child-profile/:id', // Inside stateparams you will get id as key
OR
url: '/child-profile/{id}',
OR
url: '/child-profile/:{id:int}', // Id with integer value

How to setup a parent/child with a resolve and nested states in ui-router?

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&param2&param3&param4',
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&param6&param7&param8',
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&param2&param3&param4',
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?

Accessing ui-router state controller's data in data section

I have the state configuration defined in as follows:
angular.module('app', ['ui.router')
.config(function ($stateProvider, $urlRouterProvider) {
.state('element', {
url: '/element',
templateUrl: 'element.html',
controller: 'ElementCtrl',
controllerAs:'ec',
data: {
title: 'Element {{ec.name}}',
}
})
.state('element.detail', {
url: '/detail',
templateUrl: 'detail.html',
controller: 'HomeCtrl',
controllerAs:'hc',
data: {
title: 'Detail {{hc.age}}',
}
})
.controller('ElementCtrl', function ($scope, $stateParams) {
this.name = "myName";
})
.controller('HomeCtrl', function ($scope, $stateParams) {
this.age = "myName22";
});
Here, I want to dynamically determine the data.title field based on the value assigned to the variable in the controller.
Is there a way to do this?(It is better if I do this before the state is activated).
It is not possible. See more here:
Accessing parameters in custom data
there is NO way how to set these data dynamically. E.g. based on the $stateParams. These settings are expected to be defined in the .config() phase, not evaluated in .run()(in compariosn with others like resolve, templateUrl...)
The data : {} is intended as a static setting, defined in a config phase.
The way to go is to use resolve : {}:
Resolve
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.

$stateProvider params null with ionic

I can't retrieve parameters passed from ui-router in Ionic.
Parameters passed into the Controller are undefined
This is my state code:
.state('app.dayliston', {
cache: false,
url: '/myurl',
views: {
'mainContent': {
templateUrl: 'calendar/daylist.html',
controller: 'MyCtrl',
params : { 'mode':'online'}
}
}
})
and here is My Controller code:
.controller('MyCtrl', function($scope,$state, $stateParams,CalendarFactory,FBFactory, $ionicHistory,$ionicScrollDelegate,$ionicModal,$ionicPopup, $timeout) {
console.log('MyCtrl')
console.log('mode'+$stateParams.mode) // mode is undefined
....
})
I'm using 1.6.1. Is there anything wrong with my code?
As I can see in your code, you dont need to use $stateParams because you don't get the "mode" parameter from the URL.
I think attached data in state will be a better choice (Docs):
.state('app.dayliston', {
cache: false,
url: '/myurl',
data:{
mode: 'online'
},
views: {
'mainContent': {
templateUrl: 'calendar/daylist.html',
controller: 'MyCtrl'
}
}
})
Then you can get the data stored in state like this:
.controller('MyCtrl', function($scope, $state, $stateParams, CalendarFactory, FBFactory, $ionicHistory, $ionicScrollDelegate, $ionicModal, $ionicPopup, $timeout) {
console.log('MyCtrl')
console.log('mode'+$state.current.data.mode) // "online"
})
MyCtrl is the actual name of your controller. It's not a parameter that's passed to the controller per se.
Route:
.state('app.dayliston', {
cache: false,
url: '/myurl/:mode',
views: {
'mainContent': {
templateUrl: 'calendar/daylist.html',
controller: 'MyCtrl'
}
}
})
Check URL Routing Query Parameters doc
Link from view:
<a ui-sref="app.dayliston({mode: 'online'});">Go to dayliston</a>
Go to state/route from controller:
$state.go('app.dayliston', {mode: 'online'});
you are passing the $stateParams incorrectly
https://github.com/angular-ui/ui-router/wiki/URL-Routing#stateparams-service
The should be on the url or you can pass data in using the resolve map on the state.
https://github.com/angular-ui/ui-router/wiki#resolve
also passing in custom data might be a better approach? Hard to tell from the code sample you provided

Resolving state in AngularJS

I need to get some content data to my controller:
state('admin.businesses.employees.all', {
resolve: {
executorsListTitle: 'All employees',
executorsEmptyListMessage: 'Add the first employee'
},
url: '/all',
controller: 'ExecutorsController',
templateUrl: 'templates/admin/executors/index.html'
})
And a controller code:
module.controller(
'ExecutorsController',
[
'$scope', '$rootScope', '$state',
'$stateParams', '$modal', 'executorsListTitle',
'executorsEmptyListMessage', 'Executor',
function($scope, $rootScope, $state, $stateParams, $modal, executorsListTitle, executorsEmptyListMessage, Executor) {
// Some code
}
)
But when I try to get into this state I can't do it - click by button makes nothing; if I remove resolve from state description it works good. What I do wrong? Thanks!
The resolve of the state machine expects a key and a factory. The doc states:
key – {string}: a name of a dependency to be injected into the controller.
factory - {string|function}
And when you provide a string to the factory:
If string, then it is an alias for a service.
If you want to return a string, you can do the following:
state('admin.businesses.employees.all', {
resolve: {
executorsListTitle: function() {
return 'All employees';
},
executorsEmptyListMessage: function() {
return 'Add the first employee';
},
},
url: '/all',
controller: 'ExecutorsController',
templateUrl: 'templates/admin/executors/index.html'
})
If you are using static data (the strings you are resolving to), you can also use the custom data property:
state('admin.businesses.employees.all', {
data: {
executorsListTitle: 'All employees',
executorsEmptyListMessage: 'Add the first employee'
},
url: '/all',
controller: 'ExecutorsController',
templateUrl: 'templates/admin/executors/index.html'
})
If you use this method, in the controller you can access the data like this:
$state.current.data.executorsListTitle
You can use strings directly with this method. Here is the doc for the custom data property.

Resources