Directive to Hide/Show elements based on Session Service - AngularJS - javascript

Following up this answer, I was trying to build two directives to allow/deny elements to be visible by the end user.
angular.module('app.directives').directive('deny', ['SessionTool', function (SessionTool) {
return {
restrict: 'A',
link: function (scope, elem, attrs) {
scope.$watch(SessionTool.user, function (value, oldValue) {
var list = attrs.deny.split(',');
if (SessionTool.hasAnyRole(list))
return elem.hide();
return elem.show();
});
}
}
}]);
My problem is that when I do make the logon, the $watch function is not being called again to make the invisible element appear.
A resume of my SessionTool is listed below.
angular.module('app.tools').factory('SessionTool', ['$cookies', function ($cookies) {
var _cookieKey = 'user';
return {
user: {},
init: function () {
var u = $cookies.get(_cookieKey);
try {
u = angular.fromJson(u);
this.user = u;
} catch (e) {
console.log('invalid json');
}
},
login: function (u) {
this.user = u;
$cookies.putObject(_cookieKey, u, {path: '/'}); // #TODO encrypt the whole JSON before saving it to cookies.
},
...
};
}]);
Anybody could point out why the $watch isn't being fired?
Thanks in advance.

I think that your directive is currently watching an anonymous variable SessionTool.user in your directive scope not the actual variable. I suggest going with this approach instead.
angular.module('app.tools').factory('SessionTool', ['$cookies','$rootScope', function ($cookies) {
var _cookieKey = 'user';
var _user = {};
return {
setUser: function(user) {
_user = user;
$rootScope.$broadcast('SessionToolChange');
}
getUser: function() {
return _user;
}
init: function () {
var u = $cookies.get(_cookieKey);
try {
u = angular.fromJson(u);
this.user = u;
} catch (e) {
console.log('invalid json');
}
},
login: function (u) {
this.user = u;
$cookies.putObject(_cookieKey, u, {path: '/'}); // #TODO encrypt the whole JSON before saving it to cookies.
},
...
};
}]);
angular.module('app.directives').directive('deny', ['SessionTool', function (SessionTool) {
return {
restrict: 'A',
controller: function (scope, elem, attrs) {
scope.$on('SessionToolChange', function (value, oldValue) {
// get the user and do your stuff.
});
}
}
}]);

Related

AngularJS 1.6.8: Uncaught ReferenceError: $rootScope is not defined

Uncaught ReferenceError: $rootScope is not defined
I have a form and on submit button, I am trying to store data in localStorage.
I found this below solution here: How do I store data in local storage using Angularjs?
not sure what or where I am going wrong.
Below is the related code.
contact.component.js
angular.module('myApp')
.component('contactComponent', {
restrict: 'E',
$scope:{},
templateUrl:'contact/contact.html',
controller: contactController,
controllerAs: 'vm',
factory:'userService',
$rootscope:{}
});
function contactController($scope, $state,userService,$rootScope) {
$scope.navigate = function(home){
$state.go(home)
};
$scope.user = userService;
};
function userService($rootScope) {
var service = {
model: {
name: '',
email: '',
query:''
},
SaveState: function () {
sessionStorage.userService = angular.toJson(service.model);
},
RestoreState: function () {
service.model = angular.fromJson(sessionStorage.userService);
}
}
$rootScope.$on("savestate", service.SaveState);
$rootScope.$on("restorestate", service.RestoreState);
return service;
};
$rootScope.$on("$routeChangeStart", function (event, next, current) {
if (sessionStorage.restorestate == "true") {
$rootScope.$broadcast('restorestate'); //let everything know we need to restore state
sessionStorage.restorestate = false;
}
});
//let everthing know that we need to save state now.
window.onbeforeunload = function (event) {
$rootScope.$broadcast('savestate');
};

How to get value from directive in Angular

I neeed to pass a value from this part of the code in my directive to a controller, but not sure how to achieve that:
if (!scope.multiple) {
scope.model = value;
console.log(scope.model);
return;
}
I get the value in the console.log, I just don't know how to pass it to the controller.
This is the complete directive:
angular.module('quiz.directives')
.directive('fancySelect', function($rootScope, $timeout) {
return {
restrict: 'E',
templateUrl: 'templates/directives/fancySelect.html',
scope: {
title: '#',
model: '=',
options: '=',
multiple: '=',
enable: '=',
onChange: '&',
class: '#'
},
link: function(scope) {
scope.showOptions = false;
scope.displayValues = [];
scope.$watch('enable', function(enable) {
if (!enable && scope.showOptions) {
scope.toggleShowOptions(false);
}
});
scope.toggleShowOptions = function(show) {
if (!scope.enable) {
return;
}
if (show === undefined) {
show = !scope.showOptions;
}
if (show) {
$rootScope.$broadcast('fancySelect:hideAll');
}
$timeout(function() {
scope.showOptions = show;
});
};
scope.toggleValue = function(value) {
if (!value) {
return;
}
if (!scope.multiple) {
scope.model = value;
console.log(scope.model);
return;
}
var index = scope.model.indexOf(value);
if (index >= 0) {
scope.model.splice(index, 1);
}
else {
scope.model.push(value);
}
if (scope.onChange) {
scope.onChange();
}
};
scope.getDisplayValues = function() {
if (!scope.options || !scope.model) {
return [];
}
if (!scope.multiple && scope.model) {
return scope.options.filter(function(opt) {
return opt.id == scope.model;
});
}
return scope.options.filter(function(opt) {
return scope.model.indexOf(opt.id) >= 0;
});
};
$rootScope.$on('fancySelect:hideAll', function() {
scope.showOptions = false;
});
}
};
});
Updated
I tried to do as suggested in the answers by #Zidane and defining my object first in the controller like this:
$scope.year = {};
var saveUser = function(user) {
$scope.profilePromise = UserService.save(user);
console.log($scope.year);
This is the template:
<fancy-select
title="Klassetrinn"
model="year"
options="years"
enable="true"
on-change="onChangeYears()"
active="yearsActive"
name="playerYear"
form-name="registerForm"
>
</fancy-select>
But I got an empty object in that case.
When I define my objects like this I get the right value in the controller but in the view the title is not being displayed anymore:
$scope.search = {
years: []
};
var saveUser = function(user) {
$scope.profilePromise = UserService.save(user);
console.log($scope.search.years);
<fancy-select
title="Klassetrinn"
model="search.years"
options="years"
enable="true"
on-change="onChangeYears()"
active="yearsActive"
name="playerYear"
form-name="registerForm"
>
</fancy-select>
As you defined an isolated scope for your directive like this
scope: {
...
model: '=',
...
},
you give your directive a reference to an object on your controller scope.
Declaring the directive like <fancy-select model="myModel" ....></fancy-select> you pass your directive a reference to scope.myModel on your controller. When you modify a property on the scope.model object in your directive you automatically modify the same property on the scope.myModel object in your controller.
So you have to do
myApp.controller('myController', function($scope) {
...
$scope.myModel = {};
...
}
in your controller and in your directive just do
if (!scope.multiple) {
scope.model.value = value;
return;
}
Then you can get the value in your controller via $scope.myModel.value.
For clarification: You have to define an object on your controller and pass the directive the reference for this object so that the directive can follow the reference and doesn't mask it. If you did in your directive scope.model = 33 then you would just mask the reference passed to it from the controller, which means scope.model wouldn't point to the object on the controller anymore. When you do scope.model.value = 33 then you actually follow the object reference and modify the object on the controller scope.
you can use services or factories to share data between your angular application parts, for example
angular.module('myapp').factory('myDataSharing', myDataSharing);
function myDataSharing() {
var sharedData = {
fieldOne: ''
};
return {
setData: setData,
getData: getData,
};
function setData(dataFieldValue) {
sharedData.fieldOne = dataFieldValue;
};
function getData() {
sharedData.fieldOne
};
directive:
myDataSharing.setData(dataValue);
controller:
angular.module('myapp').controller('myController' ['myDataSharing'], function(myDataSharing) {
var myDataFromSharedService = myDataSharing.getData();
}

Update directive in parent scope

I'm pretty new to angular js, currently, it's going well but now I have a question.
I have a template with a topnav and a contentpart. All with its own controller.
With a button I can open a "submenu" where I can choose data from the database, within the "Liquid"-section. Thats working pretty well.
Since the Topnav is rendered at the login of the page, the topnav wont be rendered again.
If I add a Liquid in the content section, I have to reload the data behind the "Liquid"-Dropdown.
That dropdown is encapsulated in a directive:
function liquidselect(Data){
return {
restrict: 'AE',
templateUrl: 'views/controls/liquid_select.html',
scope: {
selectedValues : '='
},
link: function(scope) {
},
controller: function ($scope) {
//
Data.post('ownrecipes').then(function (results) {
$scope.searchRes = results;
});
// debugger;
//$scope.searchRes = RecipeDataService.data;
$scope.disabled = undefined;
$scope.searchEnabled = false;
$scope.searchRes = [];
$scope.flavoring = {amount: {}};
$scope.updateModelValue = function (selected) {
// console.log(selected);
//
$scope.selectedValues = selected;
};
}
}
}
The communication to the server is done by a factory:
app.factory("Data", ['$http', 'toaster',
function ($http, toaster ) { // This service connects to our REST API
// var deffered = $q.defer();
var serviceBase = 'api/v1/';
var obj = {};
obj.toast = function (data) {
toaster.pop(data.status, "", data.message, 3000, 'trustedHtml');
}
obj.get = function (q) {
return $http.get(serviceBase + q).then(function (results) {
return results.data;
});
};
obj.post = function (q, object) {
return $http.post(serviceBase + q, object).then(function (results) {
return results.data;
});
};
obj.put = function (q, object) {
return $http.put(serviceBase + q, object).then(function (results) {
return results.data;
});
};
obj.delete = function (q) {
return $http.delete(serviceBase + q).then(function (results) {
return results.data;
});
};
return obj;
}]);
How can I update/reload the data of the directive from a child scope? Is there any chance?
Here is a plunker which showcases my problem and want I want to do:
http://plnkr.co/edit/bNANkQYZfBaS4CHH3dwX
I hope it's helpful for you :-)
Layout:
Basic Layout

AngularJS share data bettween parent and child scope directives

I have a widget like directive called waComments, it loads components via a RESTful service and displays them. In my view I'm using ng-repeat to loop over them and to render them with a button that if pressed Shows a new reply to form. This his handled by the waCommentsReply directive. One waComments widget has many child directives of type waCommentsReply. When the form is filled and submitted I want to add the new comment on top of my comments list. So both directives have to share the comments data.
I've tried to implement this here Sharing data between directives but without much success, the comment data is not updated when I add a new comment. I see that the RESTful API calls work and the data is returned, so this is not an issue.
Why is my implementation of Sharing data between directives not working in my case?
waCommentsReply directive:
waFrontend.directive('waCommentsReply', ['$rootScope', 'Comment', 'WaFormValidation', 'WaCommentStore', function($rootScope, Comment, WaFormValidation, WaCommentStore) {
return {
restrict: 'E',
templateUrl: '/stubs/comment-form.html',
transclude: true,
scope: {
replyTo: '#replyTo',
replyFormList: '=replyFormList',
loggedIn: '#loggedIn',
model: '#model',
id: '#id',
cancelButton: '#cancelButton'
},
controller: function($scope) {
$scope.comments = WaCommentStore;
if ($scope.cancelButton == undefined) {
$scope.cancelButton = true;
} else {
$scope.cancelButton = false;
}
$scope.comment = $scope.commentForm = {
Comment: {
author_name: '',
body: '',
model: $scope.model,
foreign_key: $scope.id,
parent_id: $scope.replyTo
}
};
$scope.$watch('replyFormList', function (newValue, oldValue) {
if (newValue) {
$scope.replyFormList = newValue;
}
});
if ($scope.loggedIn == undefined) {
$scope.loggedIn = false;
}
/**
* Handles the submission and response of a reply
*
* #return void
*/
$scope.reply = function() {
Comment.add($scope.comment).then(function(result) {
if (result.status == 'fail' || result.validation != undefined) {
$scope.validationErrors = result.validation;
WaFormValidation.validate(result.validation, $scope.commentForm);
} else if (result.status == 'success') {
//$scope.$parent.comments.unshift(result.data.comment);
//$scope.comments.unshift(result.data.comment);
$scope.comments.comments.unshift(result.data.comment);
//WaCommentStore.append($scope.model, $scope.id, result.data.comment);
$scope.comments, $scope.id, result.data.comment
$scope.comment = {};
$scope.replyFormList[$scope.replyTo] = false;
}
});
};
$scope.close = function() {
$scope.comment = {};
if ($scope.replyFormList[$scope.replyTo] != undefined) {
$scope.replyFormList[$scope.replyTo] = false;
}
}
}
};
}]);
WaCommentStore directive:
waFrontend.factory('WaCommentStore', function() {
return {
comments: []
};
});
waComments directive:
waFrontend.directive('waComments', ['$rootScope', 'Comment', 'WaCommentStore', function($rootScope, Comment, WaCommentStore) {
return {
restrict: 'E',
templateUrl: '/stubs/comments.html',
scope: {
model: '#commentModel',
id: '#commentFk'
},
controller: function($scope) {
$scope.comments = WaCommentStore;
$scope.loaded = false;
$scope.loadedMore = true;
$scope.currentPage = 1;
$scope.loggedIn = false;
$scope.paging = {};
$scope.replyFormList = {};
Comment.comments($scope.model, $scope.id).then(function(result) {
$scope.comments.comments.push.apply($scope.comments.comments, result.data.comments);
$scope.loggedIn = result.data.loggedIn;
$scope.paging = result.paging.Comment;
$scope.loaded = true;
});
$scope.loadMore = function() {
$scope.loadedMore = false;
if ($scope.paging.nextPage == false) {
//return false;
}
var options = {
page: $scope.paging.page + 1
};
Comment.comments($scope.model, $scope.id, options).then(function(result) {
$scope.comments.comments.push.apply($scope.comments.comments, result.data.comments);
$scope.paging = result.paging.Comment;
$scope.loadedMore = true;
});
};
$scope.submitComment = function() {
//alert($scope.author_name + $scope.body);
};
$scope.reply = function(replyId) {
$scope.replyFormList[replyId] = true;
}
}
};
}]);
since in both directive you defined scope: {} basically it means you defined those directives to use isolated scope.
with isolated scope, a scope/directive can't see what is in the parent scope.
however parent scope, can be affected by the child scope changes with 2 way binding definition.
https://docs.angularjs.org/guide/scope
try changing the shared data like this
waFrontend.factory('WaCommentStore', function() {
var comments = [];
var getComments = function() { return comments; }
var setComments = function(data) { comments = data; }
return {
getComments : getComments ,
setComments : setComments
};
});
I wanted to put it as a comments, but it would have been difficult to understand for you.
Please let me know if this works, else I will delete this answer.

AngularJS call method from controller in directive?

I have a problem: I use Angular and I need to use pushMsg method but I don't know how can I call it, boxCtrl.pushMsg(msg) does not work.
app.directive("fileread", function (socket) {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var msg = { author: 'me', class: 'me' };
// WHAT HERE???????
});
}
}
});
boxCtrl = function (socket, $scope) {
this.messages = [];
}
boxCtrl.prototype = {
pushMsg: function (message) {
this.messages.push(message);
}
}
app.controller('boxCtrl', boxCtrl);
You create an isolated scope and pass it as an attribute:
app.directive("fileread", function (socket) {
return {
scope: {
fileread: "=",
pushMessage: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var msg = { author: 'me', class: 'me' };
scope.pushMessage(msg);
});
}
}
});
And in your HTML:
<div fileread="..." push-message="pushMsg">
Edit: your controller should be something like this:
app.controller('Ctrl', function($scope) {
$scope.messages = [];
$scope.name = function(msg) {
$scope.messages.push(msg);
$scope.$apply(); //I think you need this to update the UI (not sure though)
}
})

Categories