Angular UI Router replace view on runtime - javascript

I'm trying to change a parent view template on runtime - inside a service.
My app config looks like:
$stateProvider
.state('base', {
abstract: true,
views: {
'header': {
controller: 'HeaderCtrl',
templateUrl: 'header.html'
},
'': {
template: '<div ui-view="main"></div>'
}
}
})
.state('base.home', {
url: '/',
views: {
'main': {
controller: 'SomeContentCtrl',
templateUrl: 'content.html'
}
}
});
I then have a service which is called from SomeContentCtrl that will listen for an event and upon such event I want to set the templateUrl for the header to null. Something like:
angular
.module('RemoveTemplate', [ ])
.factory('RemoveTemplate', ['$window', '$view', '$state',
function RemoveTemplate ( $window, $view, $state ) {
var windowElem = angular.element($window);
var listen = function ( ) {
windowElem.on('RemoveTemplate', function ( event ) {
$view.load('header#base', {
templateUrl: null
});
// Trying both, even tried without refreshing the state
$state.reload();
$state.go('wh.lobby');
});
};
return {
listen: listen
};
}
]);
});
But this isn't working at all. Have anyone came across a similar use case before?
Thanks

You can specify a templateURL function that runs each time you navigate to the state.
https://github.com/angular-ui/ui-router/wiki/Quick-Reference#template-templateurl-templateprovider
this method can check if it should supply a url or something else;
example:
$stateProvider.state('home', {
url: '/',
templateUrl: function () {
if (header === true) {
return 'app/templates/home.html';
} else {
return somethingElse;
}
}
})

If you want to 'hide' the header section without removing the html try to use a ng-show directive inside the header tag and check the actual state.
<div ng-show="state.is('base')">
This way you only need to inject the $state service in the controller.
When the state is base the div will show, if you navigate to child states it will hide.
p.s. $state.is returns a boolean, this method works with ng-if as well.

Related

Call a function before refreshing the page

When the user hit F5 or refresh button I need to call a function before refreshing the page, I tried the below code and it did'nt work, please suggest me what I'm doing wrong and is there a better way to do this.
I'm using Anuglar 1.5 with ui-router.
angular.module('module.name', [
]).controller('someController',['$scope', function($scope) {
$scope.$on("$stateChangeStart", function () {
functionToCallOnPageRefresh();
});
}]);
functionToCallOnPageRefresh() is not getting called on page refresh.
you need to create a parent state like as 'secure' and inherit every state in your application with that state like as-
angular.module('moduleName').config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$stateProvider.
state('secure', {
url: "/",
abstract: true,
templateUrl: "/path/to your/ master page",
resolve: {
factory: 'CheckRouting'
}
}).
state('dashboard', {
url: 'dashboard',
templateUrl: 'path/to your /template',
parent: 'secure',
});
}
]);
here i have mentioned
resolve: {
factory: 'CheckRouting'
}
in which CheckRouting inside resolve property, is a factory which is going to do some task (check user is login or not) on sate change or press f5.
in 'secure' state use 'resolve' property to execute a function if user press f5 or state change like as-
(function() {
'use strict';
angular.module('moduleName').factory('CheckRouting', ['$rootScope','$timeout' checkRouting]);
function checkRouting($rootScope,$timeout) {
if ( condition) { //user is loged in
return true;
} else {
$timeout(function() {
$state.go('login');
});
return false;
}
}
}());

angularjs - ui.router not rendering ui-view immediately

I am trying to use ui-router on my project.
Core module:
var core = angular.module('muhamo.core', ['angular-loading-bar', 'anguFixedHeaderTable', 'ui.router']);
Tracking module:
var app = angular.module(TRACKING_MODULE_NAME, ['muhamo.core']);
app.config(Configure);
Configure.$inject = ['$stateProvider', '$urlRouterProvider'];
function Configure($stateProvider, $urlRouterProvider) {
$stateProvider.state('contacts', {
templateUrl: '/static/partials/employee/employee-edit',
controller: function () {
this.title = 'My Contacts';
},
controllerAs: 'contact'
});
$urlRouterProvider.otherwise("/contacts");
console.log($stateProvider);
}
and the html definition :
<div ui-view></div>
It works fine if i click to a ui-sref link. But on page load it does not load the default view "/contacts". Am I missing something here?
UPDATE
It works after adding missing "url" property. But now I've another problem, if I extend my implementation like that :
function Configure($stateProvider, $urlRouterProvider) {
$stateProvider.state('employees', {
abstract: true,
url: "/employees"
/* Various other settings common to both child states */
}).state('employees.list', {
url: "", // Note the empty URL
templateUrl: '/static/partials/employee/employee-list'
});
$urlRouterProvider.otherwise("/employees");
console.log($stateProvider);
}
also with more states, ui-view is not rendering.
There are two fishy things in your implementation. You out an empty url and your default route is abstract. Try my changes below.
function Configure($stateProvider, $urlRouterProvider) {
$stateProvider.state('employees', {
abstract: true,
url: "/employees"
/* Various other settings common to both child states */
}).state('employees.list', {
url: "/list", // Note the empty URL
templateUrl: '/static/partials/employee/employee-list'
});
$urlRouterProvider.otherwise("/employees/list");
console.log($stateProvider);
Cheers
Yes. You need to set the state.url to '/contacts'
$stateProvider.state('contacts', {
url: '/contacts',
templateUrl: '/static/partials/employee/employee-edit',
controller: function () {
this.title = 'My Contacts';
},
controllerAs: 'contact'
});
It seems you forgot to set the url parameter, e.g.:
$stateProvider.state('contacts', {
url: "/contacts",
...
}

UI Router parent State for Authorization

I'm trying to create a parent state in my Route definition to handle Autorization like this:
$stateProvider.state('root', {
abstract: true,
resolve: {
auth: ['Auth',
function (Auth) {
return Auth.authorize()
}
]}
})
this parent state is defined in my other states like this:
.state('home', {
parent: 'root',
url: '/',
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
})
From my understanding of parent states and this post it should work just like this.
The Auth.authorize method works fine, since I can do a simple resolve on each state. Here's the function:
function authorize() {
var user = getCurrentUser();
if (user) return $q.when(user);
else {
$timeout(function() {
$state.go('login')
});
return $q.reject();
}
}
The response is ok, but the page doesn't get displayed. What am I missing here?

Angular routes not refreshing the view

I try to create an app with angular, and somehow the child state in not loading.
app.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('home', {
url : "/",
templateUrl: "/admin/home/"
})
.state('users', {
url : "/users/",
templateUrl: "/admin/users",
controller : function ($stateParams) {
console.log($stateParams);
console.log("users");
}
})
.state('users.new', {
url : "^/users/user/",
templateUrl: "/admin/users/new",
controller : function () {
console.log("users.new");
}
})
.state('users.user', {
url : "^/users/user/:uuid",
templateUrl: function ($stateParams) {
console.log($stateParams);
return "/admin/users/" + $stateParams.uuid
},
controller : function () {
console.log("users.user");
}
});
When I visiting the page
/users/user/55d8b1c706387b11480d60c1
I see the request to load the page, but only the "users" controller got executed.
This problem appears only with child states, switching between parent state working without problems.
I Using the latest versions of angular and ui-routes.
any ideas?
Please read this DOC
You mistake is in hierarchy. Child states use parent template as root and try find nested <ui-view>
You may add abstract state user without url, with empty template <ui-view></ui-view>, for nested viws. Then rename user, to E.G. user.index, it works for me
1st (It may works)
$stateProvider
.state('home', {
url : "/",
templateUrl: "/admin/home/"
})
.state('users', {
template: "<ui-view></ui-view>",
})
.state('users.index', {
url : "/users/",
templateUrl: "/admin/users",
controller : function ($stateParams) {
console.log($stateParams);
console.log("users");
}
})
.state('users.new', {
url : "^/users/user/",
templateUrl: "/admin/users/new",
controller : function () {
console.log("users.new");
}
})
.state('users.user', {
url : "^/users/user/:uuid",
templateUrl: function ($stateParams) {
console.log($stateParams);
return "/admin/users/" + $stateParams.uuid
},
controller : function () {
console.log("users.user");
}
});
2nd way is USE absulutly named views E.G.
views: {
"": { templateUrl: 'pages/menu.html' }, // relative noName view
"header": { templateUrl: "pages/header.html"}, //relative named view
"content#": { // Absolute name
template: "<ui-view></ui-view>",
controller: 'AuthController'}
}

AngularJS ui-router: how to resolve typical data globally for all routes?

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.

Categories