I have the following directive, service and controller inside my AngularJS app. The service is common between the directive and this controller (as well as other controllers using the same service in my app). As shown inside the controller I watch the service for changes, while in directive I communicate with the service to update it. For some reason which I don't know the directive is not updated my service, so can someone please tell me what I am doing wrong here? Thanks
Controller:
myapp.controller('ClientsCtrl', function ($scope, UserSvc) {
$scope.showForm = UserSvc.frmOpened;
$scope.$watch(function () {
return UserSvc.frmOpened;
}, function () {
$scope.showForm = UserSvc.frmOpened;
console.log('Changed... ' + $scope.showForm);
});
});
Service
myapp.factory('UserSvc', function ($log, $q, $http) {
return {
frmOpened: false
};
});
Directive:
myapp.directive('myDirective', ['UserSvc', function (UserSvc) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
angular.element(element).on("click", function () {
var parentElement = $(this).parent();
if (parentElement.hasClass('sample')) UserSvc.frmOpened = true; //This code never update the service
} else {
UserSvc.frmOpened = false; //This code never update the service
}
return false;
});
}
};
}]);
.on() is a jQuery method (also included in Angular's jqLite). The code inside the attached event handler lives outside of Angular, so you need to use $apply:
$apply() is used to execute an expression in angular from outside of
the angular framework. (For example from browser DOM events,
setTimeout, XHR or third party libraries). Because we are calling into
the angular framework we need to perform proper scope life cycle of
exception handling, executing watches.
For example:
element.on("click", function() {
var parentElement = $(this).parent();
scope.$apply(function() {
if (parentElement.hasClass('sample')) {
UserSvc.frmOpened = true;
} else {
UserSvc.frmOpened = false;
}
});
return false;
});
Demo: http://plnkr.co/edit/mCl0jFwzdKW9UgwPYSQ9?p=preview
Also, the element in the link function is already a jqLite/jQuery-wrapped element, no need to perform angular.element() on it again.
It looks like you have some brackets where they shouldn't be in your directive. You don't have brackets surrounding the first part of your if statement, but you have a closing bracket before your else. I think this messes up things.
Try using this as your link-function and see if it changes anything:
link: function (scope, element, attrs) {
angular.element(element).on("click", function () {
var parentElement = $(this).parent();
if (parentElement.hasClass('sample')) {
UserSvc.frmOpened = true; //Surrounded this with brackets
} else {
UserSvc.frmOpened = false;
}
return false;
});
}
Related
I have two controllers on same page. What I am trying to achieve is if specific value is 1 call controller 1 and if 0 then controller 2. But the problem is that I am getting value inside controllers which is not accessible outside the controller, so how can I achieve this?
This case calls for a service, which can be shared between controllers.
All you have to do is inject the service into both controllers and update the variable in the service.
angular.module('app').service('myService', function() {
var self = this;
self.isActive;
self.setActive = function(val) {
self.isActive = val;
};
self.getActive = function() {
return self.isActive;
})
. controller('first', function(myService) {
var isActive = myService.getActive();
})
. controller('second', function(myService) {
var isActive = myService.getActive();
})
Both controllers would have the same value, you would just have to handle managing them during the life cycle of your app via the controllers.
Note that you can do anything you want in a service in terms of logic, you just have to share the service with the required controllers. It would be more likely that a function would handle the logic which you are splitting into different controllers.
// Inside the service
self.handleActive = function(isActive) {
if (isActive) {
// Do something
} else {
// Do other
}
};
Use directive for this purpose
global.directive('dynamicCtrl', ['$compile', '$parse',function($compile,
$parse) {
return {
restrict: 'A',
terminal: true,
priority: 100000,
link: function(scope, elem) {
var name = $parse(elem.attr('dynamic-ctrl'))(scope);
elem.removeAttr('dynamic-ctrl');
// add your condition here
if(condition == true){
elem.attr('ng-controller',"first");
}
else{
elem.attr('ng-controller',"second");
}
$compile(elem)(scope);
}
};
}]);
use in html as
i've seen many posts about this subject, but not specificly about this question.
I'm wondering if there could be a generic directive/controller in AngularJs to disable a button (that calls an ajax request) and re-enable it after the request ends.
I've used the word "generic" because i've seen some solutions using a callback after a specific ajax request, but it's not what i need.
I need that:
when clicking on a button, and the button calls an ajax request, it becomes disabled until the request ends.
Thank you for your help
Here is a possibility.
You can think of http service calls just as a promise. Once a http service call is fulfilled then it will call the next promise in the chain, if there is one.
You can receive a funcion in a directive, then do a wrapping call to it and chain another function to it. So this way you know when the promise is being executed and when it's fulfilled.
You need to be sure to retun the promise in the controller. Then pass that funcion to the directive.
Check the following example
https://codepen.io/bbologna/pen/zdpxoB
JS
var app = angular.module('testApp', []);
app.factory('fakeHttpService', ['$timeout', function($timeout){
var doSomething = function(value) {
return new Promise(function(resolve, reject) {
$timeout(() => { resolve(value + 1); $timeout() }, 1000);
})
}
return { doSomething: doSomething }
}])
app.controller('sampleController', ['$scope', 'fakeHttpService',
function($scope, fakeHttpService) {
$scope.doSomething = function(value){
return fakeHttpService.doSomething(value);
}
}
])
app.directive('buttonAsync', function(){
return {
restrict: 'E',
template: `<button ng-click="excecute()" ng-disabled="disabled">DO</button>`,
scope: {
on : '&'
},
controller : ['$scope', function($scope) {
$scope.disabled = false;
$scope.excecute = function() {
$scope.disabled = true;
$scope.on()
.then(function() {
$scope.disabled = false;
})
}
}]
}
})
Html
<div ng-app="testApp" ng-controller="sampleController">
<button-async on="doSomething(3)"></button-async>
</div>
I am trying to show or hide the HTML elements based on spring security roles using Angular JS.
For this, I have created a service and directive.
I am getting data back from the server but I am not able to access the data that I got in service. Here is my code
app.service('MyService', function($http, $rootScope) {
this.data = [];
var _this = this;
$http.get('permissions').then(function data(response) {
_this.data=response.data;
console.log(response);
$rootScope.$broadcast('MyServiceReady');
})
/* return {
permissionData: function(){
return _this.data;
}
}*/
})
app.directive('allowed', [ 'MyService', function(MyService) {
return function(scope, element, attr) {
scope.$on('MyServiceReady', function() {
$subScope = this;
$subScope.status = false;
$subScope.permissions=MyService.data;
console.log(MyService.data);
console.log("First:" + $subScope.status+" permission:"+attr.allowed);
angular.forEach(permissions, function(item) {
if (angular.equals(item, attr.allowed)) {
$subScope.status = true;
}
});
console.log("last:" + $subScope.status);
console.log(element);
if (!$subScope.status) {
$(element).hide();
} else {
$(element).show();
}
});
};
} ]);
I tried to write a function inside the service and access it but even then it is showing MyService.permissionData is not a function.
Can anyone explain where I am going wrong?
I am trying to perform three tasks in the above code.
Get the Permissions array from the server
Dont create the directive till you get data.
hide or show elements based on permission.
My HTML code for this is:
<button class="btn btn-primary" allowed="1002">ADD SOMETHING</button>
Please do reply if you got any suggestions.
Try removing MyService from these two lines:
return function(scope, element, attr, MyService) {
scope.$on('MyServiceReady', function(MyService) {
You've already injected MyService into your directive, you don't pass it on the link function or your event handler.
Now that you've fleshed out what it is you're trying to do in your question I think I have a better answer for you to look at. If I'm reading this right, you are getting an array of integers that correspond to the allowed attribute on your buttons. If the array doesn't contain the value in allowed then the button should not be visible.
Here is a new version of your directive:
.directive('allowed', function(MyService) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
scope.$on('MyServiceReady', function() {
var allowed = false;
angular.forEach(MyService.data, function(item) {
if(attrs.allowed === item){
allowed = true;
}
});
if(!allowed){
element.addClass('hidden');
}
});
}
}
})
This requires a hidden class in your CSS with display: none;. Here's a working JSFiddle to illustrate the directive. I had to fake the $http call to your API. A downside to this approach is that the buttons are visible while the service is calling your API. It might be better to hide them by default and then show them if the user is allowed instead of vice versa.
I have a InAppBrowser working fine in my app
$scope.openInAppBrowser = function (url) {
var ref = window.open(encodeURI(url), '_blank', 'location=yes');
ref.addEventListener('loadstop', function (event) {
if (event.url.match("close")) {
$scope.refreshGamePage = 1; // this variable is under watch in a directive
ref.close();
}
});
}
here is the directive written to refresh the page
module.directive('reloadPage', ['$http', function ($http) {
return {
restrict: 'A',
link: function ($scope, element, attrs) {
$scope.refreshPage = function () {
if($scope.refreshGamePage ==1)
return true;
else
return false;
};
$scope.$watch($scope.refreshPage, function (v) {
if (v) {
$scope.setGamePage(); // this function will contains code to refresh game page
}
});
}
};
}]);
but it seems like loadstop event listener is unable to update scope variable.
Can anyone help me with this?
Basically idea is I want to refresh current page (one from which InAppBrowser opened) in my app as soon as InAppBrowser closes itself.
Any better way to achieve this will be appreciated.
Just use angular $apply() method to change the value of angular
variables because it is changed out of angularjs turn.
$scope.openInAppBrowser = function (url) {
var ref = window.open(encodeURI(url), '_blank', 'location=yes');
ref.addEventListener('loadstop', function (event) {
if (event.url.match("close")) {
$scope.$apply(function () {
$scope.refreshGamePage = 1;
});
ref.close();
}
});
}
I have a gallery of images which each pull in related data. I've made a directive to lazy load the images once they are in view. It works well, but each of the directives continues watching for the scroll event, with around 200 of them, it's a lot of events being fired unnecessarily. Is there a way to remove the directive, or disable it?
app.directive('lazyLoadGallery', function(resourceService, $rootScope){
return{
link: function (scope, element, attrs) {
var isLoaded = false;
$('.issue-gallery').on('scroll', function(){
console.log($rootScope.number);
if((attrs.lazyLoadGallery/10) % 1 === 0 && !isLoaded) {
if($(element).visible()){
isLoaded = true;
resourceService.issueListPages.list({number:$rootScope.number}).$promise.then(function(data){
$rootScope.issueList = $rootScope.issueList.concat(data.results);
$rootScope.number ++;
$(element).removeAttr('lazy-load-gallery');
});
};
}else{
$(element).removeAttr('lazy-load-gallery');
}
})
}
}
});
my attempt was to remove the attribute from the DOM. Even though it is removed the directive still is watching for scroll events and working as if it wasn't removed.
I was unable to $destroy listeners on the parent object without eliminating it's event for all directives. I came up with a name spaced event which cleans up listeners.
app.directive('lazyLoadGallery', function(resourceService, $rootScope, $compile){
return{
controller: function($scope, $element, $attrs) {
var isLoaded = false;
angular.element('.issue-gallery').on('scroll.'+ $scope.$id, function () {
if (($attrs.lazyLoadGallery / 10) % 1 === 0 && !isLoaded) {
if ($($element).visible()) {
isLoaded = true;
resourceService.issueListPages.list({number: $rootScope.number}).$promise.then(function (data) {
$rootScope.issueList = $rootScope.issueList.concat(data.results);
$rootScope.number++;
angular.element('.issue-gallery').off('scroll.'+ $scope.$id);
});
}
;
} else {
angular.element('.issue-gallery').off('scroll.'+ $scope.$id);
}
})
}
}
});
The angular documentation states that the return of $on function is
Returns function() Returns a deregistration function for this
listener.
So in your case just assign it to a variable and call it when you don't need it anymore.
var deregister = $scope.$on('something', function() {
if (success) {
deregister();
}
});