Interact with angular directive via button placed anywhere - javascript

I have a directive somewhere on my page. I assign the id attribute to that directive. It doesn't matter what the directive actually does.
The key point is that I now want a second directive that basically activates the first directive.
So something like this:
<body>
<!-- bunch of elements -->
<my-directive id="test"></my-directive>
<!-- bunch of elements -->
<controlling-directive directive-id="test"></controlling-directive>
</body>
The controllingDirective would be defined like so:
.directive('controllingDirective', function() {
link: function(scope, elem, attrs) {
element.on('click', function(){
// call directive with attrs.directiveId
});
}
}
The question is: how do I actually achieve this and what is the best way?
I thought of two things:
By using document.getElementById and angular.element(..).isolateScope() like so:
.directive('myDirective', function() {
scope: {},
link: function(scope, elem, attrs) {
scope.actionToStart = function() {...};
}
}
.directive('controllingDirective', function() {
link: function(scope, elem, attrs) {
element.on('click', function(){
angular.element(document.getElementById(attrs.directiveId))
.isolateScope().actionToStart();
});
}
}
Or by using a event on $rootScope:
.directive('myDirective', function($rootScope) {
scope: {},
link: function(scope, elem, attrs) {
$rootScope.$on(attrs.id + 'Start', function(){...});
}
}
.directive('controllingDirective', function($rootScope) {
link: function(scope, elem, attrs) {
element.on('click', function(){
$rootScope.$emit(attrs.directiveId + 'Start');
});
}
}
I somehow don't like both possiblities. Is there a simple thing I'm missing, how something like this is supposed to be done?
Note that I can't use the 'require'-option, since the directives are not related in the DOM.

Methinks you not need use angular.element or even events, just simple watch and attributes
var myApp = angular.module('myApp', [])
.directive('myDirective', function() {
return {
template: '{{cur}}',
scope: {
cur: '='
},
link: function(scope, elem, attrs) {
if (!scope.cur) scope.cur = 1;
scope.$watch(function() {
return scope.cur;
}, function(newVal, oldVal) {
if (newVal !== oldVal) {
if (oldVal == 4) scope.cur = 1;
}
});
}
};
}).directive('controllingDirective', function($rootScope) {
return {
scope: {
directiveCur: '='
},
template: '<button type="button" ng-click="click()">Click me</button>',
link: function(scope, elem, attrs) {
scope.click = function() {
scope.directiveCur += 1;
}
}
};
});
div {
margin-bottom: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<div ng-app="myApp">
<div>
<my-directive data-cur="test"></my-directive>
<controlling-directive directive-cur="test"></controlling-directive>
</div>
<div>
<my-directive data-cur="test2"></my-directive>
<controlling-directive data-directive-cur="test2"></controlling-directive>
</div>
</div>
UPDATE variant with $rootScope
var myApp = angular.module('myApp', []);
myApp.controller('Test', function($scope) {});
myApp.directive('myDirective', function($rootScope) {
return {
template: '{{test}}',
scope: {
cur: '#'
},
link: function(scope, elem, attrs) {
if (!$rootScope[scope.cur]) scope.test = $rootScope[scope.cur] = 1;
console.log($rootScope[scope.cur], scope.test);
scope.$watch(function() {
return $rootScope[scope.cur];
}, function(newVal, oldVal) {
if (newVal !== oldVal) {
if (oldVal == 4) scope.test = $rootScope[scope.cur] = 1;
else scope.test = $rootScope[scope.cur];
}
});
}
};
});
myApp.directive('controllingDirective', function($rootScope) {
return {
scope: {
directiveCur: '#'
},
template: '<button type="button" ng-click="click()">Click me</button>',
link: function(scope, elem, attrs) {
scope.click = function() {
$rootScope[scope.directiveCur] += 1;
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<div ng-app="myApp">
<my-directive data-cur="test"></my-directive>
<controlling-directive directive-cur="test"></controlling-directive>
<br/>
<my-directive data-cur="test2"></my-directive>
<span ng-controller="Test">
<controlling-directive data-directive-cur="test2"></controlling-directive>
</span>
<div>test: {{test}}
<br/>test2: {{test2}}</div>
</div>
UPDATE2 variant with factory
var myApp = angular.module('myApp', []);
myApp.factory('shared', function() {
return {
inc: function(cur) {
if (this[cur] == 4) this[cur] = 1;
else this[cur] += 1;
}
}
});
myApp.controller('Test', function($scope) {});
myApp.directive('myDirective', function(shared) {
return {
template: '{{shared[cur]}}',
scope: {
cur: '#'
},
link: function(scope, elem, attrs) {
if (!shared[scope.cur]) shared[scope.cur] = 1;
scope.shared = shared;
}
};
});
myApp.directive('controllingDirective', function(shared) {
return {
scope: {
directiveCur: '#'
},
template: '<button type="button" ng-click="click()">Click me</button>',
link: function(scope, elem, attrs) {
scope.click = function() {
shared.inc(scope.directiveCur);
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js"></script>
<div ng-app="myApp">
<my-directive data-cur="test"></my-directive>
<controlling-directive directive-cur="test"></controlling-directive>
<br/>
<my-directive data-cur="test2"></my-directive>
<span ng-controller="Test">
<controlling-directive data-directive-cur="test2"></controlling-directive>
</span>
</div>

Related

AngularJS - Why I cannot get element class name in directive's link function?

I got below code. I expect it will show me true. However, it show me false.
Can anyone explain it to me and provide me a solution to check if the class existed in the element? Thanks in advance.
// HTML
<tit-txt class="{{editable}}" ng-model="mdEnt.phone"></tit-txt>
//JS
.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
ngModel: '=',
},
link: function (scope, element, attrs) {
console.log(element.hasClass('editable'));
},
template: '<input ng-model="ngModel" />',
};
})
Use a watcher to detect when the class is updated:
app.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
myModel: '=',
},
link: function (scope, element, attrs) {
scope.$watch(hasClassEditable, function() {
console.log(element.hasClass('editable'));
});
function hasClassEditable() {
return element.hasClass('editable');
}
},
template: '<input ng-model="myModel" />',
};
})
Interpolated bindings such as class={{editable}} update each digest cycle. The directive needs to wait for a binding to update before using the value.
The DEMO
angular.module("app",[])
.run(function($rootScope,$timeout) {
$rootScope.$timeout = $timeout;
})
.directive('titTxt', function () {
return {
restrict: 'E',
scope: {
myModel: '=',
},
link: function (scope, element, attrs) {
scope.$watch(hasClassEditable, function(value) {
console.log("has class 'editable'", value);
});
function hasClassEditable() {
return element.hasClass('editable');
}
},
template: '<input ng-model="myModel" />',
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<input type="checkbox" ng-model="editable" ng-change="$timeout()"
ng-true-value="'editable'" ng-false-value="''" />
add class "editable"
<br>
<tit-txt class="{{editable}}" my-model="inputTxt"></tit-txt>
<br>
editable="{{editable}}"
<br>
inputTxt="{{inputTxt}}"
</body>
Try to do it like this:
// HTML
<div ng-app="myApp" ng-controller="myController">
<tit-txt custom-class="customClass"
cust-var="myVal"></tit-txt>
<div ng-bind="myVal"></div>
</div>
//JS
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.customClass = "editable";
$scope.myVal = "this";
});
app.directive('titTxt', function () {
return {
restrict: 'AE',
replace: true,
scope: {
customClass: '=',
custVar: '='
},
link: function (scope, element, attrs) {
console.log(scope);
console.log((scope.customClass === "editable"));
},
template: '<input class="myClass" ng-Model="custVar"/>',
};
})
EDIT: Edited the code to include the working code. Here is the link for plunker
Try this instead:
link: function (scope, element, attrs, controller) {
console.log(angular.element(element).hasClass('editable'));
}
Try with pre function,
link: {
pre: function(scope, element, attr, controllers) {
console.log(element.hasClass('editable'));
},
post: function(scope, element, attr, controllers) {
},
}

AngularJS updating ng-repeat via input not working?

I'm trying to update values in a ng-repeat on a ng-model;
I have the current directive:
app.directive('myDirective', function () {
return {
require: 'ngModel',
restrict: 'E',
template: '<div ng-repeat="e in model"><input ng-model="e"/></div>',
scope: {
ngModel: '='
},
link: function($scope, elem, attrs, ngModelCtrl) {
$scope.$watch(function (){
return ngModelCtrl.$modelValue;
}, function (v) {
$scope.model = ngModelCtrl.$viewValue;
});
}
};
});
but it isn't updating the value as illustrated here:
http://plnkr.co/edit/E89sbXY0gUw53EmJobz0?p=preview
anybody knows what might be wrong?
http://plnkr.co/edit/2JwxNzBRQa1dzACoJIpF?p=preview
Had to replace $scope.model = ngModelCtrl.$viewValue; with scope.model = ngModelCtrl.$viewValue; and it works fine.
app.directive('myDirective', function () {
return {
require: 'ngModel',
restrict: 'E',
template: '<div ng-repeat="e in model"><input ng-model="e"/></div>',
scope: {
ngModel: '='
},
link: function(scope, elem, attrs, ngModelCtrl) {
console.debug()
scope.$watch(function (){
return ngModelCtrl.$modelValue;
}, function (v) {
scope.model = ngModelCtrl.$viewValue;
})
}
};
});
UPDATE: I converted 'stuff' to an array of objects and now it works:
http://plnkr.co/edit/2JwxNzBRQa1dzACoJIpF?p=preview
var app = angular.module('angularjs-starter', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.stuff = [{number: 1},{number: 2},{number: 3}];
});
app.directive('myDirective', function () {
return {
require: 'ngModel',
restrict: 'E',
template: '<div ng-repeat="e in model"><input ng-model="e.number"/></div>',
scope: {
ngModel: '='
},
link: function(scope, elem, attrs, ngModelCtrl) {
console.debug()
scope.$watch(function (){
return ngModelCtrl.$modelValue;
}, function (v) {
scope.model = ngModelCtrl.$viewValue;
console.log(scope.model)
})
}
};
});
#Kiwi ng-repeat creates a child scope and ng-model will use the property on the child scope, because ng-model binding will evaluate on the current scope. This is the reason why the json presented in the view doesn't change in your example as it was bound to a property on the child scope created by the ng-repeat directive.
Check this simple jsfiddle example I hope it will be of help to you.
<div ng-app="demo">
<div ng-controller="DefaultController as ctrl">
{{ctrl.numbers | json}}
<numbers numbers="ctrl.numbers"></numbers>
</div>
</div>
angular
.module('demo', [])
.controller('DefaultController', DefaultController)
.controller('NumbersController', NumbersController)
.directive('numbers', numbers);
function DefaultController() {
var vm = this;
vm.numbers = [1, 2, 3];
}
function numbers()
{
var directive = {
restrict: 'E',
scope: {
numbers: '='
},
template: '<div ng-repeat="number in vm.numbers"><input type="number" ng-model="vm.numbers[$index]"/></div>',
bindToController: true,
controller: NumbersController,
controllerAs: 'vm'
};
return directive;
}
function NumbersController() {
var vm = this;
}

Angular js : How to use link function to set value in directives

I created directives for form input controls.
function textControlDir()
{
return {
transclude: true,
restrict: 'E',
scope: {
data: '=data',
default: '=default'
},
template: "<div ng-transclude></div><label>{{data._text}} </label><input ng-model='answer.PC' type='text' name='{{data._attributeName}}' id='{{data._attributeName}}' value='{{default}}' >"
,
link: function (scope, element, attrs)
{
console.log('default');
console.log(scope.default);
}
};
}
Html
<div ng-if="que.QuestionData._fieldType === 'text'" >
<text-control-dir data="que.QuestionData" default="{{answers[que.QuestionData._attributeName]}}"></text-control-dir>
</div>
Here for input box I want to set value. that would be as per condition.
In link function of directive i am trying to write like
link: function (scope, element, attrs)
{
if(scope.default == ''){
scope.default = que.QuestionData._pageAttributes.defaultValue
}
}
As you have suggested that you have a E element directive, so you can make use of attrs param of link function:
link: function (scope, element, attrs){
if(attrs.default == ''){
attrs.default = que.QuestionData._pageAttributes.defaultValue
}
}
(function() {
var app = angular.module('demoApp', []);
app.controller('demoController', [demoController])
.directive('textControlDir', [textControlDirective])
function demoController() {}
function textControlDirective() {
return {
restrict: 'E',
template: "<div ng-transclude>::::textControlDirective::::</div>",
link: function(scope, element, attrs) {
console.log(attrs.default);
}
};
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.min.js"></script>
<div ng-app='demoApp' ng-controller='demoController'>
<text-control-dir data="dataAttr" default="defaultAttr"></text-control-dir>
</div>

Functions in compile isolated directive - AngularJS

I'm trying to understand how to call an external function from a built-in-compile directive.
Here is an example: http://plnkr.co/edit/bPDaxn3xleR8SmnEIrEf?p=preview
html:
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="myController as vm">
<my-directive callback="vm.saveAction()" label="click me!"></my-directive>
</body>
</html>
js:
app = angular.module('app', []);
app.controller('myController', function() {
var vm = this;
vm.saveAction = function() {
alert("foo!");
}
});
app.directive('myDirective', function() {
var directive = {
restrict: 'E',
scope: {
callback: '&'
},
compile: compile,
link: link
};
return directive;
function compile(element, attrs) {
var template = '<button ng-click="action()">'+attrs.label+'</button>';
element.replaceWith(template);
}
function link(scope, element) {
scope.action = function() {
// ...something usefull to do...
scope.callback();
}
}
});
I know that I could easly do it from the link function (and it works from there), but I really need to do it from the compile method (this is just a simplified version to better point out the problem).
Could someone help me?
Thank you!
Use template directive to do this
app.directive('myDirective', function() {
var directive = {
restrict: 'E',
scope: {
callback: '&',
label: '#'
},
template: '<button ng-click="action()">{{label}}</button>',
link: link
};
return directive;
function link(scope, element, attrs) {
console.log(scope.label);
scope.action = function() {
// ...something usefull to do...
scope.callback();
}
}
});
Or if you want to use compile method, use pre or post method and compile yourself:
function compile(element, attrs) {
return {
pre: function(scope, elem, attrs) {
var template = $compile('<button ng-click="action()">'+attrs.label+'</button>')(scope);
element.replaceWith(template);
},
post: function (scope, elem, attrs) {
// or here
}
}
}

ng change with custom directive

Javascript code for directive
angular.directive('uiDatePicker', [function() {
return {
restrict: 'EA',
scope: {
ngModel: '=ngModel',
ngChange: '=ngChange'
},
templateUrl: 'complete/date-picker.tmp.html',
replace: true,
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
if(scope.ngModel !== undefined) {
scope.DatePicker = new DatePicker(new Date(scope.ngModel));
} else {
scope.DatePicker = new DatePicker();
}
scope.ngModel = scope.DatePicker.Value;
}
}
}])
Javascript code for controller
angular.controller('Main', ['$scope', function($scope) {
$scope.Change = function(value) {
console.log(value);
};
$scope.Value = 1000;
}])
Html code
<div ng-controller="Main">
<ui-date-picker ng-model="Date" ng-change="Change(Value)"></>
</div>
And my question: How to ng-change work ?. ngModel still work !

Categories

Resources