I am new to Angular and UI-Router and I have to work with an Angular codebase. I am having difficulty understanding how the params gets mapped to the url. Specifically how topics get injected as topic in /test?topic&manualMode&advancedMode I cannot seem to find the place where I can log out the intermediate value to see how the url gets constructed.
function StateConfig(
$stateProvider: angular.ui.IStateProvider,
breadcrumbsService: BreadcrumbsService
) {
$stateProvider.state("testHub", {
params: {
topics: null
},
url: "/test?topic&manualMode&advancedMode",
views: {
main: {
template:
'<div class="hubContent"><iot-mqtt-client-config></iot-mqtt-client-config></div>'
},
sidebar: {
controller: HelpControllerTutorial,
controllerAs: "vm",
templateUrl: "pages/mqtt/mqtt-client-help.html"
}
}
});
breadcrumbsService.addConfig("testHub", {
name: (i18n: I18nService) => i18n("text.test"),
parentState: null,
reconstructBreadcrumbPath: true
});
}
This should work for you in app.js:
.state('testHub', {
url: "/test/:topic/:manualMode/:advancedMode", // added for dynamic ID params
})
Then your ui-sref in substances.html will append the ( topic or manualMode or advancedMode ) to the url, and you will be able to access that in your controller in $stateParams
Related
I have an app that uses angular-ui-router with html5mode(true). Everything seems to work fine when running and routing to other states. following are the states.
$urlRouterProvider.otherwise("/navigation-master");
$stateProvider
.state('master', {
url: "",
views: {
"#": {
templateUrl: "content-master"
},
"nav-view": {
templateUrl: "nav-content"
}
}
})
.state('master.content', {
url: "/content/:pageId",
views: {
"content-view": {
templateUrl: function ($stateParams) {
return 'content?pageId=' + $stateParams.pageId;
}
}
},
params: {extra: null}
});
secApp.run(function ($transitions, $http, $templateCache ) {});
when i refresh the page while i'm currently in other routes(lets say master.content) it takes me back to navigation-master.
Maybe try to set the url at / and not `` in your first state
Codes setting up the router (the two states have a parent-child relation):
.state("tab.my-profile", {
url: "/my/profile",
views: {
"tab-my": {
templateUrl: "templates/tab-my-profile.html",
controller: "MyProfileCtrl"
}
}
})
.state("tab.my-profile-mobileinput", {
url: "/my/profile/mobileinput",
views: {
"tab-my": {
params: {"mobile": null}
templateUrl: "templates/util-mobile-input.html",
controller: "MobileInputCtrl",
}
}
})
Codes in the controller of the parent state:
.controller("MyProfileCtrl", function ($scope, $state) {
$scope.goToMobileInput = function () {
$state.go("tab.my-profile-mobileinput", {"mobile": "123456"})
};
})
Codes in the controller of the child state:
.controller("MobileInputCtrl", function ($scope, $stateParams) {
alert($stateParams.mobile); // undefined
})
I can jump to the child state. But in the child state’s controller, I can’t receive the parameter (got an “undefined”). I’ve been stuck in this problem for hours. Could anyone help me find a way out? Thanks a lot in advance.
In my apps, I set parameters on url.
.state("tab.my-profile-mobileinput", {
url: "/my/profile/mobileinput/:mobile",
views: {
"tab-my": {
templateUrl: "templates/util-mobile-input.html",
controller: "MobileInputCtrl",
}
}
})
I have the following state configuration. How can I get id parameters in index state # view so that it could be accessible by both, left and detail views controllers? Now if I try to access it via $state.params.id it is undefined, but if I console.log($state) I can see the parameter in my console. I only need the parameter if available on first load of left view.
$stateProvider
.state('index', {
url: '/',
views: {
'#' : {
template: '/**/'
resolve: {
params: function ($state) {
return $state.params.id;
}
}
},
'left#index' : {
template: '/**/',
controller: 'AppController',
controllerAs: 'app'
},
'main#index' : {
template: '/**/'
}
})
.state('index.detail', {
url: '/:id',
views: {
'detail#index' : {
template: '/**/',
controller: 'DetailController',
controllerAs: 'detail',
}
}
});
I have an AngularJS service which communicates with the server and returns
translations of different sections of the application:
angular
.module('utils')
.service('Translations', ['$q','$http',function($q, $http) {
translationsService = {
get: function(section) {
if (!promise) {
var q = $q.defer();
promise = $http
.get(
'/api/translations',
{
section: section
})
.success(function(data,status,headers,config) {
q.resolve(result.data);
})
.error(function(data,status,headers,config){
q.reject(status);
});
return q.promise;
}
}
};
return translationsService;
}]);
The name of the section is passed as the section parameter of the get function.
I'm using AngularJS ui-router module and following design pattern described here
So I have the following states config:
angular.module('app')
.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('users', {
url: '/users',
resolve: {
translations: ['Translations',
function(Translations) {
return Translations.get('users');
}
]
},
templateUrl: '/app/users/list.html',
controller: 'usersController',
controllerAs: 'vm'
})
.state('shifts', {
url: '/shifts',
resolve: {
translations: ['Translations',
function(Translations) {
return Translations.get('shifts');
}
]
},
templateUrl: '/app/shifts/list.html',
controller: 'shiftsController',
controllerAs: 'vm'
})
This works fine but as you may notice I have to explicitly specify translations in the resolve parameter. I think that's not good enough as this duplicates the logic.
Is there any way to resolve translations globally and avoid the code duplicates. I mean some kind of middleware.
I was thinking about listening for the $stateChangeStart, then get translations specific to the new state and bind them to controllers, but I have not found the way to do it.
Any advice will be appreciated greatly.
Important note:
In my case the resolved translations object must contain the translations data, not service/factory/whatever.
Kind regards.
Let me show you my approach. There is a working plunker
Let's have a translation.json like this:
{
"home" : "trans for home",
"parent" : "trans for parent",
"parent.child" : "trans for child"
}
Now, let's introduce the super parent state root
$stateProvider
.state('root', {
abstract: true,
template: '<div ui-view=""></div>',
resolve: ['Translations'
, function(Translations){return Translations.loadAll();}]
});
This super root state is not having any url (not effecting any child url). Now, we will silently inject that into every state:
$stateProvider
.state('home', {
parent: 'root',
url: "/home",
templateUrl: 'tpl.html',
})
.state('parent', {
parent: 'root',
url: "/parent",
templateUrl: 'tpl.html',
})
As we can see, we use setting parent - and do not effect/extend the original state name.
The root state is loading the translations at one shot via new method loadAll():
.service('Translations', ['$http'
,function($http) {
translationsService = {
data : {},
loadAll : function(){
return $http
.get("translations.json")
.then(function(response){
this.data = response.data;
return this.data;
})
},
get: function(section) {
return data[section];
}
};
return translationsService;
}])
We do not need $q at all. Our super root state just resolves that once... via $http and loadAll() method. All these are now loaded, and we can even place that service into $rootScope:
.run(['$rootScope', '$state', '$stateParams', 'Translations',
function ($rootScope, $state, $stateParams, Translations) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.Translations = Translations;
}])
And we can access it anyhwere like this:
<h5>Translation</h5>
<pre>{{Translations.get($state.current.name) | json}}</pre>
Wow... that is solution profiting almost from each feature coming with UI-Router... I'd say. All loaded once. All inherited because of $rootScope and view inheritance... all available in any child state...
Check that all here.
Though this is a very old question, I'd like to post solution which I'm using now. Hope it will help somebody in the future.
After using some different approaches I came up with a beautiful angularjs pattern by John Papa
He suggest using a special service routerHelperProvider and configure states as a regular JS object. I'm not going to copy-paste the entire provider here. See the link above for details. But I'm going to show how I solved my problem by the means of that service.
Here is the part of code of that provider which takes the JS object and transforms it to the states configuration:
function configureStates(states, otherwisePath) {
states.forEach(function(state) {
$stateProvider.state(state.state, state.config);
});
I transformed it as follows:
function configureStates(states, otherwisePath) {
states.forEach(function(state) {
var resolveAlways = {
translations: ['Translations', function(Translations) {
if (state.translationCategory) {
return Translations.get(state.translationCategory);
} else {
return {};
}
}],
};
state.config.resolve =
angular.extend(state.config.resolve || {}, resolveAlways || {});
$stateProvider.state(state.state, state.config);
});
});
And my route configuration object now looks as follows:
{
state: ‘users’,
translationsCategory: ‘users’,
config: {
controller: ‘usersController’
controllerAs: ‘vm’,
url: ‘/users’.
templateUrl: ‘users.html'
}
So what I did:
I implemented the resolveAlways object which takes the custom translationsCategory property, injects the Translations service and resolves the necessary data. Now no need to do it everytime.
I'm currently working on an app where I have multiple nested views, they sort of look like this:
- ui-view
- ui-view="header"
- ui-view="nav"
- ui-view="body"
My states are defined as follows:
.state('index', {
url: '', // default route
templateUrl: 'welcome.html'
})
.state('app', {
abstract: true,
templateUrl: 'app.template.html' // This template contains the 3 different ui-views
})
// I'm using a different state here so I can set the navigation and header by default
.state('in-app', {
parent: 'app',
abstract: true,
views: {
'nav#app': { '...' },
'header#app': { '...' }
}
})
// In-app routes
.state('dashboard', {
parent: 'in-app',
url: '/app/dashboard'
views: {
'body#app': { '...' }
}
})
.state('users', {
parent: 'in-app',
url: '/app/users'
views: {
'body#app': { '...' }
}
})
.state('settings', {
parent: 'in-app',
url: '/app/settings'
views: {
'body#app': { '...' }
}
})
At the moment this works great, but for the in-app routes I would like to define a title that is displayed in the header#app view.
What would be the best way to do this? At the moment I can only think of either setting a variable on the $rootScope, or sending out an event. But for both of those I would need a controller.
Is there a way I could do this directly from my routes config?
The sample applicaiton of the UI-Router, uses this code:
ui-router / sample / app / app.js
.run(
[ '$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
// It's very handy to add references to $state and $stateParams to the $rootScope
// so that you can access them from any scope within your applications.For example,
// <li ng-class="{ active: $state.includes('contacts.list') }"> will set the <li>
// to active whenever 'contacts.list' or one of its decendents is active.
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}])
And that means, that with data : {} feature:
Attach Custom Data to State Objects
You can attach custom data to the state object (we recommend using a data property to avoid conflicts).
// Example shows an object-based state and a string-based state
var contacts = {
name: 'contacts',
templateUrl: 'contacts.html',
data: {
customData1: 5,
customData2: "blue"
}
}
we can do this:
.state('in-app', {
parent: 'app',
abstract: true,
views: {
'nav#app': { '...' },
'header#app': { '...' }
}
data: { title : "my title" },
})
And use it in some template like:
<div>{{$state.current.data.title}}</div>
Some summary.
We can place state and params into $rootScope, so we can access it without any controller anyhwere.
We can declare some more custom stuff via data and use it as a title ... anyhwere