Angular uiRouter set page title from database document - javascript

I'm trying to dynamically set the page title in my angular app.
I've already have it working with hardcoded title dynamically changing based on the state
.state('faq-detail', {
url: '/faqs/:faqId',
templateUrl: 'client/faq/faq-detail.view.ng.html',
controller: 'FaqListCtrl',
data: {
title: 'Faq details'
}
});
but I want to get the, in this case the question title, and put it as the page title.
The question comes from mongo database and I'm using angular-meteor.
.state('faq-detail', {
url: '/faqs/:faqId',
templateUrl: 'client/faq/faq-detail.view.ng.html',
controller: 'FaqListCtrl',
data: {
title: Faq.findOne({_id: $stateParams.faqId}).question
},
resolve: {
theFaq: function ($meteor, $stateParams) {
return $meteor.subscribe('faq').then(function () {
return Faq.findOne({_id: $stateParams.faqId});
});
}
}
I've tried this but as you may know, $stateParams is not defiend in the data.title area.
The other method I've tried is this one:
// ATEMPT TO SET THE TITLE DYNAMICALLY WITH THE QUESTION
$scope.autorun(() => {
console.log($scope.getReactively('theFaq'));
if ($scope.theFaq) {
let faqTitle = $scope.getReactively('theFaq').question;
// $state.get('faq-detail').data.title = faqTitle;
$state.current.data.title = faqTitle;
}
});
But this instead sets the title after I load another view with the same controller. So now I have the title from the last visited view instead of the current one.
Question
How do I set the page title from an object key value returned from mongo collection in angular-meteor?

For me, the cleanest way to achieve what you want is to do it in your controller. For example :
angular.module('mymodule').controller('FaqListCtrl', function($stateParams, $window){
let title = Faq.findOne({_id: $stateParams.faqId}).question;
$window.document.title = title;
})

Related

Dynamic URLs in angular

I currently Angular 1.6 and angular-ui/ui-router
I have a problem with dynamic url which I don't know a limit of parameter
example
www.example.com/name/hello/address/telephone/postcode/city/ .... (n)
or
www.example.com/name/hello/school/age/weight/height/...(n)
or
www.example.com/name/hello/friends/family/age/address
from the example url I need to using ui-route to manage that url and get value parameters from url and just implement only one state.
expected result
let result = [hello,address,telephone,postcode,city, .... (n)];
example route (*only one state handle this case)
.state('person', {
url: '/name/:param1/:param2', (which I want to dynamic)
component: 'person',
resolve: {
search:($transition$) {
return $transition$.params();
}
}
})
Please advice :)
Update :
I found a good solution from this link
Recursive ui router nested views
you can make a function which takes your array input and return an state object.
function generateState(array){
var url = a.reduce(function(url, item){ return url+":"+item+"/" }, "/name/");
return {
url: url,
component: 'person',
resolve: {
search:($transition$)=> {
return $transition$.params();
}
}
}
}
after which you can dynamically register the state using $stateProvider

how to bind the relevant value in case of current status in ui-router?

I have attached data attribute in each .state to identify the user (authenticated or public) as following (one state example)
$stateProvider
.state('admin-panel.public.home', {
url: '/p',
templateUrl: 'app/public/home.tmpl.html',
controller: 'PublicHomeController',
controllerAs: 'ctrl',
data: {
requireLogin: false
}
});
I need to use the some state for both of user (authenticated and public) as an example
.state('403', {
url: '/403',
templateUrl: '403.tmpl.html',
controller: function($scope, $state, APP, Auth) {
$scope.app = APP;
$scope.goHome = function() {
if(Auth.isAuthenticated()){
$scope.requireLogin = true;
$state.go('admin-panel.default.home');
}
else{
$scope.requireLogin = false;
$state.go('admin-panel.public.home');
}
};
},
data: {
requireLogin: $scope.requireLogin
}
})
Here when the authenticated user access this state I need to pass the true value to requireLogin: true as well when public user access this state I need to pass the false value as requireLogin: false. I checked the current user status in the controller as above. How can I bind the $scope.requireLogin to data attribute?
Anyone in expert of ui-router please tell a way to solve???
You can solve your problem in a very cleaner way. Let's start with a global controller example GlobalCtrl which is added to the body or html tag like ng-controller="GlobalCtrl.
Doing this will enable us to keep the scope of this GlobalCtrl throughout your single page Angular app (as you are using ui-router) and we can avoid the usage of $rootScope (actually mimicking the usage of $rootScope).
Now, inside your GlobalCtrl define something like this:
// Using an object to avoid the scope inheritance problem of Angular
// https://github.com/angular/angular.js/wiki/Understanding-Scopes
$scope.globalData = {};
// Will be called everytime before you start navigating to any state
$scope.$on('$stateChangeStart', function(event, toState, toParams) {
$scope.globalData.requireLogin = false;
var statesToLoginCheck = ['403', 'foo', 'bar']; // and other states on which you want to check if user is logged in or not
// If the current state on which we are navingating is allowed to check for login
if (statesToLoginCheck.indexOf(toState.name) > -1) {
if (Auth.isAuthenticated()) {
$scope.globalData.requireLogin = true;
$state.go('admin-panel.default.home');
} else {
$scope.globalData.requireLogin = false;
$state.go('admin-panel.public.home');
}
event.preventDefault();
return;
}
});
Now, since $scope of GlobalCtrl is in body or html then every state or directive will inherit the scope of this GlobalCtrl and then you simply have to check in your any controller of variable $scope.globalData.requireLogin.

How to pass parameters to a controller when using route?

In my application, am loading views using ngRoute,
.state('ReportViewer', {
url: "/ReportViewer",
controller: 'ReportViewerControl',
templateUrl: "views/Report_viewer.html"
})
I have a search Panel where users can search reports, and when selecting a report, it should route to a different page and load the report inside an iframe. Here is my code when the user select the report,
$scope.goReport = function(report){
$state.go('ReportViewer');
}
I have defined a constant in the config which changes dynamically based on the report selection
.constant('Digin_ReportViewer','http://192.226.192.147:8080/api/repos/%3Ahome%3Aadmin%')
Here i need to pass the 'Report' variable to the ReportViewerControl when the user select the report,
Here is my ReportViewerControl
routerApp.controller('ReportViewerControl', ['$scope', '$routeParams','Digin_ReportViewer',function($scope, $routeParams,Digin_ReportViewer) {
//here i need to append the report url ,
$scope.reportURL = Digin_ReportViewer+routeParams.report ;
$scope.trustSrc = function(src) {
return $sce.trustAsResourceUrl(src);
}
}
]);
you are using ui-router for confiuring routes ,but below in ReportController you are using $routeParams.I hope you have to use $stateParams for that.
routerApp.controller('ReportViewerControl', ['$scope', '$stateParams','Digin_ReportViewer',function($scope, $stateParams,Digin_ReportViewer) {
//here i need to append the report url ,
$scope.reportURL = Digin_ReportViewer+stateParams.report ;
$scope.trustSrc = function(src) {
return $sce.trustAsResourceUrl(src);
}
}
]);
also you have to pass the params from url or in method like this
.state('ReportViewer', {
url: "/ReportViewer",
controller: 'ReportViewerControl',
templateUrl: "views/Report_viewer.html",
params: ['report']
})
Then you can navigate to it like so:
$scope.goReport = function(report){
$state.go('ReportViewer', { 'report':'monthly' });
}
Or:
var result = { 'report':'monthly' };
$state.go('ReportViewer', result);

angularjs i18n routing with path params

In one application that I'm working with, the route system need to be integrated with i18n, like the example below:
$routeProvider.when('/:i18n/section', ...);
But I'm facing some issues due to, what I guess it is, the $digest cycle, which doesn't change the i18n param at the runtime.
Other issue that I'm facing is, if the location path is pointed to something like:
http://localhost:9000/section/...
not like:
http://localhost:9000/en/section/...
the i18n path param ends being associated with /section/, which means, on $routeParams service, $routeParams.i18n = 'section';. This is expected, but I need to be able to parse the /:i18n/ param, to avoid this conflicts, and change the URL, concatenating one locale, to contextualize the session, replacing the current route with the new one, i18n-ized, whithout refreshing the view/app automatically, yet selectivelly, because some features only need to be changed, not all.
Also, I've designed one service that evaluates, based on a list of possible language settings and its weights, the language that'll be selected to the current context:
var criteria = {
default: {
tag: 'en',
weight: 10
},
user: {
tag: 'pt',
weight: 20
},
cookie: {
tag: 'zh',
weight: 30
},
url: {
tag: 'ru',
weight: 40
},
runtime: {
tag: 'es',
weight: 50
}
};
function chooseLanguage (languages) {
var deferred = $q.defer();
var weights = [];
var competitors = {};
var choosen = null;
if (defineType(languages) === 'array') {
for (var i = languages.length - 1; i >= 0; i--) {
if (languages[i].tag !== null) {
weights.push(languages[i].weight);
competitors[languages[i].weight] = languages[i];
}
}
choosen = competitors[Math.max.apply(Math, weights)];
} else if (defineType(languages) === 'object') {
choosen = languages;
} else {
return;
}
setRuntimeLanguage(choosen.tag);
deferred.resolve(choosen);
return deferred.promise;
}
Explaining the code above, when angular bootstraps the app, the snippet is executed, selecting which language is defined and if its strong enough to be selected. Other methods are related to this operation, like de detection of the URL param, if there's one logged user, etc, and the process is executed not only on the bootstrap, but on several contexts: $routeChangeStart event, when the user autenticates its session, switching the languages on a select box, and so on.
So, in resume, i need to be able to:
Parse the URL and apply the locale param properly, if its not informed initialy;
Change the URL i18n param during the runtime, whithout reloading the whole view/app;
Deal with the language changes correctly, which means, if my approach based on weights isn't the better way to go, if you suggest me something else.
Edit 1:
A $watcher doesn't do the trick because the app needs the correct locale in every path, even before it instantiates all the elements. AngularJS is used in every step of this check, but if there's any clue to do this outside, before Angular instantiates, we can discuss about it.
For now, I'm using the accepted answer below, with a solution that I developed, but it has to be improved.
I've ended up doing a kind of preliminary parse on the URL. Using only ngRoute (ui-router wasn't an option...), I check if the path matches with the restrictions, if not, a redirect is triggered, defining correctly the path.
Below, follows a snippet of the solution, for the primary route, and a simple subsequent example, due to the quantity of the routes on the app, and their specific data, that doesn't belongs to the basis idea:
$routeProvider
.otherwise({
redirectTo: function () {
return '/404/';
}
})
.when('/:i18n/', {
redirectPath: '/:i18n/',
templateUrl: 'home.html',
controller: 'HomeCtrl',
resolve: {
i18n: [
'$location',
'$route',
'i18nService',
function ($location, $route, i18nService) {
var params = $route.current.params;
return i18nService.init(params.i18n)
.then(null, function (fallback) {
if (params.i18n === '404') {
return $location.path('/' + params.i18n + '/404/').search({}).replace();
}
$location.path('/' + fallback).search({}).replace();
});
}
],
data: [
'dataService',
function (dataService) {
return dataService.resolve();
}
]
}
})
.when('/:i18n/search/:search/', {
redirectPath: '/:i18n/search/:search/',
templateUrl: 'search.html',
controller: 'SearchCtrl',
resolve: {
i18n: [
'$location',
'$route',
'i18nService',
function ($location, $route, i18nService) {
var params = $route.current.params;
return i18nService.init(params.i18n)
.then(null, function (fallback) {
$location.path('/' + fallback + '/search/').search({
search: params.search
}).replace();
});
}
],
data: [
'$route',
'searchService',
function ($route, searchService) {
var params = $route.current.params;
return searchService.resolve({
'search': params.search
});
}
]
}
});
The redirectPath property is used in case of you need the pattern for that route, because ngRoute keeps one copy for private use, but doesn't give access to it with $route public properties.
Due to the required parse before any app request (except the i18nService, which loads the locale list and triggers angular-translate to load the translations), this methods causes several redirects, which leads to a significant delay on the instantiation.
If any improvements are possible, I'll thanks for, also, if I found a better way to do it, I'll update this answer with it.

Is there any way to get a list of child states in angular-ui-router?

I am building an angular app and for part of the app, I want to have a menu with options dynamically populated based on available states. Here is a simplified piece of my state config:
$stateProvider.state('root.type.list.controls', {
url: '/controls',
templateUrl: 'views/controls.html',
controller: 'ControlsCtrl',
data: {
title: 'Controls'
}
})
.state('root.type.list.items', {
url: '/items',
templateUrl: 'views/items.html',
controller: 'ItemsCtrl',
data: {
title: 'Items'
}
})
I'd like to be able to populate a dropdown menu with the titles from every immediate child of the 'root.type.list' state, based on what is in the data section of the config. Is there any way to get a list of all child states for a particular state?
Ended up using $state.get() and doing the following:
function _children(stateName) {
return _.filter($state.get(), function(config) {
return stateName === config.name.slice(0, stateName.length) &&
config.name.split('.').length === stateName.split('.').length + 1;
});
}

Categories