I am working on hiding or showing elements based on user role from an api. The directive works when I set the data.roleName in the code but when I try to set it by I service I need to resolve a promise before loading the rest of the directive though I keep getting "cannot read property of undefined errors Here's the code.
.js
app.directive('restrictTo', ['SecuritySvc', function (SecuritySvc) {
return {
restrict: 'EA',
replace: true,
transclude: true,
scope: {},
controller: ['$scope', '$attrs', '$q', 'SecuritySvc', function ($scope, $attrs, $q, SecuritySvc) {
var defer = $q.defer();
defer.promise.then(function ($scope, SecuritySvc) {
$scope.data = SecuritySvc.getRole();
});
defer.resolve();
if ($scope.data.roleName == $attrs.restrictTo) {
$scope.allowed = true;
} else {
$scope.allowed = false;
}
console.log($scope.data);
}],
template: '<div ng-show="{{ $scope.allowed }}" ng-transclude></div>'
}
}]);
.html
<div restrict-to="customer">
<div class="hero-unit">
<h1>Welcome!</h1>
<p>Hello, valued customer</p>
</div>
</div>
<div restrict-to="Admin">
<div class="hero-unit">
<h1>Admin Tools</h1>
<p>This shouldn't be visible right now</p>
</div>
</div>
If you dont want to use Q/defer and are using $resource you can do it this way:
app.directive('restrictTo', ['SecuritySvc', function (SecuritySvc) {
return {
restrict: 'AE',
replace: true,
transclude: true,
scope: {},
controller: ['$scope', '$attrs', 'SecuritySvc', function ($scope, $attrs, SecuritySvc) {
$scope.allowed = false;
SecuritySvc.getMyRole().$promise.then(function (data,attrs) {
if (data.roleName == $attrs.restrictTo) {
$scope.allowed = true;
} else {
$scope.allowed = false;
}
});
}],
template: '<div ng-show="allowed" ng-transclude></div>'
};
}]);
Not sure what your SecuritySvc is or returns. I think you should do it in a way like this:
var defer = $q.defer();
defer.resolve(SecuritySvc.getRole());
defer.promise.then(function (data) {
$scope.data = data;
if ($scope.data.roleName == $attrs.restrictTo) {
$scope.allowed = true;
} else {
$scope.allowed = false;
}
console.log($scope.data);
});
Related
I am injecting a serivce into a directive and for some instance this service returns undefined can anyone explain what I am doing wrong?
Here is a plunker of the code below. https://plnkr.co/edit/H2x2z8ZW083NndFhiBvF?p=preview
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.players = ["A","B","C"];
});
app.factory('PlayerListS', [function() {
var playerList = [];
function getList() {
return playerList;
}
function addToList(name) {
playerList.push(name);
}
return {
addToList :addToList,
getList: getList
}
}]);
app.directive("player",['PlayerListS', function (PlayerListS) {
return {
restrict: 'E',
scope: {
person:'#person',
add:'&add'
},
replace: false,
templateUrl: "player.html",
controller: function($scope, $element, $compile) {
$scope.add = function(name) {
PlayerListS.addToList(name);
console.log(PlayListS.getList());
}
}
};
}]);
You have a typo in your console because of which the code is throwing an error. Change your directive the following way
app.directive("player",['PlayerListS', function (PlayerListS) {
return {
restrict: 'E',
scope: {
person:'#person',
add:'&add'
},
replace: false,
templateUrl: "player.html",
controller: function($scope, $element, $compile) {
$scope.add = function(name) {
debugger;
PlayerListS.addToList(name);
console.log(PlayerListS.getList());
}
}
};
}]);
Working Demo: https://plnkr.co/edit/HhmOYyoZAhm6vvXp3puC?p=preview
Please check this codepen, when clicked on the button click me to reset name, the name input should be reset to empty, when I do console.log on the directive scope, I do see name is set to empty string, but the value in the view not get updated. What's wrong?
CodePen example
angular.module('mainApp', [])
.directive('myTab', function() {
return {
restrict: 'E',
template: 'name: <input type="text" ng-model="name">',
controller: function($location, $scope) {
$scope.name = 'myname';
$('#clickMeToReset').on('click', function() {
$scope.name = '';
})
}
};
})
.directive('myMenu', function() {
return {
restrict: 'E',
template: '<button id="clickMeToReset">click me to reset name</button>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="mainApp">
<my-menu></my-menu>
<my-tab></my-tab>
</div>
After
$scope.name = '';
Do a
$scope.$apply();
When you use other libraries like jquery in angular, changes to variables like
$scope should manually broadcast.
$('#clickMeToReset').on('click', function(){
$scope.name = '';
$scope.$apply();
})
so $scope.$apply(); will do the trick!
Modify your controller code with the following:
controller: function($location,$scope, $timeout){
$scope.name = 'myname';
$('#clickMeToReset').on('click', function(){
$timeout(function() {
$scope.name = '';
});
})
}
angular.module('mainApp', [])
.directive('myTab', function() {
return {
restrict: 'E',
template: 'name: <input type="text" ng-model="name">',
controller: function($location, $scope, $timeout) {
$scope.name = 'myname';
$('#clickMeToReset').on('click', function() {
$timeout(function() {
$scope.name = '';
});
})
}
};
})
.directive('myMenu', function() {
return {
restrict: 'E',
template: '<button id="clickMeToReset">click me to reset name</button>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="mainApp">
<my-menu></my-menu>
<my-tab></my-tab>
</div>
Since you are using jQuery to change the value, which is out of the Angular's context, so Angular is unaware of the change. So you need to tell Angular that something has changed. So we are using $timeout service to achieve this. We can also use $scope.$apply() but that may fail when the digest cycle is already in progress.
Important
You should use ng-click instead and remove dependency of jQuery if possible since jQuery is also a big library in itself like Angular (see a working example below):
angular.module('mainApp', [])
.directive('myTab', function() {
return {
restrict: 'E',
template: 'name: <input type="text" ng-model="name">',
controller: function($scope) {
$scope.name = 'myname';
$scope.clearName = function() {
$scope.name = '';
}
}
};
})
.directive('myMenu', function() {
return {
restrict: 'E',
template: '<button ng-click="clearName()">click me to reset name</button>'
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="mainApp">
<my-menu></my-menu>
<my-tab></my-tab>
</div>
I hope According to my thinking this code help you . Only change Js code remaining HTML code is proper.
var mainApp = angular.module('mainApp', []);
mainApp.directive('myTab', function() {
return {
template: 'name: <input type="text" ng-model="name">',
controller: function($location,$scope){ debugger;
$scope.name = 'myname';
$('#clickMeToReset').on('click', function(){
$scope.name = '';
})
}
};
})
.directive('myMenu', function() {
return {
name:"clickMeReset",
template: '<button id="name" ng-click="clear()">click me to reset name</button>',
controller:
function($location,$scope){
$('#name').on('click', function(){
$scope.name = '';
})
}
};
});
HTML:
<div ng-repeat="item in productArr">
{{ item.title }}
</div>
<div category-page-navigation current-page='currentPage' category-products-count='productsCount'></div>
JS:
.controller('categoryController', ['$scope', '$location', '$http', '$q', '$window', '$stateParams', function($scope, $location, $http, $q, $window, $stateParams) {
$scope.currentPage = 1;
$scope.productsCount = 0;
var GET = {
getProductData: function() {
var defer = $q.defer();
$http.post('services/loadProduct.php', {
'id' :1,
}).then(function(response) {
defer.resolve(response);
}, function(response) {
defer.resolve([]);
});
return defer.promise;
}
};
var getData = {
getProduct: function() {
var productData = GET.getProductData();
$q.all([productData]).then(
function(response) {
$scope.productArr = response[0].data.products;
$scope.productsCount = response[0].data.products.length;
});
}
};
getData.getProduct();
}])
.directive('categoryPageNavigation', function($compile, $parse) {
return {
scope: {
currentPage: '=currentPage',
categoryProductsCount: '=categoryProductsCount'
},
link: function (scope, element, attrs) {
debugger;
// Here scope.categoryProductsCount = undefined
// ...
$scope.$watch(scope.currentPage, function(value) {
// ...
});
}
};
});
I try to form new HTML for navigation to manipulate with HTML I get from ng-repeat.
In directive I need currentPage(from start =1) and total count of items from ng-repeat(length of array) witch I get from service. How I can pass variables to directive? First I need to get variables from service(ajax request or something else) then pass variables(some ather data) to directive.
If I understood correctly what you mean. Here is a code pen example on how to shared data between you controller and your directive.
A good read to understand the code below:https://docs.angularjs.org/guide/providers
http://codepen.io/chocobowings/full/Xmzxmo/
var app = angular.module('app', []);
//-------------------------------------------------------//
app.factory('Shared', function() {
return {
sharedValue: {
value: '',
}
};
});
//-------------------------------------------------------//
app.controller('ctrl', function($scope, Shared) {
$scope.model = Shared.sharedValue;
});
//-------------------------------------------------------//
app.directive('directive', ['Shared',
function(Shared) {
return {
restrict: 'E',
link: function(scope) {
scope.model = Shared.sharedValue;
},
template: '<div><input type="text" ng-model="model.value"/></div>'
}
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
Ctrl:
<div ng-controller="ctrl">
<input type="text" ng-model="model.value" />
<br/>
</div>
Directive:
<directive value="model.value"></directive>
</div>
I'm working on a page that is made up of 5 directives, for example:
<directive-one></directive-one>
<directive-two></directive-two>
<directive-three></directive-three>
<directive-four></directive-four>
<directive-five></directive-five>
I would like to be able to re-order these on demand so that a user can control how their page looks. The only way I could think of doing that was putting them in an ng-repeat:
$scope.directiveOrder = [{
name: "directive-one",
html: $sce.trustAsHtml('<directive-one></directive-one>'),
order: 1
}, ...
HTML:
<div ng-repeat="directive in directiveOrder" ng-bind-html="directive.html">
{{directive.html}}
</div>
This will give me the right tags, but they aren't processed as directives by angular. Is there a way around that? I'm assuming it's something to do with $sce not handling it, but I might be way off?
Try creating a new directive and using $compile to render each directive:
https://jsfiddle.net/HB7LU/18670/
http://odetocode.com/blogs/scott/archive/2014/05/07/using-compile-in-angular.aspx
HTML
<div ng-controller="MyCtrl">
<button ng-click="reOrder()">Re-Order</button>
<div ng-repeat="d in directives">
<render template="d.name"></render>
</div>
</div>
JS
var myApp = angular.module('myApp',[])
.directive('directiveOne', function() {
return {
restrict: 'E',
scope: {},
template: '<h1>{{obj.title}}</h1>',
controller: function ($scope) {
$scope.obj = {title: 'Directive One'};
}
}
})
.directive('directiveTwo', function() {
return {
restrict: 'E',
scope: {},
template: '<h1>{{obj.title}}</h1>',
controller: function ($scope) {
$scope.obj = {title: 'Directive Two'};
}
}
})
.directive("render", function($compile){
return {
restrict: 'E',
scope: {
template: '='
},
link: function(scope, element){
var template = '<' + scope.template + '></' + scope.template + '>';
element.append($compile(template)(scope));
}
}
})
.controller('MyCtrl', function($scope, $compile) {
$scope.directives = [{
name: 'directive-one'
}, {
name: 'directive-two'
}];
$scope.reOrder = function () {
$scope.directives.push($scope.directives.shift());
console.log($scope.directives);
};
});
I hope You can easily done it.
var myApp = angular.module('myApp',[])
.directive('directiveOne', function() {
return {
restrict: 'E',
scope: {},
template: '<h1>{{obj.title}}</h1>',
controller: function ($scope) {
$scope.obj = {title: 'Directive One'};
}
}
})
.directive('directiveTwo', function() {
return {
restrict: 'E',
scope: {},
template: '<h1>{{obj.title}}</h1>',
controller: function ($scope) {
$scope.obj = {title: 'Directive Two'};
}
}
});
myApp.controller('ctrl',function($scope){
$scope.data = [{name:'directive-one'},{name:'directive-two'}];
});
<html ng-app='myApp'>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js"></script>
</head>
<body ng-controller='ctrl'>
<div ng-repeat='item in data'>
<item.name></item.name>
<directive-one></directive-one>
</body>
</html>
I am setting up a <button> that is supposed to open a chat window.
the chat window has a ng-show depending on scope.openChat which is false to start.
When I clicked the button I update scope.openChat to true and use $scope.apply.
The scope seems to have updated but the ng-show does not do anything.
here is the html
<div ng-controller="MessagesCtrl">
<button start-chat>Send Messages</button>
</div>
and
<div ng-show="openChat" ng-controller="MessagesCtrl">
<div>CHAT WINDOW
</div>
</div>
here is the controller:
app.controller("MessagesCtrl", function MessagesCtrl($scope,Auth) {
$scope.openChat = false;
$scope.message = { recipient : undefined, sender: undefined, text:'text'};
});
Here is the directive for the button:
'use strict';
app.directive('startChat',[ 'Post', 'Auth', function (Post, Auth) {
return {
restrict: 'A',
replace: true,
bindToController: true,
controller: 'MessagesCtrl',
link: function(scope, element, attrs) {
element.bind('click', function() {
scope.$apply(function() {
scope.openChat = true;
scope.message.recipient = scope.profile.$id;
scope.message.sender = Auth.resolveUser().uid;
});
});
}
}
}]);
thank you
I'd suggest not creating two instances of MessagesCtrl. Here is a simplified working example of the issue you are trying to solve. Examine the markup and see that MessagesCtrl contains both of these elements. Otherwise, you were on the right track updating $scope and calling $apply
Also note that .on() is the preferred method to .bind() see jQuery docs
JSFiddle Link
<div ng-app="app">
<div ng-controller="MessagesCtrl">
<button start-chat>Send Messages</button>
<div class="chatWindow" ng-show="openChat"></div>
</div>
</div>
app.directive('startChat', [function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.on('click', function() {
scope.$apply(function() {
scope.openChat = true;
});
});
}
}
}]);
app.controller('MessagesCtrl', ['$scope', function($scope) {
$scope.openChat = false;
$scope.message = { recipient : undefined, sender: undefined, text:'text'};
}]);