How to import ngModel into link funciton of directive - angularjs

I have to access ngModel into my directives link function. Here is the code:
app.directive("contenteditable", function() {
var directive = {};
directive.require = ['^parentDirective','?ngModel'];
directive.link = function(scope, element, attrs, ngModel) {
ngModel.someMethod(); // Gives method not found
}
But I am not sure how to pass or access 'ngModel' from directive link function.

Since you're "requiring" an array of controllers, you will get an array of controllers back injected into your link function.
directive.link = function(scope, element, attrs, ctrls) {
var ngModel = ctrls[1];
ngModel.someMethod();
}
From documentation:
The require takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the injected argument will be an array in corresponding order.

Related

Add ngModel directive to dynamic element in Javascript

Is there a way to use javascript to apply the ng-model directive to a created element? In the code below, I want the new select element to be bound using ng-model to a scoped variable inside the controller:
angular.module('myApp').directive('dynamicHtml', function ($q, $http, $compile) {
return {
restrict: 'EAC',
scope: '=',
compile: function(element, attr) {
return function(scope, element, attr) {
var select = document.createElement('select');
// Here I want to use javascript to apply ng-model='controllerVar'
// to the new select element
element.append(select);
}
}
};
});
Whoops - my apologies...I forgot to compile the thing...carry on.

Directive within another directive - scope var undefined

I'm trying to generate a smart-table directive from within a custom directive I've defined:
<div ng-controller="myContrtoller">
<containing-directive></containing-directive>
</div>
The directive definition:
angular.module('app')
.directive('containingDirective', function() {
return {
restrict: 'E',
replace: true,
template: '<table st-table="collection" st-pipe="scopeFun"></table>',
link: function(scope, elem, attrs) {
scope.scopeFun = function () {
// solve the misteries of life
}
}
}
});
As you can see my directive tries to replace the element by the template generated by the st-table directive, using the st-pipe directive depending on the first, briefly:
ng.module('smart-table')
.controller('stTableController' function () {
// body...
})
.directive('stTable', function () {
return {
restrict: 'A',
controller: 'stTableController',
link: function (scope, element, attr, ctrl) {
// body
}
};
})
.directive('stPipe', function (config, $timeout) {
return {
require: 'stTable',
scope: {
stPipe: '='
},
link: {
pre: function (scope, element, attrs, ctrl) {
var pipePromise = null;
if (ng.isFunction(scope.stPipe)) { // THIS IS ALWAYS UNDEFINED
// DO THINGS
}
},
post: function (scope, element, attrs, ctrl) {
ctrl.pipe();
}
}
};
});
Problem:
The st-pipe directive checks the scope var stPipe if it is defined or not by: if (ng.isFunction(scope.stPipe)). This turns out to be ALWAYS undefined. By inspecting I found two things:
From the stPipe directive, the value supposed to be scope.stPipe that is my scopeFun defined within my containingDirective is undefined on the scope object BUT defined within the scope.$parent object.
If I define my $scope.scopeFun within the myContrtoller I don't have any problem, everything works.
Solution:
I did find a solutions but I don't know what really is going on:
Set replace: false in the containingDirective
Define the scope.scopeFun in the pre-link function of containingDirective
Questions:
Why is the scopeFun available in the stPipe directive scope object if defined in the controller and why it is available in the scope.$parent if defined in the containingDirective?
What is really going on with my solution, and is it possible to find a cleaner solution?
From the docs: "The replacement process migrates all of the attributes / classes from the old element to the new one" so what was happening was this:
<containing-directive whatever-attribute=whatever></containing-directive>
was being replaced with
<table st-table="collection" st-pipe="scopeFun" whatever-attribute=whatever></table>
and somehow st-table did not enjoy the extra attributes (even with no attributes at all..).
By wrapping the containingDirective directive template within another div fixed the problem (I can now use replace:true):
<div><table st-table="collection" st-pipe="scopeFun"></table></div>
If someone has a more structured answer would be really appreciated

Why element argument in link function of directive is undefined in $watch?

I created a simple directive which triggers focus event on input to show tooltip
.directive('tooltipValidationObserver', ['$timeout', function($timeout) {
return {
restrict: 'A',
require: '?ngModel',
link: function($scope, element, attrs, ngModel) {
$scope.$watch(function() { return ngModel.$invalid; }, function(newVal, oldVal) {
$timeout(function() {
$('#' + attrs.id).trigger('focus');
});
});
}
}
}])
It works perfectly if I use attrs.id, but if I want to perform
$(element).trigger('focus')
element is undefined, when in link function it's defined on linking phase.. and undefined in watch function. Why? In watch function I can obtain all available variables (such as $scope, attrs, ngModel) but not the element..
use,
$(element[0]).trigger('focus');
in the link function element is the jqLite-wrapped element, try to console.log(element) and expand the output and you can see the 0 index is the actual dom element. here is a DOC
get the actual dom element by element[0] and we create jquery reference as $(element[0]) to that element and trigger the focus event on it. hope this helps.

Using required controller in controller - not link

When using require: "^directive1" in directive2 You can access it's variables and function in link function link: function (scope, element, attrs, ctrl) {} - ctrl is directive's 1 controller.
How to use ctrl in controller - not link func?
Is there anything that can be shared between the controllers? Yes, there is: the directives can share the same scope!
You need to expose the controller on the shared scope object. The simplest way to do that is in the link function of your second directive:
var linkFn = function (scope, element, attrs, ctrl) {
scope.exposedCtrl = ctrl;
};
As long as you're not using isolated scopes, you should be able to access $scope.exposedCtrl in your parent directive. Just remember that the exposedCtrl property won't be initialized until the child directive's link function has been called.
I found perfect solution for my issue:
.controller('myCtrl', ['$element', function($element) {
var parentCtrl = $element.parent().controller('diective1');
}])
source: https://github.com/angular/angular.js/issues/10998#issuecomment-73308256

How to create a new isolated scope with an existing object?

I want to create a new scope with this object:
$scope.model = {
itemA: "First item",
itemB: "Second item"
};
// I know, this is wrong, but I want to show you, what I would like to do.
var newScope = $scope.$new($scope.model);
The new scope I want to access in the ngTransclude-Part of my directive:
link: function (scope, element, attrs, ctrl, transclude) {
transclude(scope.model, function (clone, scope) {
element.find('section').html("").append(clone);
});
And in the template:
<p>{{itemA}} - {{itemB}}
But this doesn´t work
I have the idea from: http://angular-tips.com/blog/2014/03/transclusion-and-scopes/
but I don´t want to work in the scope of the directive, but in a new scope.
AFAIK when you are creating directive usually it inherits the scope. The idea is to create isolated scope and this is done by doing.
.directive('directiveName', function ($compile) {
return {
restrict: "AE",
scope:{
yourModelName:"=";
}
link: function (scope, element) {
var el = angular.element('<div>Here you put your template scope.yourModelName</div>');
$compile(el)(scope);
element.append(el);
}
};
})
Will be copied from the upper scope but you can change it in the directive without changing it in the upper scope
his is my solution I found to a similar problem. Slightly long-winded but it works!
Create a new, 'empty' class directive with its own scope.
Add this directive as a class attribute to your DOM element. It automatically takes the scope of the new directive.
In your case, you would use it on your p tag. You would then select this element in your link function and call scope() on it:
1. <p id="ID" class="my-empty-directive">{{itemA}} - {{itemB}}
2. create your new directive:
angular.module('sgComponents').directive('panelData', [function () {
return {
restrict: 'C', // a class Directive
scope: true // with its own scope
};
}]);
2. link: function (scope, element, attrs, ctrl, transclude) {
var pTag = jQuery('#ID');
var angularElement = angular.element(pTag);
var newScope = angularElement.scope(); // THIS WILL BE THE NEW EMPTY DIRECTIVE'S SCOPE

Resources