I am creating drag and drop functionality by creating a <dragItem> directive and a <droptTraget> directive, but I don't understand yet how to work with the inner and out scope in this way.
Here are my directives. The events triggers the functions properly, I just want the on dragstart event to store a value of the drag element and the drop event to trigger the function testAddSet() which adds the drag value to my model.
drag
angular.module('app.directives.dragItem', [])
.directive('dragItem', function(){
return { // this object is the directive
restrict: 'E',
scope: {
excercise: '='
},
templateUrl: "templates/dragTile.html",
link: function(scope, element, attrs){
element.on('dragstart', function (event) {
var dataVar = element.innerText;
// It's here that I want to send a dataVar to the $scope
});
}
};
});
drop
angular.module('app.directives.dropTarget', [])
.directive('dropTarget', function(){
return { // this object is the directive
restrict: 'E',
scope: {
day: '='
},
templateUrl: "templates/calDay.html",
link: function(scope, element, attrs){
element.on('drop', function (event) {
event.preventDefault();
// It's here that I'd like to take the value from the drag item and update my model
testAddSet() // doesn't work
$parent.testAddSet() // doesn't work
});
element.on('dragover', function (event) {
event.preventDefault();
});
}
};
});
Since you are using isolate scope, you need to define an attribute for the function binding.
angular.module('app.directives.dropTarget', [])
.directive('dropTarget', function(){
return { // this object is the directive
restrict: 'E',
scope: {
day: '=',
//Add binding here
testAddSet: '&'
},
templateUrl: "templates/calDay.html",
link: function(scope, element, attrs){
element.on('drop', function (event) {
event.preventDefault();
//Invoke the function here
scope.testAddSet({arg: value, $event: event});
});
element.on('dragover', function (event) {
event.preventDefault();
});
}
};
});
In your template, connect the function using the directive attribute.
<drop-target test-add-set="fn(arg, $event)" day="x"></drop-target>
For more information on isolate scope binding, see AngularJS $compile Service API Reference - scope.
I recommend that the event object be exposed as $event since that is customary with AngularJS event directives.
$event
Directives like ngClick and ngFocus expose a $event object within the scope of that expression. The object is an instance of a jQuery Event Object when jQuery is present or a similar jqLite object.
-- AngularJS Developer Guide -- $event
I think the easiest way to get your cross-directive communication is to make a scope variable on the host page and then pass it double-bound ('=') to both directives. That way, they both have access to it as it changes.
Related
So this is mainly to guarantee backwards compatibility. I need to add additional functionality on blur to a large number of html elements without having to edit the controller methods called by the ng-blur attribute of those elements.
I wrote a directive to do this, but the problem is the directive method seems to always finish executing after the ng-blur methods in the controller. I need the directive method to finish executing before the ng-blur method in the controller begins.
Here is the basic format of the code:
<input test-directive ng-model="testValue" ng-blur="ngBlurTest(testValue)">
Directive:
angular.module('testDirectives', [])
.directive('testDirective', [ function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
ngModel: '=',
},
link: function (scope, element, attrs, ngModelCtrl, test) {
element.bind('blur', function() {
console.log('This needs to appear first')
}
});
}
};
}])
Controller (Method)
$scope.ngBlurTest = function(value) {
console.log('TEST FROM CONTROLLER: ' + value);
}
Thanks for the help!
I'm not sure if it's possible, but try setting priority of the directive to a really high number, for example:
.directive('testDirective', [ function() {
return {
priority: 1000000,
restrict: 'A',
...
Also try using addEventListener, but don't forget to clear it:
var listener = function(event) {...};
element[0].addEventListener('blur', listener);
...
scope.$on('$destroy', function() {
element[0].removeEventListener('blur', listener);
});
Also try setting useCapture parameter of addEventListener to true:
element[0].addEventListener('blur', listener, true);
How do I create a directive that injects a fixed position modal or a directly underneath the element that the directive is attached to? So in the directive I would want scope.$on to trigger injecting directly below the element.
Here's my code so far:
angular.module('mobileDashboardApp')
.directive('httpErrorMessage', function (HTTPErrors) {
return {
template: '<div></div>',
link: function(scope, element, attrs) {
console.log("hello");
scope.$on(HTTPErrors.badRequest, function (event, args) {
// Inject template directly below 'element' <-- part I'm not sure how to do
});
}
};
});
This it would be my directive
I try to add click events in the link function for my directive when it initiates, but it gives me empty element when I try to get the elements.
var link = function(scope, elem, attrs, controller){
console.log('WidgetId:' + controller.widget);
//Not working,
angular.element(elem[0].querySelectorAll('.widget-btn')).on('click', function(){
console.log('click event with querySelector');
});
// Directive element DOM
console.log(elem);
// That call return empty array, the PROBLEM is here
console.log(elem[0].querySelectorAll('.widget-btn'));
elem.on('click', function(e){
// But when I click in the directive, it returns the elements that I want
console.log(angular.element(elem[0].querySelectorAll('.widget-btn')));
});
scope.$on(scope.widget, function(){
console.log('directive get the controller message');
});
var widgetId = controller.widget;
function socketInit(){
socket.forward(widgetId, scope);
scope.$on('socket:'+ widgetId, function(){
console.log('Get socket msg');
});
};
socketInit();
}
return {
restrict: 'EA',
controller: controller,
// Our controller $scope will change for vm
controllerAs: 'vm',
// We set our widget info in the datasource.
scope: {
// After the configuration our datasource is accesible in the vm.datasource
datasource: '=',
addchanneluser: '&',
widget: '#'
},
bindToController: true,
templateUrl: '/angular-js/views/dashboard/directives/small/small.template.html',
link: link,
}
console.log(elem), it gives me directive element DOM so what I want to do is find in that DOM, all the elements that has channel class. I execute with querySelector and it gives me empty array.
But when all the directive its charge in the browser and I click in the directive console call is with content.
My questions is, it can be some initialisation problem or querySelector is charged after the link function.
Thanks
How can I access a directive's ngModelController from another directive?
The scenario
I'm creating a type ahead widget, which is composed of a typeAhead directive and autoCompletePopUp directive.
AutoCompletePopUp directive will interact with the typeAhead using typeAhead's controller.
But I don't know how to call typeAhead's $setViewValue from autoCompletePopUp when an item is selected.
Why not just add a function to the controller for typeAhead that calls $setViewValue on itself. In the context of typeAhead's controller you should have access to the scope. You can put the ngModelController for typeAhead on the scope if needed. Something like this:
angular.module("myModule").directive("typeAhead", function() {
return {
require: "ngModel",
controller: function($scope) {
this.setValue = function(value) {
$scope.ngModelController.$setViewValue(value);
};
},
link: function(scope, element, attributes, ngModelController) {
scope.ngModelController = ngModelController;
},
};
});
angular.module("myModule").directive("typeAhead", function() {
return {
require: "typeAhead",
link: function(scope, element, attributes, typeAheadController) {
scope.someAction = function(value) {
typeAheadController.setValue(value);
};
},
};
});
(protect against minification and move controllers into separate objects / files as desired; done inline here for convenience)
I have a custom directive that creates a field as part of a larger form. Vastly simplified, it looks like this:
.directive('entityField', function () {
return {
restrict: 'E',
scope: {
field: '=',
attributes: '=',
editMode: '='
},
templateUrl: '<input ng-blur="blur()" type="text"/>',
link: function (scope, element, attrs) {
scope.blur = function() {
// call out to event listeners that blur event happened
}
}
};
});
My goal is to have the directive fire an event on blur of its input element so that the surrounding controller can handle the blur event and perform some complex validation.
So, how do I raise an event in a directive such that the surrounding controller can listen for it?
If you weren't using isolated scope, you could use $emit to send the event up to the controller's scope. However, since you are using isolated scope, your directive does not inherit from the parent controller's scope, which makes this method impossible.
Instead, you can use the $broadcast method from $rootScope to send the event down the scope prototype chain to the controller:
Directive:
.directive('entityField', function ($rootScope) {
return {
restrict: 'E',
scope: {
field: '=',
attributes: '=',
editMode: '='
},
templateUrl: '<input ng-blur="blur()" type="text"/>',
link: function (scope, element, attrs) {
scope.blur = function() {
$rootScope.$broadcast('blur');
}
}
};
});
Then use $on to catch it in your controller:
Controller:
.controller('MyController', function($scope){
$scope.$on('blur', function(){
//react to event
})
});
Hopefully this is a sufficient answer to the narrowest of interpretation of your question. I want to also mention that it is often better to use the prototypical nature of the scope and/or services for cross directive/controller communication. My answer to another question today helps to cover this topic: How to call controller function from directive?