Accessing parent formController in angular directive controller - javascript

In this question I have found how to access parent form within link in a directive. But I need it in my controller and access to form validation, so I implement this:
return {
restrict: 'A',
require: '?^form',
replace: true,
scope: {
someVariable: '='
},
link: function (scope, element, attrs, formCtrl) {
scope.formCtrl = formCtrl;
},
controller: function ($scope) {
$scope.someMethod = function () {
if ($scope.formCtrl.$valid) {
//Do something
}
}
}
};
It´s the correct way to do that? there's a better way?
EDIT: I need isolated scope and I´m actually using require: '?^form'

Related

Using service inside directive?

I am learning how to create custom directives.
My service looks like that:
myApp.service('myService',function(){
this.myFunction=function(myParam){
// do something
}
});
Here is my directive:
myApp.directive('myDirective',function(myService){
return {
restrict: 'E',
scope: {
param: '=myParam',
},
template: '<button ng-click="myService.myFunction(param)">Do action</button>',
}
});
In HTML, when I use <my-directive my-param="something"></my-directive> it properly renders as a button. However when I click it, myService.myFunction, doesn't get executed.
I suppose I am doing something wrong. Can someone give me a direction?
I guess this has something to do with the directive's scope.
The service wont be available directly inside the template. You'll have to use a function attached to the directive's scope and call the service function from within this function.
myApp.directive('myDirective',function(myService){
return {
restrict: 'E',
scope: {
param: '=myParam',
},
template: '<button ng-click="callService(param)">Do action</button>',
link: function(scope, element, attrs) {
scope.callService = function() {
myService.myFunction();
}
}
}
});
It doesn't work because in your example a directive doesn't actually know what is myService. You have to explicitly inject it e.g.:
myApp.directive('myDirective', ['myService', function(myService){ ... }]);
See also this question or this question.
You should use a controller to do all DOM-modifications.
See this plunkr: https://plnkr.co/edit/HbfD1EzS0av5BG6NgtIv?p=preview
.directive('myFirstDirective', [function() {
return {
'restrict': 'E',
'controller': 'MyFirstController',
'controllerAs': 'myFirstCtrl',
'template': '<h1>First directive</h1><input type="text" ng-model="myFirstCtrl.value">'
};
}
You can inject the service in the controller and then call that function inside your template:
Inject myService into controller:
myApp.controller("ctrl", function($scope, myService) {
$scope.doService = function(myParam) {
return myService.myFunction(myParam);
};
});
Call doService method of the controller inside your template:
myApp.directive('myDirective',function(){
return {
restrict: 'E',
scope: {
param: '=myParam',
},
template: '<button ng-click="doService(param)">Do action</button>',
}
});

how to pass multiple scope variables into custom directive in angularjs

I am trying to pass 2 scope variables from controller into a custom directive and having problem in accessing both of them.Model is same for the directive and controller.
Here is the code:
Html:
<myDirective data="var1" item="var2"></myDirective>
Controller:
$scope.var1="abc";
$scope.var2="xyz";
Directive:
app.directive('myDirective', function () {
return {
restrict: 'E', //E = element, A = attribute, C = class, M = comment
scope: {
var1: '='
var2:'='
},
templateUrl: 'myTemplate.html',
link: function ($scope, element, attrs) {
}
}
});
TemplateUrl: myTemplate.html
<div>{{var1}}</div> // This works
<div>{{var2}}</div> // This doesn't works
Any idea how can I use both?
Make these changes in your code
<popover data="var1" item="var2"></popover>
JS
app.directive('popover', function () {
return {
restrict: 'E', //E = element, A = attribute, C = class, M = comment
scope: {
data: '=',
item: '='
},
templateUrl: 'myTemplate.html',
link: function (scope, element, attrs) {
console.log(scope.data, scope.item);
}
}
});
Change your template to match the names declared in the DDO.
<myDirective var1="var1" var2="var2"></myDirective>
Avoid using data as an attribute name. That is a reserved prefix that is stripped during normalization. For more information on attribute normilization, see AngularJS Developer Guide - Directive Normilization.

How to unit test AngularJs directive requiring parents directive controller with Jasmine?

I have AngularJs directive accordionPanel that requires controller of parent directive accordion. I need to test accordionPanel directive to see if model changes when I call foldUnfold function. How would I write unit test to see if the model changes on foldUnfold call. Thats simplified version of my directives and test I got so far is below that:
.directive("accordion", [
function() {
return {
templateUrl: "otherurl",
transclude: true,
replace: true,
scope: {
},
controller: ["$scope",function($scope) {
this.isOneOpenOnly = function() {
return $scope.oneOpenOnly;
}
}],
link: function(scope, elem, attrs, ctrl, linker) {
// some code
}
}
}
])
.directive("accordionPanel", [
function() {
return {
templateUrl: "urlblah",
transclude: true,
replace: true,
require: "^accordion",
scope: {},
link: function(scope, elem, attrs, ctrl, linker) {
scope.foldUnfold = function() {
// some logic here then
scope.changeThisModel=ctrl.isOneOpenOnly();
}
}
}
}
])
Thats my test so far:
it('Should return unfolded as true', function() {
var scope=$rootScope.$new(),
element=$compile("<div accordion><div accordion-panel></div></div>")(scope);
scope.$digest();
scope.foldUnfold(); // this is fails as scope refers to accordion but I need to access accordionPanel
expect(scope.changeThisModel).toBe(true);
});
The problem is I cannot get access to accordionPanel scope where foldUnfold sits. I think it might be possible to access it via $$childHead and such, but even if possible it doesn't seem like the right way to do. How would I test it then?

Two controllers on a directive

i'm creating a directive that will have methods that the children will access. But at the same time i'm requiring 'form' on my directive.
return {
restrict: 'A',
scope: {
object: '='
},
require: 'form',
controller: function ($scope, $element) {
this.hi = function () {
console.log('i am here')
}
},
link: function (scope, elm, attrs, controllers) {
console.log(controllers);
}
}
I've always thought that if i was goign to use two controllers, they would be in one array. But when i log them, i only get the FormController:
What should i do?
I noticed the syntax for require is a little different in the docs. Have you played around with that? e.g. require: ['^form']
Not sure how to specify your other controller in there, but perhaps in this way it might show up as an array?

AngularJS directive to directive communication throwing an error about controller not found

I have 2 directives, one for searching and one for pagination. The pagination directive needs to access the search directive to find out what property we're currently searching by. When I load the page though, it throws an error saying Error: [$compile:ctreq] Controller 'search', required by directive 'pagination', can't be found!. However I have a controller setup in my search directive.
Here is my search directive:
angular.module('webappApp')
.directive('search', function ($route) {
return {
templateUrl: 'views/search.html',
restrict: 'E',
scope: {
searchOptions: '=',
action: '=',
currentProperty: '=',
currentValue: '='
},
controller: function($scope) {
$scope.searchBy = $scope.searchOptions[0].text;
$scope.searchByProperty = $scope.searchOptions[0].property;
$scope.setSearchBy = function(event, property, text) {
event.preventDefault();
$scope.searchBy = text;
$scope.searchByProperty = property;
};
$scope.search = function() {
$scope.searching = true;
$scope.currentProperty = $scope.searchByProperty;
$scope.currentValue = angular.element('#searchCriteria').val();
$scope.action($scope.searchByProperty, $scope.currentValue, function() {
$scope.searching = false;
});
};
$scope.reload = function() {
$route.reload();
};
}
};
});
Here is my pagination directive:
angular.module('webappApp')
.directive('pagination', function () {
return {
templateUrl: 'views/pagination.html',
restrict: 'E',
require: '^search',
scope: {
basePath: '#',
page: '=',
sort: '='
},
link: function(scope, element, attrs, searchCtrl) {
console.debug(searchCtrl);
scope.searchByProperty = searchCtrl.searchByProperty;
}
};
});
In order for one directive to use another's controller by use of require, it needs to either share the same element as the controller containing directive, or it has to be a child of it.
You can't use require in the way you have, where the elements are siblings.
Angular docs about directives, including require
If it doesn't make sense to rearrange the DOM in the way I've described, you should inject a service into both directives which contains the data/methods you wish to share between the two.
Note: you could also experiment with the $$nextSibling / $$prevSibling properties of the directives' scopes, but this would present only a very fragile solution
You cannot use require in directive like that, however , since the only thing you need to pass between directives is a string , just bind them to the same property in parent controller (it can be parent directive controller):
...
<div ng-app='app' ng-controller='MyCtrl as ctrl'>
<my-dir-one s1='ctrl.message'></my-dir-one>
<my-dir-two s2='ctrl.message'></my-dir-two>
and first directives:
app.directive('myDirOne', function ($route) {
return {
templateUrl: 'views/my-dir-one.html',
restrict: 'E',
scope: {
s1: '=',
second directive
app.directive('myDirTwo', function ($route) {
return {
templateUrl: 'views/my-dir-one.html',
restrict: 'E',
scope: {
s2: '=',

Categories