How to change pageTitle dynamically on state change angularjs inside state routing - javascript

I want to change the pageTitle inside data in state Provider.
$stateProvider.state(test, {
url: '/test',
views: {
main: {
controller: 'TestCtrl',
templateUrl: 'admin/test.tpl.html'
}
},
data: {
pageTitle: 'Need some dynamic title',
},
});
Here I want to set the page title dynamically may be somewhere inside $state.go().
I tried using
//The controller from where the state is called and we got to know what the title is
$state.get('test').data.pageTitle = $scope.title;
$state.go('test');
But nothing has happened.
Please help.

Inside $state.go() you can do:
$state.go('test', {pageTitle: $scope.title});
(or it will be data.pageTitle: $scope.title, I'm not absolutely sure).
And you didn't include any HTML, so don't forget to bind value to title tag like this:
<title ng-bind="$state.current.data.pageTitle"></title>

Related

Update $state.params without reloading a controller [duplicate]

Get the context, angular, ui-router, nothing special, a root view built with 3 named ui-views.
so in index.html we have
<body>
<div ui-view='left'>
<div ui-view='center'>
<div ui-view='right'>
</body>
my route looks like
$stateProvider
.state('main', {
url: '/',
views: {
'left': {templateUrl: 'foo.html'},
'center': {templateUrl: 'bar.html'},
'right': {templateUrl: 'xyz.html'}
}
})
.state('main.b', {
url: '/b',
params: { foo: {value: 'bar'} }
views: { 'right#': {templateUrl: '123.html'} } // I wish to update $stateParams in 'left#' view
})
.state('main.c', {
url: '/c',
params: ...
views: { 'left#': ..., 'center#': ..., 'right#': .. }
});
Is there a way in going to b state to update the $stateParams in the 'center' and 'left' view?? I can get it using a service but i need to add a $watch to the variable I need and it looks a little bit hacky to me.
Going into c state I can actually get what I want, but the view is reloaded, and i wish to avoid this behaviour cause i have a canvas in the 'left' view.
You could use the following to go to a specific route without reloading the views:
$state.go('.', {parm1: 1}, {notify: false});
The last object literal represents the options which you can pass along to go. If you set notify to false, this will actually prevent the controllers from being reinitialized. The . at the beginning is the absolute state name or relative state path you wanna go to.
The important thing is the notify though.
I think that using "Dynamic params" is now a better solution:
When dynamic is true, changes to the parameter value will not cause the state to be entered/exited. The resolves will not be re-fetched, nor will views be reloaded.
$stateProvider.state('search', {
url: '/search?city&startDate&endDate',
templateUrl: 'some/url/template.html',
params: {
city: {
value: 'Boston',
dynamic: true
}
}
}
and then:
$state.go('.', {city: 'London'});
https://ui-router.github.io/ng1/docs/latest/interfaces/params.paramdeclaration.html#dynamic
https://github.com/angular-ui/ui-router/issues/2709
Quoting #christopherthielen from https://github.com/angular-ui/ui-router/issues/1758#issuecomment-205060258:
using notify: false is almost never a good idea, and is now
deprecated. Use reloadOnSearch if you must.
You can also try dynamic parameters in the 1.0 version (currently
1.0.0-alpha.3). In your state, configure a parameter as dynamic and implement the uiOnParamsChanged callback :
.state('foo', {
url: '/:fooId',
params: { fooId: { dynamic: true } },
controller: function() {
this.uiOnParamsChanged = function(changedParams, $transition$) {
// do something with the changed params
// you can inspect $transition$ to see the what triggered the dynamic params change.
}
}
});
For a demo, have a look at this plunker: http://plnkr.co/edit/T2scUAq0ljnZhPqkIshB?p=preview

What's wrong with my code to go to a child state in UI Router

Let's say I've configured an state as follows:
{
name: "results",
parent: "modules/globalSearch/main",
url: ""
// other settings
}
* Note that I'm not using URLs. "modules/globalSearch/main" is the actual state name
In the other hand, "modules/globalSearch/main" is configured as follows:
$stateProvider.state({
name: "modules/globalSearch/main",
abstract: true
// other settings
});
Since "results" is a child of "modules/globalSearch/main", I would expect to call $state.go as follows:
$state.go("modules/globalSearch/main.results");
But UI Router will say that it can't transition to such state, and I can do so if I just call $state.go("results").
What's wrong here?
Try $state.go("modules.globalSearch.main.results");
EDIT :
By the way, I don't know how you declared your states in config.js
but that should stand this form:
.state('parent', {
url : '/parent',
templateUrl : 'url
})
.state('parent.child', {
url : '/child/:parameter/',
templateUrl : 'url
})
BR

UI-router accessing parent state from child

I'd like to set the title based on the state in my angular app. It works nicely, but if there's no title defined on the child state, I'd like to fall back to the parent's title property.
This is what I have so far:
In my app run block:
...
$rootScope.$on('$stateChangeSuccess', function (event, current, previous) {
if (current.hasOwnProperty('title')) {
$rootScope.title = current.title;
}
});
...
In my module's route
.state('main.parent', {
url: '/parent',
controller: 'ParentController',
controllerAs: 'vm',
templateUrl: 'app/parent.html',
title: 'Parent',
})
.state('main.parent.child', {
url: '/child',
controller: 'ChildController',
controllerAs: 'vm',
templateUrl: 'app/child.html'
})
During the code inspection, I found that $stateChangeSuccess is triggered twice as expected, first for the parent and then for the child. I tried to access current.parent when the child was called, but it has no such property. Is there any way to access it?
UPDATE
I saw these questions:
get parent state from ui-router $stateChangeStart,
get parent state from ui-router $stateChangeStart
But none of them had a clear answer to this problem (except the state splitting hack in the latter one but I'd rather avoid that).
The both ways are hacks a bit.
Don't forget to import $state (and $stateParams for 2nd variant) providers in your controller.
You can use:
$state.$current.parent.self.title
or
var tree = current.name.split('.');
tree.splice(tree.length-1, 1);
var parent = tree.join('.');
$state.get(parent, $stateParams);
It seems that you just need the title property to fall back to the parent's title property if there's no title defined on the child state, I will suggest you to use this repository: angular-ui-router-title.
So finally I came up with the following solution:
I store title inside the state's data object, because from $state you can't access the parent's custom properties, only the ones stored in data (which makes sense - not to pollute the state definition with custom properties).
.state('main.parent', {
url: '/parent',
controller: 'ParentController',
controllerAs: 'vm',
templateUrl: 'app/parent.html',
data: {
title: 'Parent'
}
})
.state('main.parent.child', {
url: '/child',
controller: 'ChildController',
controllerAs: 'vm',
templateUrl: 'app/child.html'
})
And I changed the runblock as follows:
...
$rootScope.$on('$stateChangeSuccess', function (event, toState, fromState) {
var current = $state.$current;
if (current.data.hasOwnProperty('title')) {
$rootScope.title = current.data.title;
} else if(current.parent && current.parent.data.hasOwnProperty('title')) {
$rootScope.title = current.parent.data.title;
} else {
$rootScope.title = null;
}
});
...
This is not the perfect solution yet, as in the case of multiple nested states it'd be nice to fall back to the title of the youngest ancestor's title but it does it for now.
Simply below code is working for me
$state.$current.parent
Looking at the UI-Router documentation in the "to" section, you could do something like below:
$state.go('^');
to go back to the parent state.

AngularJs : Attach a stateParams value to custom data in state objects

I'm defining a page title in my state object as advised here.
$stateProvider
.state('project', {
url: '/projects/:origin/:owner/:name',
template: '<project></project>',
data : { pageTitle: 'Project page title'},
});
so i can access it this way later on:
$state.current.data.pageTitle
Now ... What i'd like to do is that instead of having a fixed value as pageTitle i'd like to access one the stateParams.
BEWARE, below exemple is not working, this is the way i'd like it to work.
$stateProvider
.state('project', {
url: '/projects/:origin/:owner/:name',
template: '<project></project>',
data : { pageTitle: $stateParams.name},
});
I could also use the attribute resolve :
resolve: { title: 'Project page title' },
For stateParams value i could do that:
resolve:{
pageTitle: ['$stateParams', function($stateParams){
return $stateParams.name;
}]
}
But then console.log($state.current.resolve.pageTitle); in my controller will return the entire function and not the result.
What would be the proper way to do that?
UPDATE :
The thing which i didn't mention is that i use angularJs with ES6 and i use a modular architecture.
Therefore each component has its own scope.
You can check my previous post to have a better idea about the modular architecture i'm using.
I'm building a breadcrumb grabbing the parent states (using the defined pageTitle).
Therefore defining the page title name in each module would be perfect.
So i can decide if the page title would be a fixed value "My page title" or a stateParams value.
Maybe this is totally wrong and this should not be done that way. Let me know if it is the case.
If you use a resolve like your example:
resolve:{
pageTitle: ['$stateParams', function($stateParams){
return $stateParams.name;
}]
}
You'll need to inject pageTitle into your controller:
angular.module('app').controller('controller', [
'pageTitle',
function (pageTitle) {
}
]);
But i don't see how that is any different from injecting $stateParams:
angular.module('app').controller('controller', [
'$stateParams',
function ($stateParams) {
}
]);

Angular UI-router: ok to leave first child empty? Or hidden?

I want to have a nested view with UI router, which I've done before (see image) with a main section and then a nav which loads sub-sections into the nested UI-View. This I can do, no issues.
My question is: this time I need to have the initial child state not show to the user until a button is clicked, like this:
Can I do this? Or is it better to load the "baseball" view but hide it and the nav with ng-hide?
UPDATE
Someone asked how I would do the simple nested states in a case like this:
(function() {
'use strict';
angular.module('elements').config(function($stateProvider) {
$stateProvider.state('elements', {
url: '/elements',
abstract: true,
templateUrl: 'modules/elements/templates/elements.html',
controller: 'ElementsController as elements'
})
.state('elements.buttons', {
url: '/elements/buttons',
templateUrl: 'modules/elements/templates/elements-buttons.html'
})
.state('elements.accordion', {
url: '/elements/accordion',
templateUrl: 'modules/elements/templates/elements-accordion.html',
controller: 'AccordionController as accordion'
})
.state('elements.colorcharts', {
url: '/elements/colorcharts',
templateUrl: 'modules/elements/templates/elements-colors-charts.html',
controller: 'ChartColorsController as charts'
})
.state('elements.grid', {
url: '/elements/grid',
templateUrl: 'modules/elements/templates/elements-grid.html'
});
});
})();
Yes it is absolutely possible. I usually accomplish this by using programmatically defined states, which looks like it should work for your situation.
If you have a state for baseball then you could control it as such:
state config
.state('baseball', {
url: '/views/baseball',
template: 'imabaseball!'
})
html
<div ui-view="{{state}}">
<button ng-click="state = 'baseball'">Show Baseball</button>
Then the view in question would not be rendered until the user clicked the button

Categories