AngularJS directive using a service as its scope - angularjs

I have a simple view problem that I may have made more difficult by trying to create my first directive in Angular. I have this service that returns an array of user roles for the current user. Many users are only going to have the user role. With that in mind I don't want to display anything on their profile page. However, some people like myself will have User and Admin roles. So on the profile page I would like to display a drop down to change the current role.
I thought I'd create a directive that did a check basically
if user.roles.length != 1 then show drop down else remove the element from the dom.
Like I said before I have never created a directive before and they seem like the right way to go but I am not getting the result I would like.
I have this:
app.directive('selector', function (SessionState, $compile) {
return {
priority: 100000,
scope: false,
compile: function (element, attr, linker) {
var data = SessionState.User.Data.roles;
var multiRoleView = '<p>Active Role:</p><select ng-model="State.User.activeRole" ng-options="role for role in data"></select>';
if (data.length != 1) {
element.html(multiRoleView).show();
$compile(element.contents());
} else {
element.children.remove();
element.remove();
}
return function linkFn() {
/* Optional */
}
}
}
});
This will render the html correctly depending on if that user should see it, but for admins it doesn't display any roles because I am assuming my data variable is never being used.

I hate to post a bad question but with some fiddling around and reading the documentation for $compile I found that by setting the scope variable to true. I could simply use the parents scope and that made everything very easy.
The completed and correct code:
Live link: https://app.baileysproject.com
Set scope to true, uses parents scope.
Removed the element.children.remove(), throws errors.
app.directive('selector', function (SessionState, $compile) {
return {
priority: 100000,
scope: true,
compile: function (element, attr, linker) {
var multiRoleView = '<p>Active Role:</p><select ng-model="State.User.activeRole" ng-options="role for role in State.User.Data.roles"></select>';
if (data.length != 1) {
element.html(multiRoleView).show();
$compile(element.contents());
} else {
element.remove();
}
return function linkFn() {
/* Optional */
}
}
}
});

Related

How to call an Angular method when browser closes

I'm relatively new in AngularJS and I have been asked to modify our application so that when the user closes the browser, we also log them out of Auth0 service.
I have the code below but I can't get it to fire. Kindly help.
$rootScope.$on("$destroy", function() {
lockService.logout();
});
lockService.logout() holds the functionality that successfully logs out the user when they click logout button, including the Auth0 signout function.
Somewhere I read that I can use the $on.$destroy of the main Controller but I am not sure how to do this. The above code is in fact inside the mainController function but it still doesn't work.
This is where I found my answer: https://stackoverflow.com/a/36444134/1168597
You can add a directive like this to your body element.
<body body-unload></body>
app.directive('bodyUnload', [
function () {
return {
restrict: 'A',
replace: false,
link: function (scope, element) {
function cleanupApp(){
// do cleanup here
}
element[0].onbeforeunload = function() {
cleanupApp();
};
}
};
}
]);
I have found a much cleaner and more effective approach.
$rootScope.$on('$locationChangeStart', function () {
if (!($window.performance.navigation.type === 1) && lockService.isAuthenticated()) {
$rootScope.logout();
}
});
So here if window.performance.navigation.type is 1, it means the page was refresh-ed. What I do then is if it is not refreshed AND my lockService.isAuthenticated() returns true, I logout the user.

Angular Custom Directive - How to make it Behave Like Ng-if

I'm trying to build a custom directive to either show items on a page or completely remove them based on authorization data, and I'm clearly missing something, 'cuz its not working and I'm having a hard time figuring out why.
I've been following the guide here:
http://adamalbrecht.com/2014/09/22/authorization-with-angular-and-ui-router/
Which said to take a copy of the NgIf source code, and modify it (as I have below).
I'm really confused because not only is it not working as expected, but even when I put break-points within the function calls in the directive, it never hits those break points.
I'm not sure what I'm doing wrong. Is there something else not documented within the steps that I need to do in order to use a custom directive, or did I somehow miss a step? Regular ng-if's work on the same page just fine.
EDIT: I should note that AuthorizeService.isAuthorizedForOne returns a promise value which is either true or false. This works fine in other contexts.
'use strict';
/**
* #ngdoc directive
* #name ngIfPermission
* #restrict A
*
* #description
**/
var ngIfPermissionDirective = ['$animate', function($animate, AuthorizeService) {
return {
multiElement: true,
transclude: 'element',
priority: 600,
terminal: true,
restrict: 'A',
$$tlb: true,
link: function($scope, $element, $attr, ctrl, $transclude) {
console.log("I am here");
var block, childScope, previousElements;
$attr.$observe("ngIfPermission", function(value){
console.log("I am there");
var permissions = JSON.parse(value.replace(/'/g, '"'));
AuthorizeService.isAuthorizedForOne(permissions).then(function(auth){
if (!childScope) {
$transclude(function(clone, newScope) {
childScope = newScope;
clone[clone.length++] = document.createComment(' end ngIfPermission: ' + $attr.ngIfPermission + ' ');
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when its template arrives.
block = {
clone: clone
};
$animate.enter(clone, $element.parent(), $element);
});
}
},
function(err) {
if (previousElements) {
previousElements.remove();
previousElements = null;
}
if (childScope) {
childScope.$destroy();
childScope = null;
}
if (block) {
previousElements = getBlockNodes(block.clone);
$animate.leave(previousElements).then(function() {
previousElements = null;
});
block = null;
}
});
});
}
};
}];
How I'm referencing it:
<div ng-if-permission="['OOGY']">You can see this.</div>
<div ng-if-permission='["BOOGY"]'>or this</div>
I think you might got the declaration for the directive wrong.
app.directive( 'ngIfPermissionDirective', function($animate){
//directive here
));
DEMO http://plnkr.co/edit/BhubrfMAiW3K4ANI3pTx

implementing external links to a page with angular-scroll and ng-route

So this is is an angularjs app.
I have implemented this angular-scroll api :https://github.com/oblador/angular-scroll, to show a catalog of products, where the content is loaded from db. this catalog has all the subcategories (with its products) and every subcategory has an anchor identified like: anchor+categoryId.
So from the menu , i click a category and it scroll nicely to the correct section.
The problem arise when I need to create some links from other pages of the site, to go to an specific section category inside the catalog. Because I have ng-route, i need to create a new url to redirect to the catalog, and there capture when the content is loaded to do the scroll to the required category.
BUT I have a directive associated with the route of the catalog, that looks for the partials depending on the domain of the client, so to show the correct template i have to use an $http , get the content and replace it in my directive.
Because that I dont know how i can know when the content of the directive is ready to make the call to the scroll... better show some code here :
this is the route that is receiving the call
$routeProvider.
when('/products/category/:categoryId/page/:page/anchor/:anchorId?', {
template:'<product-display-view></product-display-view>',
controller: 'ProductListCtrl',
access: {
authorizedRoles: [USER_ROLES.all]
},
resolve: {
wait : 'waitForIt',
prefetchDataProducts: ['waitForIt','$route','SearchService',
function(waitForIt,$route,SearchService) {
return waitForIt.then(function() {
return SearchService.getProducts($route.current.params.categoryId,$route.current.params.page);
});
}],
prefetchDataCategories:['waitForIt','CategoryService',
function(waitForIt,CategoryService) {
return waitForIt.then(function() {
return CategoryService.getCategories();
});
}]
}
}).
this is the directive product-display
productDirectives.directive('productDisplayView',['$rootScope','$compile','$http','$templateCache' ,'$document',
function($rootScope,$compile, $http, $templateCache,$document){
return {
restrict: 'E',
link: function (scope, element, attrs) {
var templateUrl = 'users/catwizardAngularCore/app/partials/themes/' + scope.app.theme.themeName + '/partials/product-display.html';
$http.get(templateUrl, {cache: $templateCache})
.success(function (templateContent) {
element.replaceWith($compile(templateContent)(scope));
});
/* this doesn't work because the someElement doesn't exist*/
var newHash = 'anchor' + scope.anchorId;
var someElement = angular.element(document.getElementById(newHash));
angular.element(someElement).ready(function () {
$document.scrollToElement(someElement, 200, 2000);
});
}
}]);
There is a duplicate question with the correct answer, but it has not been accepted yet so I am copying the answer here.
The $anchorScroll has to occur after the page has been rendered,
otherwise the anchor doesn't exist. This can be achieved using
$timeout().
$timeout(function() {
$anchorScroll('myAnchor');
});
Credits to Tony

Accesing javascript variable in angularjs

I am trying to accessing javascript variable and then change it in controller and then use it in route.. but it is showing the same old one.
var userEditid = 0;
app.controller("cn_newuser", function ($scope,$window) {
editUser = function (this_) {
$window.userEditid = this_.firstElementChild.value;
alert(userEditid);
//window.location.path("#/edit_user");
//$window.location.href = "#/edit_user";
}
route
.when("/edit_user", {
templateUrl: "/master/edituser/" + userEditid //userEditid should be 3 or some thing else but is showing 0
})
in abobe route userEditid should be 3 or some thing else but is showing 0
This is because the .when() is evaluated on app instantiation, when this global var (which is really not a great idea anyways) is set to 0, rather than when your controller is run. If you are trying to say, "load the template, but the template name should vary based on a particular variable," then the way you are doing it isn't going to work.
I would do 2 things here:
Save your variable in a service, so you don't play with global vars
Have a master template, which uses an ng-include, which has the template determined by that var
Variable in a service:
app.controller("cn_newuser", function ($scope,UserIdService) {
$scope.editUser = function (this_) {
UserIdService.userEditid = this_.firstElementChild.value;
$location.path('/edit_user');
}
});
also note that I changed it to use $location.path()
Master template:
.when("/edit_user", {
templateUrl: "/master/edituser.html", controller: 'EditUser'
});
EditUser controller:
app.controller("EditUser", function ($scope,UserIdService) {
$scope.userTemplate = '/master/edituser/'+UserIdService.userEditId;
});
And then edituser.html would be:
<div ng-include="userTemplate"></div>
Of course, I would ask why you would want a separate template per user, rather than a single template that is dynamically modified by Angular, but that is not what you asked.
EDITED:
Service would be something like
app.factory('UserIdService',function() {
return {
userEditId: null
}
});
As simple as that.

Struggling with my first 'in-depth' directive: an image/video gallery

I'm trying to build a relatively simple gallery as a directive so I can use it on a couple different templates on my site. Here' what I have so far. The item attr accepts a json object which will contain an 'images' array of objects, each with a url and a label. It also has an optional video property containing a youtube video id. I have a separate youtube embed directive already working that this gallery can use. The $scope.gallery object is intended to contain information about the current state of the gallery as well as functions to modify it. I'm having trouble accessing the item object from within my gallery object. I'm not sure that I'm using the linking function correctly.
app.directive('gallery', function () {
return {
restrict: 'E',
replace: true,
scope: {item: '='},
templateUrl: './app/directives/templates/gallery.html',
link: function ($scope, elem, attrs) {
// this works
console.log($scope);
/* this logs 'undefined' even though the
previous log shows a non empty item property */
console.log($scope.item);
$scope.gallery = {
currentImage: 0,
video: false,
/* I need to set count equal to the number
of images in a given item, but again can't access
$scope.item to see the images property */
count: 5,
next: function () {
if (this.video === true) {
if (this.currentImage < $scope.item.images.length) {
this.currentImage += 1;
}
} else {
if (this.currentImage < $scope.item.images.length -1) {
this.currentImage += 1;
}
}
},
previous: function () {
if (this.currentImage > 0) {
this.currentImage -= 1;
}
},
select: function (index) {
this.currentImage = index;
}
};
}
};
});
I assume that you use some Developer Tools like that of Chrome, right?
In the devtools, if you console.log() an object which is not a primitive value, it will be a dynamic presentation, that means the inner fields will be evaluated only when you read it. Instead, for a primitive value, including undefined, it will be printed as string immediately. When your code was run, $scope.item may have not got the object (sometimes the values are requested remotely right?). But that happened very soon after that. So this is why you can see item in $scope but $scope.item is undefined.
I would suggest that you change all this reference in your functions to $scope.gallery .

Resources