AngularJS - Directive wrapping without losing connection to controller - javascript

Is there a way for not losing connection to the current controller when you are wrapping data with a directive ?
My problem is, that the directive within the wrapped template has no connection to the outside controller any more and so I can not execute the function.
Wrapping Directive:
myApp.directive('wrapContent', function() {
return {
restrict: "E",
scope: {
model: "=",
datas: "="
},
templateUrl: "./any/template.php",
link: function(scope, element, attr) {
// any
}
};
});
Directive within the wrapped Template
myApp.directive('doAction', function() {
return {
restrict: "A",
link: function(scope, elem, attrs) {
$(elem).click(function(e) {
scope.$apply(attrs.doAction);
});
}
}
});
Conroller:
lmsApp.controller('OutsideController', function ($scope){
$sope.sayHello = function() {
alert("hello");
};
});
HTML where I want to execute the function (template.php):
<div>
<do-action="sayHello()"></do-action>
</div>
How I call the wrapContent directive which is outside (Updated):
<div ng-controller="OutsideController">
<wrap-content model="any" datas="data_any"></wrap-content>
</div>
How can I execute the sayHello() function?
Thank you for your help! I would appreciate every answer.

wrapContent directive will be processed with the scope of controller.
DoAction directive will be processed with the isolateScope of wrapContent directive.
Solution1:
Get a reference to the sayHello function in wrapContent using '&' and execute it in event handler.
Solution2:
Instead of using scope in your event handler, use scope.$parent.

You should pass sayHallo function to your parent directive using &
myApp.directive('wrapContent', function() {
return {
restrict: "E",
scope: {
model: "=",
datas: "=",
sayHallo: "&"
},
templateUrl: "./any/template.php",
link: function(scope, element, attr) {
// any
}
};
});
HTML
<div ng-controller="OutsideController">
<wrap-content model="any" datas="data_any" sayHallo="sayHallo()"></wrap-content>
</div>
Then in your child directive, you will have sayHallo in your scope, to call it just do it this:
myApp.directive('doAction', function() {
return {
restrict: "A",
link: function(scope, elem, attrs) {
scope.sayHallo();
}
}
});
And you dont need pass it again. So your child directive should looks like this:
<div>
<do-action></do-action>
</div>
UPDATE
If you want to use all your parent model functions,without passing each function. In your child directive,just use scope.model to have access to model attributes and functions.
myApp.directive('doAction', function() {
return {
restrict: "A",
link: function(scope, elem, attrs) {
scope.model.sayHallo();
}
}
});

Related

passing data through Angular custom directive

I created a custom directive in angular. I would like to pass parent data through the directive using scope but I'm getting 'undefined' when I log scope and scope.questionId.
HTML
<a class="waves-effect waves-light btn" my-directive="" on-flag="someFunction" question-id="question">Flag</a>
Angular Directive
angular.module('myApp').directive('myDirective', function($http) {
return {
restrict: 'A',
scope: {
onFlag: '&onFlag',
questionId: '='
},
link: function(scope, element, attrs) {
element.click(function() {
console.log(scope);
console.log(scope.questionId);
return;
});
}
};
});
Try this
elem.bind:- It uses the JQLite which is lite version of JQuery. Here we are writing the code to handle the click event performed on the directive.
It is same like JQuery $("class or id").click(). (I hope this explains is sufficient)
angular.module('myApp').directive('myDirective', function($http) {
return {
restrict: 'A',
scope: {
onFlag: '&onFlag',
questionId: '='
},
link: function(scope, elem, attrs) {
elem.bind('click', function() {
console.log(scope.questionId);
});
}
};
});

change controller $scope from a directive

I have a controller:
function myController($scope) {
$scope.clicked = false;
}
and a directive:
function myDirective() {
return {
restrict: 'E',
link: function(scope, elem, attrs) {
elem.bind('click', function() {
// need to update controller $scope.clicked value
});
},
template: '<div>click me</div>';
replace: true;
}
}
and I´m using it like this:
<div ng-controller="myController">
<my-directive></my-directive>
</div>
How can I change the controller value of $scope.clicked ?
thanks!
As you don't use isolated scope in your directive, you can use scope.$parent.clicked to access the parent scope property.
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$parent.clicked = ...
});
},
I would not recommend using scope.$parent to update or access the parent scope values, you can two way bind the controller variable that needs to be updated into your directive, so your directive becomes:
function myDirective() {
return {
restrict: 'E',
scope: {
clicked: '='
},
link: function(scope, elem, attrs) {
elem.bind('click', function() {
// need to update controller $scope.clicked value
$scope.clicked = !$scope.clicked;
});
},
template: '<div>click me</div>';
replace: true;
}
}
now pass this clicked from parent:
<div ng-controller="myController as parentVm">
<my-directive clicked="parentVm.clicked"></my-directive>
</div>
function myController() {
var parentVm = this;
parentVm.clicked = false;
}
I would recommend reading up on using controllerAs syntax for your controller as that would really solidify the concept of using two way binding here.
I like to use $scope.$emit for such purposes. It allows to send data from directive to the controller.
You should create custom listener in your controller:
$scope.$on('cliked-from-directive', function(event, data){
console.log(data)
})
As you can see, now you have full access to your controller scope and you can do whatever you want. And in your directive just to use scope.$emit
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$emit('cliked-from-directive', {a:10})
});
Here I've created jsfiddle for you

How do I create an isolate scope of the directive's module name?

I have a directive called contenteditable with an isolate scope called post: "&"
Question:
How can I replace the isolated scope called Post with the default contenteditable directive.
angular.module('t23App').
directive("contenteditable", function() {
return {
restrict: "A",
require: "ngModel",
scope: {
post: "&"
},
link: function(scope, element, attrs, ngModel) {
console.log(contenteditable)
function read() {
ngModel.$setViewValue(element.html());
}
ngModel.$render = function() {
element.html(ngModel.$viewValue || "");
};
element.bind("blur keyup change", function() {
scope.$apply(read);
});
}
};
});
Therefore:
What currently looks like this
<div contenteditable post="dosomething()"> Click this</div>
In the end the html will look like :
<div contenteditable="dosomething()"> Click this</div>
Put the name of the directive in the scope:
scope: {
contenteditable: "&"
}

Interaction of directive and controller in AngularJS

I want to create a component that displays itself as a collapsible box.
When it is expanded, it should show the transcluded content; when it is collapsed it should only show its label.
myApp.directive('collapsingBox', function() {
return {
restrict: 'E',
transclude: true,
require: '^ngModel',
scope: {
ngModel: '='
},
template: '<div ng-controller="CollapseController" class="collapsingBox"><div class="label">Title: {{ ngModel.title }}</div><br/><div ng-transclude ng-show="expanded">Test</div></div>',
link: function($scope, element, attr) {
element.bind('click', function() {
alert('Clicked!');
$scope.toggle();
});
}
};
});
This component should be reusable and nestable, so I wanted to manage the values (like "title" and "expanded") in a controller that gets instantiated for every use of the directive:
myApp.controller('CollapseController', ['$scope', function($scope) {
$scope.expanded = true;
$scope.toggle = function() {
$scope.expanded = !$scope.expanded;
};
}]);
This "almost" seems to work:
http://plnkr.co/edit/pyYV0MAikXThvMO8BF69
The only thing that does not work seems to be accessing the controller's scope from the event handler bound during linking.
link: function($scope, element, attr) {
element.bind('click', function() {
alert('Clicked!');
$scope.toggle(); // this is an error -- toggle is not found in scope
});
}
Is this the correct (usual?) way to create one instance of the controller per use of the directive?
How can I access the toggle-Function from the handler?
Rather than using ng-controller on your directive's template, you need to put the controller in your directive's controller property:
return {
restrict: 'E',
transclude: true,
require: '^ngModel',
scope: {
ngModel: '='
},
template: '<div class="collapsingBox"><div class="label">Title: {{ ngModel.title }}</div><br/><div ng-transclude ng-show="expanded">Test</div></div>',
controller: 'CollapseController',
link: function($scope, element, attr) {
element.bind('click', function() {
alert('Clicked!');
$scope.toggle();
});
}
};
As it is CollapseController's scope will be a child scope of your directive's scope, which is why toggle() isn't showing up there.

Sharing Data between two Directives in AngularJS

I have the following code:
<div id='parent'>
<div id='child1'>
<my-select></my-select>
</div>
<div id='child2'>
<my-input></my-input>
</div>
</div>
I also have two directives which get some data from the data factory. I need the two directives to talk to each other such that when a value in select box is changed the input in changes accordingly.
Here's my two directives:
.directive("mySelect", function ($compile) {
return {
restrict: 'E',
scope:'=',
template: " <select id='mapselectdropdown'>\
<option value=map1>map1</option> \
<option value=map2>map2</option> \
</select>'",
link: function (scope, element, attrs) {
scope.selectValue = //dont konw how to get the value of the select
}
};
})
.directive("myInput", function($compile) {
return {
restrict: 'E',
controller: ['$scope', 'dataService', function ($scope, dataService) {
dataService.getLocalData().then(function (data) {
$scope.masterData = data.input;
});
}],
template: "<input id='someInput'></input>",
link: function (scope, element, attrs) {
//here I need to get the select value and assign it to the input
}
};
})
This would essentially do the onchange() function that you can add on selects. any ideas?
You could use $rootScope to broadcast a message that the other controller listens for:
// Broadcast with
$rootScope.$broadcast('inputChange', 'new value');
// Subscribe with
$rootScope.$on('inputChange', function(newValue) { /* do something */ });
Read Angular docs here
Maybe transclude the directives to get access to properties of outer scope where you define the shared variable ?
What does this transclude option do, exactly? transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.
-> https://docs.angularjs.org/guide/directive
After much research this is what worked...
I added the following:
.directive('onChange', function() {
return {
restrict: 'A',
scope:{'onChange':'=' },
link: function(scope, elm, attrs) {
scope.$watch('onChange', function(nVal) { elm.val(nVal); });
elm.bind('blur', function() {
var currentValue = elm.val();
if( scope.onChange !== currentValue ) {
scope.$apply(function() {
scope.onChange = currentValue;
});
}
});
}
};
})
Then on the element's link function I added:
link: function (scope, elm, attrs) {
scope.$watch('onChange', function (nVal) {
elm.val(nVal);
});
}
Last added the attribute that the values would get set to in the scope:
<select name="map-select2" on-change="mapId" >

Categories

Resources