A parameter of ui-router in controller and resolve - javascript

I have configured the following ui-router.
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('global.editor', {
url: '/posts/editor/{id}',
templateUrl: '/htmls/editor.html',
controller: 'EditorCtrl',
resolve: {
post: ['$stateParams', 'codeService', function ($stateParams, codeService) {
return codeService.getPost($stateParams.id)
}]
}
}
.state('global.new', {
url: '/new',
templateUrl: '/htmls/editor.html',
controller: 'EditorCtrl'
})
.state('global.newTRUE', {
url: '/newTRUE',
templateUrl: '/htmls/editor.html',
controller: 'EditorCtrl'
})
.state('global.editor.panels', {
controller: 'PanelsCtrl',
params: { layout: 'horizontal' },
templateUrl: function (params) { return "/htmls/" + params.layout + '.html' }
}
}])
app.controller('EditorCtrl', ['$scope', '$state', function ($scope, $state) {
$scope.layout = "horizontal";
$scope.$watch('layout', function () {
$state.go('global.editor.panels', { layout: $scope.layout });
});
}]);
As a result, https://localhost:3000/#/new in a browser leads to (the state global.editor, then to) the state global.editor.panels.
Now, I want to add a parameter connected:
I don't want it to be shown in the url
https://localhost:3000/#/new in a browser makes connected to be false, and https://localhost:3000/#/newTRUE in a browser makes connected to be true
connected can be past into the controller EditorCtrl and PanelsCtrl
connected can be available in the resolve of global.editor; ideally, we could resolve different objects according to the value of connected.
Does anyone know how to accomplish this?

You can add resolve for new and newTRUE:
.state('global.new', {
url: '/new',
templateUrl: '/htmls/editor.html',
resolve: {
isConnected: function() {
return false;
}
},
controller: 'EditorCtrl'
})
.state('global.newTRUE', {
url: '/newTRUE',
templateUrl: '/htmls/editor.html',
resolve: {
isConnected: function() {
return true;
}
},
controller: 'EditorCtrl'
})
And in EditorCtrl (or PanelsCtrl) you can use it like:
app.controller('EditorCtrl', ['$scope', '$state', 'isConnected', function($scope, $state, isConnected) {
console.log("connected : " + isConnected); // you can save this value in Service and use it later.
...
}]);

You can use classic approach - in resolve
Or you can use hidden parameters from angular ui router.
Define params : {isConnected' : null} in your parent global state.
In:
global.newTRUE - set value in $state config
global.new - set value in $state config
global.editor.panels - set parameters in transition/go or ui-sref
definition is like this:
$stateProvider
.state('global.newTRUE', {
url : '/:newTRUE',
params : {
'isConnected' : false
}
});
}
and in controller you get in from $stateParams.
Problem with this approach is hidden parameters are loses in refresh page, except if is set default value

You can surely use the params of UI-Router states' config to not show it in URL and achieve all mentioned points.
Also, as per #2, you need connected to be false for /new and true for /newTRUE. We can do so by passing true or false as default value for those states. Something like this:
$stateProvider
.state('global.editor', {
url: '/posts/editor/{id}',
templateUrl: '/htmls/editor.html',
params: { connected: null },
controller: 'EditorCtrl',
resolve: {
post: ['$stateParams', 'codeService', function ($stateParams, codeService) {
return codeService.getPost($stateParams.id)
}]
}
}
.state('global.new', {
url: '/new',
templateUrl: '/htmls/editor.html',
params: { connected: false }, // default false for /new
controller: 'EditorCtrl'
})
.state('global.newTRUE', {
url: '/newTRUE',
templateUrl: '/htmls/editor.html',
params: { connected: true }, // default true for /newTRUE
controller: 'EditorCtrl'
})
.state('global.editor.panels', {
controller: 'PanelsCtrl',
params: { layout: 'horizontal', connected: null },
templateUrl: function (params) { return "/htmls/" + params.layout + '.html' }
}
For #3, In order to access connected in your controllers (EditorCtrl and PanelsCtrl) you can inject $stateParams to controller and use $stateParams.connected to get it.
For #4, (This is more or less similar to achieveing #3)
Just like you get $stateParams.id, you can have $stateParams.connected as well, which you can use to resolve different objects according to the value of connected. Something like this:
.state('global.editor', {
url: '/posts/editor/{id}',
templateUrl: '/htmls/editor.html',
params: { connected: null },
controller: 'EditorCtrl',
resolve: {
post: ['$stateParams', 'codeService', function ($stateParams, codeService) {
return $stateParams.connected ?
codeService.getPost($stateParams.id) :
codeService.getSomethingElse($stateParams.id)
}]
}
}
But, for that to work, make sure that you are passing connected as params when you visit global.editor state (using $state.go or ui-sref)
Hope this helps!

Related

Angularjs ui-router changes state but does not update url when passing params

I am using UI-Router to navigate to detailed view. It changes state correctly and passes parameters correctly, yet the url address in the browser stays unchanged. I would like to display the passed params in the url.
app.js
'use strict';
var myApp = angular.module("myApp", ['ui.router']);
myApp.config(['$stateProvider', '$urlRouterProvider','$locationProvider',
function($stateProvider, $urlRouterProvider,$locationProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('/', {
url: '/',
templateUrl: 'partials/listView.html'
})
.state('list', {
url: '/',
templateUrl: 'partials/listView.html'
})
.state('detail', {
url: '/detail/:key',
params: {
key: { value: "" }
},
templateUrl: 'partials/detailView.html',
controller: 'DetailController'
})
// use the HTML5 History API
$locationProvider.html5Mode(true);
}]);
Controller function to go to the detail state:
myApp.controller('MainContr', [ '$scope', '$http', '$location', '$state',
'$filter','$rootScope', MainContr ]);
function MainContr($scope, $http, $location, $state, $filter,$rootScope) {
$scope.goDetailView= function(key){
console.log("trying to change url ");
// changes state correctly, and passes params to DetailController,
// but does not change browser address url.
// How can I update the url in browser here?
$state.go('detail',
{key: $scope.selectedPDFKey},
{
location:true
});
}
}
// detail view
myApp.controller('DetailController', [ '$scope', '$stateParams'
DetailController ]);
function PDFDetailController($scope,$state)
{
$scope.currentKey=$state.params.key;
}
If I remove params in $state.go('detail'), the url in browser address bar is replaced. How can I get url in browser address bar replaced as well when I pass parameters in $state.go(). Thank you.
Issue was fixed when state was changed to use query in url as:
.state('detail', {
url: '/detail?key',
params: {
key: { value: "" }
},
templateUrl: 'partials/detailView.html',
controller: 'DetailController'
})
I came across this, but in my case it was just a missing / at the beginning of the url. So this is wrong:
.state('detail', {
url: 'detail/:key',
templateUrl: 'tpl.html'
});
And this is correct/working:
.state('detail', {
url: '/detail/:key',
templateUrl: 'tpl.html'
});
I dont needed any <base> tags in my <head> at all. My application is in a subfolder called /app.
By default - UI-Router will always show the param in address bar, if is defined in the url (not just in params : {} option)
To prove it, there is a plunker with your scenario, which does what you would expect - http://plnkr.co/edit/9uBlhNoNqZsjAJEdIhYa?p=preview
Links
<a ui-sref="list">
<a ui-sref="detail({key:1})">
<a ui-sref="detail({key:22})">
A state def (as yours)
.state('list', {
url: '/',
templateUrl: 'tpl.html',
controller: 'ParentCtrl',
})
.state('detail', {
url: '/detail/:key',
params: {
key: { value: "" }
},
templateUrl: 'tpl.html',
controller: 'ChildCtrl',
});
Check it here
(you can run it in separate window, by clicking the icon in the top right corner - and see the address bar with a key param)

Set $scope.parameter, ui-router

I got a main.html file, inside that I got a section that is visible/hidden depending on the state of a $scope.parameter. This main.html is used in two of my routes, one is the 'main' other is a sub-route. The look like this,
.state('medications', {
url: '/medications',
templateUrl: '/partials/home.html',
controller: 'mainController',
resolve: {
postPromise: ['medicationservice', function(medicationservice) {
return medicationservice.getAll();
}]
}
})
.state('medications.add', {
url: '/add',
templateUrl: '/partials/home.html',
controller: 'mainController'
})
So what I would like to do in this case is set that $scope.parameter to show this section. I have seen this examples but I don't get how I set what controller it should use.
$stateProvider.state('contacts', {
template: '<h1>{{title}}</h1>',
controller: function($scope){
$scope.title = 'My Contacts';
}
})
How do I set a parameter with explcitly setting the controller name?
Option 1:
Make it a stateParams (recommended):
.state('medications', {
params: {showSection: false},
//...
.state('medications.add', {
params: {showSection: true},
//...
And set your $scope variable in mainController to that $stateParam:
$scope.$watch($stateParams.showSection, function(){
$scope.parameter = $stateParams.showSection;
}, true);
Option 2:
Watch the $stateChangeStart event and change your $scope variable according to toState:
$scope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
$scope.parameter = toState === 'medications' ? false : true;
})
Use the 'data' object of $state:
.state('medications.add', {
url: '/add',
templateUrl: '/partials/home.html',
controller: 'mainController',
data: { myParameter: 'true' }
})
Then, in the controller, you can access the data using the $state service. Assign the data to the scope of the controller and you're good to go:
angular.module('myapp').controller('myController',function($scope,$state){
$scope.myParameter = $state.data.myParameter;
});

AngularJS ui-router navigating states with $state.params and queries

I have an app with multiple states that each have nested views. The one state has a conditional templateUrl, and based on a $state.param will show specific HTML/JS. I want to set a query on the URL of the state, so that I know which list item is being looked at when I click it. I cannot figure out how to set a url query and transition to the desired state's view.
My states:
.state('index', {
url: '/',
views: {
'#' : {
templateUrl: 'views/layout.html'
},
'top#index' : {
templateUrl: 'views/top.html',
controller: function($scope, $state) {
$scope.logOut = function() {
$state.go('login');
};
}
},
'left#index' : { templateUrl: 'views/left.html' },
'main#index' : { templateUrl: 'views/main.html' }
}
})
.state('index.list', {
url: 'list?item',
templateUrl: 'views/lists/list.html',
controller: 'ListCtrl'
})
.state('index.list.details', {
url: '/',
params: {
detail: 'overview'
},
controller: ctrl,
views: {
'details#index' : {
templateUrl: function($stateParams) {
if($stateParams.detail === 'status' ) {
ctrl = 'StatusCtrl';
return 'views/details/status.html';
} else if ($stateParams.detail === 'overview') {
ctrl = 'OverviewCtrl';
return 'views/details/overview.html';
}
}
}
},
})
In my controller for the index.list, this is where I have the list and click and populate the details view.
HTML & Js:
<div class="channel" ng-repeat="item in items" ng-click="viewListDetails(item)">
$scope.viewListDetails = function(item) {
$location.search('item', item.id);
$state.go('index.list.details', {detail: 'status'}, {reload: true})
};
My JS above runs the through the function however it does nothing! It will not set the query or transition to the desired view for that sate.
Any help is appreciated!
index.deviceList.detail is not a defined state. You probably intended to write index.list.details.

Angular UI-Router doesn't match a defined state

I'm using UI-Router module for routing. I have 2 states that router should match the urls with them :
// Dashboard
.state('dashboard', {
url: "/dashboard",
templateUrl: "dashboard/views/index.html",
controller: "DashboardController",
...
})
// Users
.state('users', {
url: "/users",
templateUrl: "users/views/index.html",
controller: "UsersController",
...
})
// Single User
.state('users.id', {
url: "/{id:^[a-z0-9_-]{3,16}$}",
templateUrl: "users/views/show.html",
controller: "UserController",
...
})
also I have set a default route :
$urlRouterProvider.otherwise("/dashboard");
I want the router to match users state when I go to http://127.0.0.1:8000/app/#/users
and to match user state when I go to http://127.0.0.1:8000/app/#/users/testuser
Problem :
the users state works good, but the user state url doesn't get matched and redirects to the default state. What's the problem?
There is a working example
Try to use this regex def:
.state('users.id', {
url: "/{id:(?:[a-z0-9_-]{3,16})}",
These links will work
<a href="#/users">
<a href="#/users/testuser">
This will go to otherwise
<a href="#/users/xx">
Some inspiration could be found here
In case, we want to go to state 'users.id' directly, we just have to use proper call. In this extended plunker, we can see that it could be done like this:
// working
<a ui-sref="users">
<a ui-sref="users.id({id:'testword'})">
// not working - id does not fit - otherwise is used
<a ui-sref="users.id({id:'not-working simply too long'})">
The same would be with $state.go('users.id', {id:'testword'})
Check it here
Here is an example of how I've done it in the past. Maybe it will help you.
app.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider,$rootScope,$cookieStore) {
$urlRouterProvider.otherwise("/login");
$stateProvider
.state('login', {
url: '/login',
title: 'Login',
templateUrl:'views/loginView.html',
controller: 'loginCtrl',
resolve: resolver($rootScope),
})
.state('account', {
url: '/account',
title: 'My Account',
accessLevel: 2,
resolve: resolver($rootScope,$cookieStore),
views: {
'navigation': {
templateUrl: 'views/navigationView.html',
controller: 'navigationCtrl'
},
'content': {
templateUrl: 'views/contentView.html',
controller: 'navigationCtrl'
}
}
})
.state('account.dashboard', {
url:'/dashboard',
title: 'Dashboard',
views : {
'pageContent': {
templateUrl:'views/dashboardView.html',
controller: 'dashboardCtrl'
}
}
})
.state('account.foo', {
url:'/foo',
title: 'foo',
views : {
'pageContent': {
templateUrl:'views/foo.html',
controller: 'fooCtrl'
}
}
})
.state('account.maps', {
url:'/maps',
title: 'Maps and stuff',
views : {
'pageContent': {
templateUrl:'views/mapsView.html',
controller: 'mapsCtrl'
}
}
})
}])

How to unit test onEnter and resolve of ui-router state

I'm trying to test an Angular UI Bootstrap modal that is being called from the onEnter of the following state:
.state("profile.index.edit.services", {
url: "/edit/services/:serviceCode",
parent:"profile.index",
onEnter: ['$stateParams', '$state', '$modal',function($stateParams, $state, $modal) {
$modal.open({
templateUrl: 'profile/edit-services-modal.html',
controller: 'ProfileEditServicesController',
resolve: {
profileData: function(CacheService){
return CacheService.getItem(CacheService.Items.Profile.loadedProfile);
},
allServicesList: function(ProfileService){
return ProfileService.getAllServicesList($stateParams.serviceCode,$stateParams.orgId);
},
serviceCode: function() {
return $stateParams.serviceCode;
}
}
}).result.then(function(result) {
if (result) {
return $state.transitionTo("profile.index", { orgId: $stateParams.orgId });
}
else { // cancel
return $state.transitionTo("profile.index", { orgId: $stateParams.orgId }); // don't reload
}
}, function () {
// executes on close/cancel/ESC
return $state.transitionTo("profile.index", { orgId: $stateParams.orgId });
});
}]
})
I've been banging my ahead against this trying many different things to set the test up, but can't seem to figure it out. Any plunkers or suggestions are greatly appreciated!!!!
By the way, the state above is a descendant of these states, which I've been able to write tests for that pass:
.state('profile.index', {
url: '/:orgId',
views: {
'profile-index#profile': {
templateUrl: 'profile/view-profile.html',
controller: 'ProfileController'
},
'header#': {
templateUrl: 'common/layout/header.html',
controller: 'HeaderController'
},
'footer#':{
templateUrl: 'common/layout/footer.html',
controller: 'FooterController'
}
},
resolve: {
profileData: function(ProfileService, $stateParams){
return ProfileService.getProfile($stateParams.orgId, true);
}
}
})
.state('profile.index.edit', {
url: '',
abstract: true
})
did you ever figure this out? you should have passed a named controller, that way you could test that controller (since it would just be a function) directly instead of relying on onEnter being fired.

Categories

Resources