angular-ui-router unable to create a dynamic named, named view - angularjs

I am using angular-ui-router multiple views.
I have a constant module:
angular.module('app.constants', [])
.constant('constants', {
parent1: "parent1",
parent2: "parent2"
});
my state looks like this:
.config(['$stateProvider', 'constants', function ($stateProvider, constants) {
$stateProvider.state(constants.parent1, {
abstract: true,
url: '/' + constants.parent1
});
$stateProvider.state(constants.parent1 + ".state1", {
url: '/state1',
views: {
'cool-view#' + constants.parent1 + 'state1': {
templateUrl: root + 'views/splashScreen/splash.html',
controller: 'splashScreenCtrl'
}
}
});
$stateProvider.state(constants.parent2, {
abstract: true,
url: '/' + constants.parent2
});
$stateProvider.state(constants.parent2 + ".state1", {
url: '/state1',
views: {
'cool-view#' + constants.parent2 + 'state1': {
templateUrl: root + 'views/splashScreen/splash.html',
controller: 'splashScreenCtrl'
}
}
});
}]);
I have many parent abstract states.
In a child state i have a named view which i want to trigger.
The problem is that i can't dynamically create the line:
'cool-view#' + constants.parent1 + 'state1'
It will work with :
'cool-view#parent1.state1'
Gives me an error. How will i be able to create a dynamically field name inside the views object.
The reason i want to be able to create it dynamically is because i have a constant module which i might change in the future and i do not want to hardcode it in the views.

jsfiddle to solve this problem without much thinking about why you are doing this:
http://jsfiddle.net/qq1rbbb7/
var constants = { parent2: "someThing" };
var root = "root";
var views = {};
views['cool-view#' + constants.parent2 + 'state1'] = {
templateUrl: root + 'views/splashScreen/splash.html',
controller: 'splashScreenCtrl'
};
var state = {
'url': '/state1',
'views': views
};
console.debug(state);

Related

Dynamic template URL

I am diving head first into Angular 1 for the first time with an existing app. One of the things about this app that I want to change is how there are separate services and other things for every entity in our application.
I've abstracted it out so that there's only one service for all entities, but now I am trying to load a template where the file name is equal to a particular state parameter.
Here's how the per-entity routing is done now:
namespace App.Employee {
'use strict';
angular
.module('app.employee')
.run(appRun);
appRun.$inject = ['routerHelper'];
function appRun(routerHelper: Common.Router.IRouterHelperService) {
routerHelper.configureStates(getStates());
}
function getStates() {
return [{
name: 'employee',
url: '/employee/{employeeId}',
templateUrl: 'app/employee/employee.html',
controller: 'employeeCtrl',
controllerAs: 'vm',
data: {
title: 'Employee'
}
}];
}
}
Here's what I want to change it to:
namespace App.Entity {
'use strict';
angular
.module('app.entity')
.run(appRun);
appRun.$inject = ['routerHelper'];
function appRun(routerHelper: Common.Router.IRouterHelperService) {
routerHelper.configureStates(getStates());
}
function getStates() {
return [{
name: '||entityTypeName||',
url: '/{entityTypeName}/{entityId}',
templateUrl: 'app/entity/||entityTypeName||.html',
controller: 'entityCtrl',
controllerAs: 'vm',
data: {
title: '||entityTypeName||'
}
}];
}
}
Notice how I introduced {entityTypeName} in the URL. This successfully points to the proper Web API service and pulls back the entity. However, I want to tokenize the ||entityTypeName|| placeholder to be what's matched for {entityTypeName}. That's the general idea, at least.
I know little of Angular at this point and am learning as I go along, so let me know if additional code is needed.
The templateUrl property can be given a function instead of a string, and it will be passed the current state parameters. Like so:
templateUrl: function(params) {
return 'app/entity/' + params.entityTypeName + '.html';
}
The params argument is provided for you by the ui-router framework.
I also do something similar using dynamic routing with views and view parameters like so:
/**
* url: "/view?a"
* Will match to url of "/view?a=value"
*/
.state('root.view', {
url: '/view?a',
views: {
'main-content#': { templateUrl: function(params) {console.log(params.a); return 'app/views/ + params.path + ".php"} }
}
})
Or for the following:
/**
* Dynamic Routing
*/
.state('root.catpath', {
url: '/{path:.*}',
views: {
'main-content#': { templateUrl: function(params) {console.log(params); return 'app/views/' + params.path + ".php"} }
}
});

angular-ui route won't load controller

.state('app.match.indicator.speciality',{
url: '/speciality/:jobId?',
views: {
'sidebar#app.match.indicator':{
templateUrl: ENVApp + '/views/match/match.roleProfileSideBar.html?' + cacheVersion,
},
'createRoles#app.match.indicator': {
templateUrl: ENVApp + '/views/match/match.page2.html?' + cacheVersion
}
},
controller: 'RoleProfileCreateSpecialtyController'
})
That's what I have as a state definition, however my RoleProfileCreateSpecialtyController doesn't get loaded for some reason. I know this because I threw an alert in there that never happens.
What am I doing wrong?
This also fails:
.state('app.match.indicator.speciality',{
url: '/speciality/:jobId?',
views: {
'sidebar#app.match.indicator':{
templateUrl: ENVApp + '/views/match/match.roleProfileSideBar.html?' + cacheVersion,
},
'createRoles#app.match.indicator': {
templateUrl: ENVApp + '/views/match/match.page2.html?' + cacheVersion
}
},
// controller: 'RoleProfileCreateSpecialityController'
controller: function() {
alert('fd')
}
})
When defining multiple views in a state, you cannot define a single controller (see https://stackoverflow.com/a/33139917/3153169 for reference).
To fix this, you can just define the controller for the multiple views:
.state('app.match.indicator.speciality',{
url: '/speciality/:jobId?',
views: {
'sidebar#app.match.indicator':{
templateUrl: ENVApp + '/views/match/match.roleProfileSideBar.html?' + cacheVersion,
controller: 'RoleProfileCreateSpecialtyController'
},
'createRoles#app.match.indicator': {
templateUrl: ENVApp + '/views/match/match.page2.html?' + cacheVersion,
controller: 'RoleProfileCreateSpecialtyController'
}
}
})

Determine Angularjs Controller based on route params

I have the following configuration for my route:
$routeProvider.when('/', {
controller: 'homeController',
templateUrl: 'app/views/main/home.html'
})
.when('/:section/:tree', {
templateUrl: function($routeParams) { return 'App/Views/'+$routeParams.section+'/'+$routeParams.tree+'.html'; },
controller: function ($routeParams) { return $routeParams.tree + 'Controller'; }
})
.otherwise({
redirectTo: '/'
});
}
The view is loaded correctly but without controller and the controller function is not even called.
Is there any way to solve this issue or determine the controller based on route params.
The controller function is not a function returning the controller name. It's supposed to be the controller itself.
Let's suppose the tree route param can be Foo or Bar, you just need
.when('/:section/Foo', {
templateUrl: function($routeParams) { return 'App/Views/'+$routeParams.section+'/Foo.html'; },
controller: 'FooController'
})
.when('/:section/Bar', {
templateUrl: function($routeParams) { return 'App/Views/'+$routeParams.section+'/Bar.html'; },
controller: 'BarController'
})
This, BTW, will avoid your application to cause an exception is the user enters something other than Foo or Bar in the URL.
If you have many similar routes, just use a loop to define them:
['Foo', 'Bar', 'Baz', 'Brr', ...].forEach(function(tree) {
$routeProvider.when('/:section/' + tree, {
templateUrl: function($routeParams) { return 'App/Views/' + $routeParams.section+'/' + tree + '.html'; },
controller: tree + 'Controller'
});
});

AngularJS - UI Router: Get template url from within the template

I'm creating states via ui-router, and attaching them to the same controller. (although obviously different instances of that controllers are initialized)
Inside of that controller, I'd like to know the name of the template it was initialized for.
My idea was to somehow pass to that controller properties in the stateProvider (ui-sref doesn't solve opening the browser with a deep link), but from my searches so far it appears it can't be done.
I can't simply check the name of the current state, since I'm working with multiple views.
I'm working with the same controller for multiple states and views, the controller can act accordingly as soon as he can know the name of the template he's attached to.
Here's how I create my states and views:
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home');
var controller = 'textFile';
var base = 'templates';
var views = ['home', 'about'];
for (var i = 0; i < views.length; i++)
{
var view = views[i];
$stateProvider.state(view, {
url: '/' + view,
views: {
'header': {
templateUrl: base + '/header.html',
controller: controller
},
'page': {
templateUrl: base + '/' + view + '.html',
controller: controller
},
'footer': {
templateUrl: base + '/footer.html',
controller: controller
}
}
});
}
}]);
I need to be able to know in textFile for instance if it's attached to header.html, footer.html, etc...
Why don't you use a resolve, so that you can access the unique data for each controller instance. For example:
$stateProvider.state(view, {
url: '/' + view,
views: {
'header': {
templateUrl: base + '/header.html',
controller: 'textFile',
resolve: {
viewName: function() { return 'header'; }
}
},
'page': {
templateUrl: base + '/' + view + '.html',
controller: 'textFile',
resolve: {
viewName: function() { return 'page'; }
}
},
'footer': {
templateUrl: base + '/footer.html',
controller: 'textFile',
resolve: {
viewName: function() { return 'footer'; }
}
}
}
});
Then you can access the value in your controller:
.controller('textFile', function($scope, viewName){
console.log(viewName);
});

ui-router dynamic template path

I'm using ui-router 0.2.8. I'm wanting to load a template based on device width. I can get the device width without issue, set it in the scope etc but I can figure out how to bind it to $stateParams. I have the scope variable in another controller which can be accessed the state's controller it's just not available to the state itself. I've tried the templateProvider but that is only returning me a string. What else can I try in order for this to work?
.state('list', {
abstract: true,
url: "/list",
templateUrl: 'views/'+$stateParams.somevalue+'/page.html',
controller: "myCtrl"
})
.state('list.first', {
url: "/first",
templateUrl: "views/first.html"
})
You might be looking for dynamic template name based on the state params
$stateProvider.state('contacts', {
templateUrl: function ($stateParams){
return '/partials/contacts.' + $stateParams.filterBy + '.html';
}
})
See the docs for more information
https://github.com/angular-ui/ui-router/wiki#templates
You can access to state params in the $stateChangeStart event. You can also dynamically update the templateUrl there as well.
So perhaps your code might look something like this:
angular.module('app', ['ui.router'])
.run(function($rootScope){
$rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
if (toState.name === 'list') {
toState.templateUrl = 'views/'+toParams.somevalue+'/page.html';
}
});
}
You might also want to take a look at the onEnter callback supported by ui.router. I have not used this before but it might be neater than putting your template generating code into the $stateChangeStart event.
This reference here was great for me figuring out how to do dynamic templates in angular, but I'd like to update with my own solution.
Solution 1 based on Dipesh Kc's (this works great for redefining parent templateUrl for abstract states) Notice I used toParams instead of $stateParams:
// custom parent template
.state('template', {
abstract: true,
url: "/tm/:tmfolder/:tmview",
templateUrl: function (toParams) {
return 'views/' + toParams.tmfolder + '/' + toParams.tmview + '.html';
},
})
.state('template.contacts', {
url: "/contacts/:folder/:view",
templateUrl: function (toParams) {
return 'views/' + toParams.older + '/' + toParams.view + '.html';
},
})
Solution 2 based on biofractal's (there is no way to update a parent templateUrl using this method):
.state('contact', {
url: "/contact/:folder/:view"
})
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
var customStates = ["contact"]
for (var i = 0; i < customStates.length; i++) {
if (toState.name.indexOf(customStates[i]) != -1) {
toState.templateUrl = 'views/' + toParams.folder + '/' + toParams.view + '.html';
break;
}
}
});

Resources