using $rootScope in templateUrl function - angularjs

I just started with angularJS and I have a question:
How can I access a variable defined with $rootScope in a templateUrl function?
Here is my code:
myApp.config(['$routeProvider',
function($routeProvider, $rootScope) {
$routeProvider.
when( '/', {
templateUrl: 'partials/login.html',
controller: 'loginCtrl'
}).
when( '/home', {
templateUrl: function($rootScope){
console.log($rootScope.utilisateur.user.role_id);
if ($rootScope.utilisateur.user.role_id==2){
return 'partials/home.html';
}
else return 'partials/login.html';
},
controller: 'homeCtrl'
}).
otherwise({redirectTo:'/'});
}]);
It tells me that utilisateur is undefined.
I defined it in the index controller:
$rootScope.utilisateur = null;
$rootScope.rapports = null;
And then in the LoginCtrl:
var user = Authentification.login(email,password);
user.success(function(response){
$rootScope.utilisateur = response;
console.log($rootScope.utilisateur);
$location.path('/home');
});

You cannot use the $rootScope inside of the config block, as the config block runs before the $rootScope is created. Constants and providers may be used inside of the config block instead. If constants are not an option for you, you may want to redirect to the correct url inside of your homeCtrl.
[EDIT] Added possible solution from comment below:
Option 1: Have 2 different routes
/admin/home
/home
Option 2: Switch templates according to permission inside of controller/view
home.html
<div ng-switch="utilisateur.user.role_id">
<div ng-switch-when="2">
<!-- is admin -->
</div>
<div ng-switch-default>
<!-- not admin -->
</div>
</div>
Not the ideal solution, but it'd work for what you are trying to do, based on your comments below

Your problem seems like you have two different views, and on condition base you have to redirect views.
Pass the parameters in url like from your views (Create/Edit link etc.) As i have set the cookie on login and accessed here as a parameters or you can use different way to access the parameters.
''
And your config in $routeProvider use like this:
$routeProvider.when("/editTest/:ID/:flagTab",
{
templateUrl: function (params) {
var flag = params.flagTab;
if (flag == 'tabPanelView') {
return '/apps/templates/editTest.html';
}
else {
return '/apps/templates/editTestPageView.html';
}
},
controller: 'EditTestController'
});.
Refere a link swtiching views in routeProvider based on condition

Instead of describing different ways to achieve what you would like to, the code snippet below answers the actual question that was asked:
How can I access a variable defined with $rootScope in a templateUrl function?
The answers above and here imply there is no way to reference $rootScope within the config/$routeProvider. While that may strictly be true, there is a simple way to access the $rootScope through $routeProvider. Below is how to do this:
Sample HTML:
<div ng-app="myApp">
<div ng-controller="masterController">
<script type="text/ng-template" id="home.html">{{something}}</script>
Page
<div ng-view></div>
</div>
</div>
Sample javascript:
var myApp = angular.module('myApp',[],function($routeProvider) {
$routeProvider
.when('/home',{templateUrl:'home.html'})
.when('/page',
{
template:'<p>{{something}}</p>',
controller:'masterCtrlWrapper'
})
.otherwise({redirectTo:'/home'});
});
myApp.controller('masterCtrlWrapper', function($rootScope)
{ $rootScope.changeVariable(); });
myApp.controller('masterController', function($rootScope)
{
$rootScope.something = 'This is the $rootScope talking.'
$rootScope.pressCount = 0;
$rootScope.changeVariable = function()
{ $rootScope.something = "This function was run " + $rootScope.pressCount++ + " times."; };
});

Like others have said, $rootScope doesn't exist yet, but unlike their answers, it doesn't at all mean you can't use it at all, you just have to code-in the wait.
Here is your example working but just printing since we dont have the templates.
myApp
.provide('home', function() {
var $rootScope = null;
this.templateUrl = function() {
var check =
$rootScope.utilisateur &&
$rootScope.utilisateur.user.role_id
;
console.log(check);
return (check) ? 'partials/home.html' : 'partials/login.html';
};
this.controller = 'homeCtrl';
this.resolve = {load:['$rootScope', function(fetched) { $rootScope = fetched; }]};
this.$get = function() { };
})
.config(['$routeProvider', 'homeProvider', function($routeProvider, homeProvider) {
$routeProvider
.when('/', {
templateUrl : 'partials/login.html',
controller : 'loginCtrl'
})
.when('/home', homeProvider)
.otherwise({redirectTo:'/'})
;
}])
;

Related

AngularJS, string not interpolating $routeParams values

I am trying to build a link using routeParams of the route angularJs class. which works pretty well but for some reason it doesn't interpolate my strings.
I have tried the following:
{{username}} as in the controller i set $scope.username = $routeParams.username;
{{ Repo.username }} as the controller is called RepoController.
however both had no result except printing it as a string literal on the screen.my code is as below
App.js
(function() {
var app = angular.module("githubViewer", ["ngRoute"])
app.config(function($routeProvider) {
$routeProvider
.when("/main", {
templateUrl: "main.html",
controller: "MainController"
})
.when("/user/:username", {
templateUrl: "user.html",
controller: "UserController"
})
.when("/repo/:username/:reponame", {
templateUrl: "repo.html",
controller: "RepoController"
})
.otherwise({
redirectTo: "/main"
})
});
}());
RepoController.js
(function() {
var app = angular.module("githubViewer")
var RepoController = function($scope, github, $routeParams) {
$scope.username = $routeParams.username;
$scope.reponame = $routeParams.reponame;
app.controller("RepoController", ["$scope", "github", "$routeParams", RepoController]);
}());
Repo.html
<section>
{{ username }}
<br />
{{ repo.name }}
</section>
There is a plunker available:
https://plnkr.co/edit/oGJJOUfCqW8G7OAXxXGa?p=preview
thanks a lot for any help. Cheers!
There are a couple of syntactic and semantic issues in the Plunkr that may be affecting your actual code.
You have a syntax error in the RepoController.js -- you do not close the RepoController function declaration with }
You are not including <script src=RepoController.js> in index.html
$scope.repo is not an object with a name property. In your template, use reponame instead or you could do $scope.repo = {name: $routeParams.reponame}

ui-router can't change value from $scope after view was called

I'm starting using angularjs for my new websites, so I'm a beginner.
I have a problem, which I can't change the value from my $scope inside my controller after the view was called.
I'm using ui-router to multiple views.
I explain in this example:
<html lang="pt" ng-app="myApp">{...}
<header ng-controller="siteHeader"><div ng-show="mySlogan"></div></header><div ui-view="content"></div>
In my js:
var app = angular.module('myApp', ['ui.router']); app.config(['$locationProvider','$stateProvider','$urlRouterProvider'
,function($locationProvider,$stateProvider,$urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('home',{
url: '/',
views: {
'content':{
templateUrl: 'templates/main.html',
controller: 'siteHeader'
}
}
})
.state('login',{
url: '/login',
views: {
'content':{
templateUrl: 'templates/login.html',
controller: 'siteHeader'
}
}
});}]);
app.controller('siteHeader',['$scope','$location',function($scope, $location){
if($location.path()=="/")
$scope.mySlogan = true;
else
$scope.mySlogan = false;}]);
The controller works at first time as page is load, but when the path was changed my $scope.mySlogan does nothing.
What I'm doing wrong?
Use the $state service instead of $location and get the URL as following :
$state.url
Then, try to create a dedicated controller for each route. This will avoid using extensively conditions just to match specific route's template requirements.
The code is only executed once in your controller assigned to <header>. The controllers assigned to your views are just other instances of siteHeader controller and define a new mySlogan variable in their own scope. So, changing the value there won't affect the value in the header scope.
To solve your problem, you could watch the location path:
$scope.$watch(
function() {
return $location.path();
}, function (newValue) {
if (newValue === '/')
$scope.mySlogan = true;
else
$scope.mySlogan = false;
});
Or a better way would be to listen to the ui-router events:
app.controller('siteHeader',['$scope',
function($scope){
$scope.$on('$stateChangeSuccess',
function(event, toState) {
if ( toState.includes('home') )
$scope.mySlogan = true;
else
$scope.mySlogan = false;
});
}]);
You need to assign the controller only to your sideHeader and not to your actual views.

AngularJS - Show route only after all promises are resolved

I try to load route only after promises are resolved
var app = angular.module("thethaoso", ["ngRoute"]);
app.config(['$routeProvider', '$locationProvider', function ($routeProvider) {
$routeProvider
.when('/', {
resolve: {
message: function (repoService) {
return repoService.getMsg();
}
}
});
}]);
app.factory('repoService', function ($http) {
return {
getMsg: function () {
return "hihihi";
}
};
});
app.controller('teamLoadCtrl', function ($scope,message) {
$scope.message= message;
});
View:
<div ng-app='thethaoso' ng-controller='teamLoadCtrl'>
{{message}}
</div>
Always get the error Error: [$injector:unpr]http://errors.angularjs.org/1.3.7/$injector/unpr?p0=messageProvider%20%3C-%20message%20%3C-%20teamLoadCtrl
full code at http://jsfiddle.net/c0y38yp0/5/
Am I missing something ?
Thanks all.
The problem is that you have not specified a template and a controller to resolve the message object to. If you used the following syntax, it will work.
.when("/", {
templateUrl: "yourView.html",
controller: "yourController",
resolve: {
message: function(yourService){
return yourService.get();
}
}
Here is a working jsfiddle: http://jsfiddle.net/c0y38yp0/10/
You can also resolve the promise manually in your controller like so:
repoService.getMsg()
.then(function (msg) {
$scope.message = msg;
}
When the promise is resolved onto the scope as I did above, the ui will update. You can show a loading bar and use ng-hide to make the pages feel fluent while the loading occurs.
When you resolve, service have to return promise not value.
Here is example service
app.factory('repoService', function ($http, $q) {
var user = {};
var q = $q.defer();
$http.get('https://api.github.com/users/Serhioromano')
.success(function(json){
user = json;
q.resolve();
}).error(function(){
q.reject();
});
return {
promise: function() {
return q.promise;
},
get: function() {
return user;
}
};
});
The point here is that you return promise only. You handle how you save result. And then you can use this result like in get(). You know that by the time you call get() the user variable already set because promise was resolve.
Now in router.
app.controller('MainCtrl', function($scope, repoService) {
$scope.user = repoService.get();
});
app.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: '/view.html',
controller: 'MainCtrl',
resolve: {
message: function (repoService) {
return repoService.promise();
}
}
})
.otherwise({ redirectTo: '/' });
});
You return promise by repoService.promise()
In controller repoService.get() is triggered only after that promise resolved.
So you get your data.
Another thing in your code, you used ng-controller. But that thing is not binded to router and thus it avoid if it is resolved or not. You have to delete ng-controller and use controller router controller: 'MainCtrl',.
This affect your HTML
<body ng-app="myapp">
<ng-view></ng-view>
<script type="text/ng-template" id="/view.html">
<p>Hello {{user.name}}!</p>
</script>
<body>
You have to use <ng-view> to include subtemplate there and then in sub template you can use scope of the controller.
See plunker.
There are a few things wrong with the code you posted, in contrast to the code you are attempting to draw inspiration from.
When you resolve a route with the $routeProvider, the results are applied against an element <ng-view></ngview>, not a base element <div> as you have specified. Without the <ng-view> element, there is no way for the $routeProvider to bind the correct controller to the correct html fragment. Using ng-controller instantiates a controller instance when the dom element is rendered, and does not allow passing parameters to the controller as you have tried. Thus your resolution error due to an unknown message object. Effectively, message is not available outside the $routeProvider instance.

how to put the condition which set the template url to load

I would like to have two main pages (logged in, logged out), which have url like '/'.
Is it posiible to do it with ngRoute?
I was searching the soltion but everywhere was to use ui-router.
Now I have sth like:
$rootScope.$on('$routeChangeStart', function (event, next) {
if ( next.$$route.orginalPath == '/' && loggedIn ) {
next.$$route.templateUrl = "app/main/main-loggedIn.html"
} else {
next.$$route.templateUrl = "app/main/main-loggedOut.html"
}
});
and
angular.module('name').config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'app/main/main-loggedOut.html',
controller: 'MainCtrl'
})
});
but it doesn't work well. It changes the templateURL but it get data before that changings and load always loggedOut page.
I have also the solution to put both html in one file in set them ng-if, but I prefer to avoid it.
solution:
main.html
<div ng-include="mainTemplate"></div>
main.js
angular.module('bookkeepingApp').config(function ($routeProvider) {
$routeProvider.when('/', { controller: 'MainCtrl', templateUrl: "app/main/main.html" })
}).controller('MainCtrl', function ($scope, Auth) {
$scope.$watch(function($scope) {
return $scope.mainTemplate = Auth.isLoggedIn() ? 'app/main/main-loggedIn.html' : 'app/main/main-loggedOut.html';
})
});
I don't know how you're getting the loggedIn value, but templateUrl accepts a function as value, you just have to return the correct one, based on the information of the user. I guess the code above might work.
angular.module('name').config(function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'MainCtrl',
templateUrl: function() {
return loggedIn ? "app/main/main-loggedIn.html" : "app/main/main-loggedOut.html"
}
})
});
You can put a template within a template, i.e in your route "/" you have a
<div ng-include="scopeVariable">
</div>
the scope variable points to a loggedin/loggedout page depending on your needs.

Does AngularJS have dynamic routing?

Does angular support dynamic routing at all?
Maybe some trick like this:
$routeProvider.when('/:ctrl/:action',
getRoute($routeParams.ctrl,$routeParams.action))
function getRoute(ctrl, action){
return {
templateUrl: ctrl+"-"+action+".html"
controller: 'myCtrl'
}
}
Please help me, I need to get templateUrl based out of routeParams
This is a late answer but I came across this problem myself, but it turns out that the solution by Dan conflicts with ngAnimate classes on the ngView directive, and the view is shown but the ng-leave animation will immediately be applied and hide the view opened with his dynamic routing.
I found the perfect solution here, and it's available in 1.1.5 +
In the $routeProvider, the templateUrl value can be a function, and is passed the route parameters:
app.config(function ($routeProvider) {
$routeProvider
.when('/:page', {
templateUrl: function(routeParams){
return '/partials/'+routeParams.page+'.html';
}
})
});
Though the controller can't be given as a function so my solution is to give it in the template html as per usual with ng-controller="HomeCtrl".
Using this solution we can route by convention in Angular.
I hope this helps others who weren't keen on manually adding every route to the routeProvider.
You want to bring it down to the controller level.
In this example, I am overriding entire pages as well as partials by subdomain:
app.js
config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider.when('/', {
template: 'home'
});
$routeProvider.when('/contact', {
template: 'contact'
});
$routeProvider.otherwise({redirectTo: '/'});
}])
controllers.js
controller('AppController', ['$scope','Views', function($scope, Views) {
$scope.$on("$routeChangeSuccess",function( $currentRoute, $previousRoute ){
$scope.page = Views.returnView();
});
$scope.returnView = function(partial){
return Views.returnView(partial);
}
}])
services.js
factory('Views', function($location,$route,$routeParams,objExistsFilter) {
var viewsService = {};
var views = {
subdomain1:{
'home':'/views/subdomain1/home.html'
},
subdomain2:{
},
'global.header':'/views/global.header.html',
'global.footer':'/views/global.footer.html',
'home':'/views/home.html',
'home.carousel':'/views/home.carousel.html',
'contact':'/views/contact.html',
};
viewsService.returnView = function(partial) {
var y = (typeof partial === 'undefined')?$route.current.template:partial;
var x = $location.host().split(".");
return (x.length>2)?(objExistsFilter(views[x[0]][y]))?views[x[0]][y]:views[y]:views[y];
};
viewsService.returnViews = function() {
return views;
};
return viewsService;
}).
filters.js
filter('objExists', function () {
return function (property) {
try {
return property;
} catch (err) {
return null
}
};
});
index.html
<!doctype html>
<html lang="en" ng-controller="AppController">
<body>
<ng-include src="returnView('global.header')"></ng-include>
<ng-include src="page"></ng-include>
<ng-include src="returnView('global.footer')"></ng-include>
</body>
</html>

Resources