I'm trying to apply a CSS style in an element within a certain route. For this I have created a directive where I add the class when $state.include includes the location, but since the path keeps changing it needs to watch for changes in the $state, but it is not working. Here is my code:
angular
.module('pharm')
.directive('flApplyStyle',['$stateParams','$state',applyStyle]);
function applyStyle($stateParams,$state) {
return {
link: function(scope, element, attributes){
console.log($state.current);
scope.$watch($stateParams, function (newValue, oldValue) {
if ($state.includes("app.calc.edit")) {
element.addClass('BackgroundEdit');
}else{
element.addClass('none');
}
});
}
}
};
This should probably work. Tap into the $stateChangeStart event and then check for changes to $state. If that event doesn't work, try $stateChangeSuccess.
angular
.module('pharm')
.directive('flApplyStyle', ['$rootScope','$state', applyStyle]);
function applyStyle($rootScope, $state) {
return {
link: function(scope, element, attributes){
$rootScope.$on('$stateChangeStart', function () {
if ($state.includes("app.calc.edit")) {
element.addClass('BackgroundEdit');
} else {
element.addClass('none');
}
});
}
}
}
Related
I am trying to open dialogs, which have their own Controllers, opening them through events.
My problem now is, that I am always getting
Cannot read property $emit of undefined`, because for some reason
my $rootScope is undefined.
How can I inject the $rootScope properly?
I am using Angular 1.6.7.
.directive("opendialog", [function($rootScope) {
return {
link: function(scope, element, attributes) {
element.bind("click", function(event) {
var dialogId = $(element).attr("id");
$rootScope.$emit(dialogId, {
command: "open"
});
});
}
}
}]);
Try this
.directive("opendialog", ["$rootScope", function ($rootScope) {
return {
link: function (scope, element, attributes) {
element.bind("click", function (event) {
var dialogId = $(element).attr("id");
$rootScope.$emit(dialogId, {command: "open"});
});
}
}
}]);
I have defined a custom click directive as below:
(function() {
'use strict';
angular.module('myApp.core')
.directive('customClick', customClick);
customClick.$inject = ['$rootScope'];
function customClick() {
return {
restrict: 'A',
/*scope: {
customClick: '&'
},*/
link: function(scope, element, attrs){
$(element).on('click', function(e) {
scope.$apply(function() {
console.log("Hello..customClick..");
scope.customClick();
});
});
}
};
}
})();
And I get the following error on this;
Error logged by WDPR Angular Error handler service {xx.."stacktrace":"TypeError: a.customClick is not a function","cause":"unknown cause"}(anonymous function)bowerComponents.js:5745
How can I resolve this? If I add scope with '&' I get demanding isolated scope. Hence how to resolve it?
If I remove - scope.customClick();, it does not show anything on second html for custom-click, it has impact on only 1 html, and its controller. I want to use it in multiple controller + html.
customClick is a function on the directive itself. It is not a function on the scope. That's why the error has occurred.
link is used to manipulate dom/add event handlers on elements, which you have rightly done with element.bind('click', function() {
Whenever click occurs, the function binded to click automatically gets invoked. There is no need of watch and the invoking statement.
Your link can just be
link: function(scope, element){
element.bind('click', function() {
console.log("Hello..customClick..");
});
}
As you have used camel case in naming the directive, exercise caution in its usage in template.
You can use it as <p custom-click>hi</p>
I would recommend you to avoid using jQuery in angular apps. Try following
angular.module('newEngagementUI.core')
.directive('customClick', customClick);
customClick.$inject = ['$rootScope'];
function customClick() {
return {
restrict: 'A',
scope: {
customClick: '&'
},
link: function(scope, element, attrs){
element.bind('click', function () {
scope.customClick();
})
}
};
}
In your template:
<div custom-click="clickFunction"></div>
And your template controller should be like:
angular.module('myApp', []).controller(['$scope', function ($scope) {
$scope.clickFunction = function () {
alert('function passed');
}
}])
Working fiddle here: https://jsfiddle.net/xSaber/sbqavcnb/1/
i tried to include http://krescruz.github.io/angular-materialize/#select and i got error in console (you can see in image). Is there any solution for this issue, here is the directive which is in angular-materialize.js
angular.module("ui.materialize.material_select", [])
.directive("materialSelect", ["$compile", "$timeout", function ($compile, $timeout) {
return {
link: function (scope, element, attrs) {
if (element.is("select")) {
$compile(element.contents())(scope);
function initSelect() {
element.siblings(".caret").remove();
element.material_select();
}
$timeout(initSelect);
if (attrs.ngModel) {
scope.$watch(attrs.ngModel, initSelect);
}
if ("watch" in attrs) {
scope.$watch(function () {
return element[0].innerHTML;
}, function (oldVal, newVal) {
if (oldVal !== newVal) {
$timeout(initSelect);
}
});
}
}
}
};
}]);
This error means that you are trying to use jQuery methods without loading jQuery. Angular comes with lightweight jQuery-like implementation (angular.element) with limited set of methods. Those methods don't include is and trigger (and many others).
So the solution is simple: if you use jQuery methods - load jQuery before Angular.
I want to create a directive, where it observe the 'playerSearchSpinnerOn' property in the parent scope. where the value changes, than execute code in my directive link function. At the moment, my observe function isn't been triggered when value changes.
html
<div id="addUsers" class="center" spinner spinnerOn="{{playerSearchSpinnerOn}}">
directive
monopolyMenuModule.directive('spinner', function () {
return {
restrict: "A",
link: function (scope, elem, attr) {
attr.observe('spinnerOn', function (newValue, oldValue) {
var spinner = new spinner();
if (newValue) {
// load spinner
spinner.spin(elem);
}
else if (newValue == false) {
// close spinner
spinner(false);
}
});
}
}
parent controller
monopolyMenuModule.controller('AddUsersCtrl', ['$scope', 'addUserServices', 'GameGroupDetails', function ($scope, service, GameGroupDetails) {
// add code to call notifyUsers object.. watch pluralsight "connecting our server to client" and "how signalr works"
$scope.playerSearchSpinnerOn = false;
$scope.FindUsers = function () {
if (GameGroupDetails != null) {
service.FindUsers(GameGroupDetails).done(function () {
// add spinner once group has been show in invite screen
$scope.playerSearchSpinnerOn = true;
});
}
};
}])
when playerSearchSpinnerOn property changes in the AddUserCtrl parent controller, i want my 'spinner' directive to react to this change.
where am i going wrong?
Instead of watching an attribute, you should instead use an isolated scope
monopolyMenuModule.directive('spinner', function () {
return {
restrict: "A",
scope:{
spinnerOn: "#"
}
link: function (scope, elem, attr) {
$scope.$watch('spinnerOn', function (newValue, oldValue) {
var spinner = new spinner();
if (newValue) {
// load spinner
spinner.spin(elem);
}
else if (newValue == false) {
// close spinner
spinner(false);
}
});
}
}
This way you don't have to rely on any weird parent-based logic, you just pass in the value you want to watch. That being said, I have no idea what this spinner thing is, and I suspect you have some issues there.
Also, read this blog for a great guide on isolated scopes.
Solved. I had to wrap the playerSearchSpinnerOn property with the $apply service. This was needed because, changing this property was happening outside of angular' knowledge. calling apply() looks for any new changes in the model, and if one or more is found, then it update's the DOM.
$scope.FindUsers = function () {
if (GameGroupDetails != null) {
service.FindUsers(GameGroupDetails).done(function () {
// add spinner once group has been show in invite screen
// apply is needed and apply is only called in angularjs directives
$scope.$apply(function(){
$scope.playerSearchSpinnerOn = true;
});
});
}
};
You made some sort of typo, it should be attr.$observe instead of attr.observe, $observe will work as like watch inside a directive, it will call a attr.$observe function whenever the interpolation ({{}}) directive of attribute gets evaluated. Also on UI use attribute as hyphen (-) separated & in directive it would be used as camelCase instead of
HTML
<div id="addUsers" class="center" spinner spinner-on="{{playerSearchSpinnerOn}}">
Directive
monopolyMenuModule.directive('spinner', function () {
return {
restrict: "A",
link: function (scope, elem, attr) {
attr.$observe('spinnerOn', function (newValue, oldValue) {
var spinner = new spinner();
if (newValue) {
// load spinner
spinner.spin(elem);
}
else if (newValue == false) {
// close spinner
spinner(false);
}
});
}
}
I have a custom directive:
.directive('myDirective', function() {
return {
scope: {ngModel:'='},
link: function(scope, element) {
element.bind("keyup", function(event) {
scope.ngModel=0;
scope.$apply();
});
}
}
});
This works as planned, setting the variables to 0 on keyup, but it doesn't reflect the changes on the input themselves. Also when initialized, the values of the model are not in the input. Here is an example:
http://jsfiddle.net/prXm3/
What am I missing?
You need to put a watcher to populate the data since the directive creates an isolated scope.
angular.module('test', []).directive('myDirective', function () {
return {
scope: {
ngModel: '='
},
link: function (scope, element, attrs) {
scope.$watch('ngModel', function (val) {
element.val(scope.ngModel);
});
element.bind("keyup", function (event) {
scope.ngModel = 0;
scope.$apply();
element.val(0); //set the value in the dom as well.
});
}
}
});
Or, you can change the template to
<input type="text" ng-model="$parent.testModel.inputA" my-directive>
the data will be populated thought it will break your logic to do the event binding.
So it is easier to use the watcher instead.
Working Demo