AngularJS calling from Parent to Child Directive controller function - angularjs

I am use to working in Angular and now I am on AngularJS ( The otherway round)
I've a directive:
<li ng-mouseover="vm.setCurrentEditedTile(item.id)">
<panel-buttons-directive ></panel-buttons-directive>
</li>
My panel-buttons-directive has a controller called ButtonsController.
What I would like when user hovers on top of <li> element, it run a function that is inside the child controller. So that I have a separate "Module" where I have buttons HTML in the directive and function in the controller and from the parent I can call the function.
Link: https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md

One approach is to have the directive publish an API when initialized:
<fieldset ng-mouseover="pbdAPI.setCurrentEditedTile(item.id)">
Mouseover Me
</fieldset>
<panel-buttons-directive on-init="pbdAPI=$API">
</panel-buttons-directive>
app.directive("panelButtonsDirective", function() {
return {
scope: { onInit: '&' },
bindToController: true,
controller: ButtonsController,
controllerAs: '$ctrl',
template: `<h3>Panel Buttons Component</h3>
<p>Current edited tile = {{$ctrl.id}}</p>
`,
};
function ButtonsController() {
var $ctrl = this;
var API = { setCurrentEditedTile: setCurrentEditedTile };
this.$onInit = function() {
this.onInit({$API: API});
};
function setCurrentEditedTile(id) {
$ctrl.id = id;
}
}
})
The directive in the above example uses expression & binding to publish its API when initialized.
The DEMO
angular.module("app",[])
.directive("panelButtonsDirective", function() {
return {
scope: { onInit: '&' },
bindToController: true,
controller: ButtonsController,
controllerAs: '$ctrl',
template: `<h3>Panel Buttons Component</h3>
<p>Current edited tile = {{$ctrl.id}}</p>
`,
};
function ButtonsController() {
var $ctrl = this;
var API = { setCurrentEditedTile: setCurrentEditedTile };
this.$onInit = function() {
this.onInit({$API: API});
};
function setCurrentEditedTile(id) {
$ctrl.id = id;
}
}
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<h3>Mouseover Component DEMO</h3>
<p><input ng-model="item.id" ng-init="item.id='tile0'"/></p>
<fieldset ng-mouseover="pbdAPI.setCurrentEditedTile(item.id)">
Mouseover Me
</fieldset>
<panel-buttons-directive on-init="pbdAPI=$API">
</panel-buttons-directive>
</body>

Related

How to handle ng-click inside directive Angular JS?

I have the following directive:
.directive("feedList", function() {
return {
restrict: 'E',
scope: {
feeds: '=feedData'
},
templateUrl: 'jstemplates/feed-list.html',
link: function(scope) {
angular.forEach(scope.feeds, function(value, key) {
if(value.who.fullname == " "){
scope.feeds[key].fullname = "email";
}
console.log(value.who.fullname);
});
}
}
})
Inside my template there is an event: ng-click="do()". How to handle this event in directive ot in parent controller?
As it's your isolated scope directive, so pass the callback function and then call that function directly from template or from controller or link function.
Working fiddle
var app = angular.module('myApp', []);
app.controller('AppCtrl', function($scope){
$scope.testFunction = function(){
alert("Called from isolated scope directive");
};
});
app.directive("isolatedScopeDirective", function(){
return{
scope:{
go:"&"
},
template : `<button ng-click='go()'>Test Button</button>`
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="AppCtrl">
<isolated-scope-directive go="testFunction()"></isolated-scope-directive>
</div>

Rebinding this on angular callback from inside link funtion

I have a directive similar to this this:
app.directive('example', function() {
return {
restrict: 'E',
transclude: true,
scope: {
callback: '&'
},
template: '<span ng-click="example.callback()">Click Me</span>',
bindToController: true,
controllerAs: 'example',
controller: function() {
this.counter = 0;
this.incrementCount = function() {
this.counter++;
};
this.getCount = function() {
return this.counter;
};
},
link: function(scope, el, attrs, ctrl) {
var oldCallback = scope.callback;
ctrl.callback = function() {
console.log(ctrl);
return oldCallback.call(ctrl); // I want to be able to use `this` as the controller to access the API from within the callback
};
}
};
});
with a controller
app.controller("ctrl", ["$scope", function(s) {
s.callback = function() {
this.incrementCount();
console.log("Value: " + this.getCount());
};
}]);
And view
<div ng-app="app">
<div class="container" ng-controller="ctrl">
<example callback="callback()"></example>
</div>
</div>
(codepen)
When I log ctrl in within the ctrl.callback in the link function it logs the example controller as I expect but when oldCallback is called, it doesn't get ctrl rebound to this as I want. Is there any way to access the API defined in the directive's controller from within the callback on the scope while still using an isolate scope for the directive?
You could pass the directives controller out through the callback. e.g.
example html
<span ng-click="example.callback({$exampleCtrl:example})">Click Me</span>
index html
<example callback="callback($exampleCtrl)"></example>
controller
$scope.callback = function($exampleCtrl) {
$exampleCtrl.incrementCount();
console.log("Value: " + $exampleCtrl.getCount());
};
http://codepen.io/anon/pen/BzqqzV
Also note that bindToController is only supported in AngularJs 1.3+ and your code pen was using 1.2

How to Access HTML Inside of Angular Directive (Transclusion)

I am using angular-ui. I am trying to reduce html coding my making reusable elements with angular directives. I'm definitely missing something. What I want to do is this:
<modal modal-title="Some Title" button-text="Click Me">Modal Content</modal>
And I want that to output the modal and button so instead of adding all that markup over and over for the modal I can just use this custom element. It seems to be working except that I can not for the life of me figure out how to get the content inside of the element. Here's the basics of what I'm doing:
angular.module('app').directive('modal', function () {
return {
templateUrl: 'partials/modal.html',
restrict: 'E',
controller: 'modalController',
controllerAs: 'mCtrl',
transclude: true,
link: function postLink(scope, element, attrs) {
scope.buttonText = attrs.buttonText;
scope.modalTitle = attrs.modalTitle;
}
};
}).controller('modalController', function($scope,$modal,$attrs) {
var _this = this;
this.buttonText = $attrs.buttonText;
this.modalTitle = $attrs.modalTitle;
this.open = function () {
var modalInstance = $modal.open({
templateUrl: 'modal-content.html',
controller: 'ModalInstanceCtrl',
controllerAs: 'miCtrl',
resolve: {
modalTitle: function() { return _this.modalTitle; }
}
});
modalInstance.result.then(function (selectedItem) {
//something
}, function () {
console.info('Modal dismissed at: ' + new Date());
});
};
this.save = function() {
//something
};
}).controller('ModalInstanceCtrl', function ($scope, $modalInstance, modalTitle) {
this.modalValue = 1;
this.modalTitle = modalTitle;
this.ok = function () {
$modalInstance.close(this.modalValue);
};
this.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
Here's the template (partials/modal.html)
<script type="text/ng-template" id="modal-content.html">
<div class="modal-header">
<h3 class="modal-title">{{miCtrl.modalTitle}}</h3>
</div>
<div class="modal-body"> {{ mCtrl.content }} </div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="miCtrl.ok()">OK</button>
<button class="btn btn-warning" ng-click="miCtrl.cancel()">Cancel</button>
</div>
</script>
<button class="btn btn-default" ng-click="mCtrl.open()">{{mCtrl.buttonText}}</button>
How do I get the content of the element into mCtrl.content? The rest of it works as expected, I'm just missing something. Thanks!
Edit: It seems I need to use transclusion, so this is what I want to do:
<div class="modal-body"><ng-transclude></ng-transclude></div>
But I get this kind of error when I open up the modal:
[ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: <ng-transclude>

Make directive function accessible in parent scope without events

I have a directive with an isolated scope and want to call its function to update data from the parent controller without using events.
var myApp = angular.module('MyApp',[]);
myApp.directive('myDirective', function() {
return {
scope: {},
link: function(scope) {
scope.update = function() {
alert('Directive updated!');
}
}
}
});
function MyCtrl($scope) {
$scope.updateDirective = function() {
// make me call update() function in directive
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<button ng-click="updateDirective()">Update!</button>
<span my-directive></span>
</div>
You could apply this solution.
In this way you are passing a variable in two way binding:
my-directive="myFunction" in the html
and myFunction: '=myDirective' in the directive)
Then assign the function in the directive:
scope.myFunction = function () {
alert('Directive updated!');
}
In this way you can use a function defined in a directive.
var myApp = angular.module('MyApp', []);
myApp.directive('myDirective', function () {
return {
scope: {
myFunction: '=myDirective'
},
link: function (scope) {
scope.myFunction = function () {
alert('Directive updated!');
}
}
}
});
function MyCtrl($scope) {
$scope.myFunction = {};
$scope.updateDirective = function () {
console.log( $scope.myFunction );
$scope.myFunction();
// make me call update() function in directive
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp" ng-controller="MyCtrl">
<button ng-click="updateDirective()">Update!</button> <span my-directive="myFunction"></span>
</div>
You could tackle this issue by introducing a new directive that is required by your isolated directive. Conveniently, you can assign the controller to this new directive.
Once required you then 'register' your isolated directive to the 'parent' directive as the target for your function. In the code snippet below I only provided a way to add 1 directive, but you could easily extend this to be an array of child directives. A good of example of such a setup are tabs, where each tab is a child directive of a common tabs directive.
angular.module("MyApp", []);
angular.module('MyApp').directive("myParentDirective", function(){
return {
controller: function ($scope) {
var childUpdate;
this.registerChild = function(_childUpdate_){
childUpdate = _childUpdate_;
};
$scope.updateDirective = function() {
childUpdate();
};
}
};
});
angular.module('MyApp').directive('myDirective', function() {
return {
require: '^myParentDirective',
scope: {},
link: function(scope, element, attrs, myParentController) {
myParentController.registerChild(update);
function update() {
alert('Directive updated!');
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div my-parent-directive>
<button ng-click="updateDirective()">Update!</button>
<span my-directive></span>
</div>
</div>

ng-hide doesn't work within hierarchy directives

I'm a newbie angular and will be happy to have some help here.
I'm struggeling to find out why I cannot setup one directive that is setting up an attribute hide="true" or "false" that will be used within the directive (Rank) as a parameter to change the inner directive (label) ng-hide to hide the label.
I tried everything
The outer directive (Rank) html:
<div>
<img src="/Components/Directives/images/blue_{{RankValue}}.svg" tooltip="{{RankValue}}/4" />
<label-info ng-hide="hide" header="{{header}}"></label-info>
</div>
The outer directive (Rank) directive java script:
angular.module('reusableDirectives')
.directive('Rank', function () {
return {
restrict: 'E',
scope: {
hide: '='
},
link: function (scope, element, attrs) {
scope.safeApply(scope.RankValue = scope.$eval(attrs.value));
scope.safeApply(scope.hidelabel = "true");
if (attrs.hidelabel == "false")
scope.safeApply(scope.hidelabel = "false");
scope.hidelabel = attrs.hide;
},
templateUrl: '/Components/Directives/Rank.html'
};
})
.controller('rankCtrl', ['scope', function ($scope) {
}]);
The inner directive (label) Html:
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3>{{header}}</h3>
</div>
<div class="modal-body">
<div ng-bind-html="items"></div>
</div>
</div>
<div class="modal-footer">
<div style="float:left;">
<button class="btn btn-primary" ng-click="ok()">Close</button>
</div>
</div>
</script>
<div>
<div class="fs-labelInfo-text">
{{header}}
</div>
<img class="fs-labelInfo-img"
ng-click="update(header)"
src="Components/Directives/images/questionMark.png" />
</div>
The inner directive (Label) directive java script:
angular.module('reusableDirectives')
.directive('labelInfo', function () {
return {
restrict: 'E',
scope: {
isolatedLabelHide: '#hidelabel'
},
controller: function ($scope, $element, $modal, $log, $http, $rootScope, myService) {
$scope.header = "header attribute";
$scope.caption = "label caption";
$scope.ok = function (header) {
myService.getLabelInfo(header).then(function (data) {
//this will execute when the AJAX call completes.
$scope.items = data;
console.log(data);
$scope.open();
});
};
$scope.open = function () {
$log.info('open');
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
header: function () {
return $scope.header;
},
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function () {
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
},
link: function (scope, element, attrs) {
scope.header = attrs.header;
},
templateUrl: '/Components/Directives/LabelInfo.html'
};
});
angular.module('reusableDirectives')
.controller('ModalInstanceCtrl', function ($scope, $modalInstance, header, items) {
$scope.items = items;
$scope.header = header;
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
The html that I'm using to test is:
One example to show the label:
<rank hide="false" value="3.5"></rank>
Another example to show the label:
<rank value="3.5"></rank>
example to hide will be:
<rank hide="true" value="3.5"></rank>
Thank you for your effort.
Best regards,
Chen
You set the scope property name as "hideLabel":
scope.hidelabel = attrs.hide;
So, you need to use "hideLabel" for the ng-hide attribute:
<label-info ng-hide="hideLabel" header="{{header}}"></label-info>
And you need to declare the ng-hide in your "labelInfo" directive template:
<div ng-hide="hideLabel">
(This "div" is the one that comes above <div class="fs-labelInfo-text"> at /Components/Directives/LabelInfo.html.)

Resources