How ng-click call for ng-directive function - angularjs

In my directive I have a html button. I want to call a function when it is clicked. I have not much knowledge about angular js. So I want to open a modal when the button clicked.
I think the path is,
html button -> showModal() function inside the directive. this showModal() is a webservice function which call a modal with webservice.
I have added the ng-click="documentManagerButtonCtrl.showDocumentModal()" in the html.
but it is not calling the directive function. can anyone explain how to open a modal with a service when click the button
<div class="col">
<a class='btn'
ng-click="pimDocumentManagerButtonCtrl.showDocumentModal()"
data-target='demoModal' modal>Create</a>
</div>
(function () {
'use strict';
angular
.module('app.directives')
.directive('documentManagerButton', documentManagerButton);
/* #ngInject */
function documentManagerButton() {
return {
restrict: 'E',
templateUrl: 'directives/document_manager.html',
scope: {
datagroupName: "#",
employeeNumber: "=",
documentTemplateDetails: "=",
employeeService: "="
},
bindToController: true,
link: function (scope, element, attrs) {
},
controller: documentManagerController,
controllerAs: 'DocumentManagerButtonCtrl'
};
}
documentManagerController.$inject = ['webservice', '$q', 'modalHelperService'];
function documentManagerController(webservice, $q) {
var vm = this;
function showDocumentModal(){
modalHelperService.showModal(
'app/document/document_modal.html',
'documentController',
'modal'
);
}
}
})();

Change
controllerAs: 'DocumentManagerButtonCtrl'
to
controllerAs: 'vm'
because you have
var vm = this;
After that you need expose showDocumentModal() function with
vm.showDocumentModal = showDocumentModal;
And then you can call with ng-click:
ng-click="vm.showDocumentModal()"

Related

Angular directive not working if loaded with RequireJS

I have a directive that works correctly when RequireJS is not used, and I'm trying to migrate it to an application based on RequireJS.
The directive wraps an Angular UI modal, and uses transclude to populate the modal elements (modal elements are defined in the controller that declares the directive). The problem is that if loaded with RequireJS, the modal does not show ANY elements (i.e. it's empty).
This is the plunk of the directive that works correctly without RequireJS. You will see a modal populated with elements.
This is the plunk of the directive that is loaded with RequireJS. You will see that the modal is empty.
There are no errors thrown when the modal is displayed empty, so I'm not sure how to tackle this problem. Any help will be greatly appreciated.
This is the directive:
define(['app','uiBootstrap'], function (app) {
'use strict';
app.directive("theModal", function($timeout,$uibModal) {
return {
restrict: "AE",
scope: {
control: '='
},
transclude: true,
link: function (scope, element, attrs, ctrl, transclude) {
scope.control = scope.control || {}
scope.control.openModal = function () {
var instance = $uibModal.open({
animation: false,
scope: scope,
windowClass: 'the-modal',
template: '<div>in the template</div><div class="content"></div>',
appendTo: element
});
$timeout(function (){
transclude(scope.$parent, function(clonedContent){
element.find('.content').append(clonedContent);
})
},10);
};
}
}
});
});
And this is how I invoke it:
<div ng-controller="ctl">
<button ng-click="open()">open it!</button>
<div the-modal control="modalCtl">
<p>some text</p>
<input type="text" ng-model="input1" />
</div>
</div>
The issue is you have circular dependency. The app needs your modal module to display things correctly, but your modal directive needs that app. The solution is to load your modal directive into a separate module.
Define a separate Angular Module for Modal
// contents of modal.js (remove app dependency)
define(['uiBootstrap'], function () {
'use strict';
var mod = angular.module('modal', ['ui.bootstrap']);
mod.directive("theModal", function($timeout, $uibModal) {
return {
restrict: "AE",
scope: {
control: '='
},
transclude: true,
link: function (scope, element, attrs, ctrl, transclude){
// removed code for brevity
}
}
});
mod.directive("theButton", function($timeout) {
return {
restrict: "AE",
scope: {
control: '='
},
transclude: true,
link: function (scope, element, attrs, ctrl, transclude){
// removed code for brevity
}
}
});
return mod;
});
Make app depend on Modal
// contents of app.js
define([
'angular',
'uiBootstrap',
'uiRouter',
'modal' // <--- the modal.js directive
], function (angular) {
'use strict';
return angular.module('app', ['ui.bootstrap','ui.router', 'modal']); // <--- also add it here
});

Angular - pass object from directive to parent scope on click

I'm using an angular directive to generate a reusable template and show some data in it. The directive also has an ng-click that should take an object and pass it to the parent controller. I'm kind of stuck, not really sure how to pass that data from the directive controller to the scope of the parent controller. I read here but the circumstances are a bit different.
The js code of the directive:
angular.module("app")
.directive('userData', function() {
return {
restrict: "E",
templateUrl: "directives/userData/userData.html",
scope: {
userObj: "="
},
controller: function($scope){
},
link: function(scope, elements, attrs, controller){
}
}
});
And this is the directive html:
<div class="style" ng-click="displayFullDetails(userObj)">{{userObj.first_name}}</div>
Parent controller:
angular.module("app").controller("parentCtrl", ['$scope', function ($scope) {
angular.element(document).ready(function () {
getDataService.getJsonData().then(function (data) {
$scope.users = data.data;
})
});
}]);

How to watch controller asynchronous data in angular directive

I define a directive in angular 1.4,which receives a scope parameter “b”:
(function() {
'use strict';
angular
.module('m')
.directive('mydirective', mydirective);
/** #ngInject */
function mydirective() {
var directive = {
restrict: 'E',
templateUrl: 'app/components/mydirective/mydirective.html',
scope: {
b: '='
},
controller: MydirectiveController,
controllerAs: 'vm',
bindToController: true
};
return directive;
/** #ngInject */
function MydirectiveController($scope, $state) {
var vm = this;
//here How to watch the parameter b to refresh the directive html result?
}
in html page:
<mydirective b="ctrl.b"></myupl>
in the business controller, b is got from an angular resource
PayService.getBusinessNumber().then(function(results){
vm.b = {business_id: results.no};
});
in index.route.js which define the route and the business controller,
$stateProvider
.state('payShowInfo', {
url: '/payShowInfo',
templateUrl: 'app/pay_show_info.html',
controller: 'PayShowController',
controllerAs: 'ctrl'
});
my problem is , When the directive loaded, the parameter “b” is undefined,  How to watch controller asynchronous data in angular directive? and then use the new value of “b” to refresh the html page?
If you define the directive parameter as "=", you mean that you will have two way data binding. So if you change the value in the controller, you will have the change reflected in the view.
angular.module('nib', [])
.directive('mydirective', function ($timeout) {
return {
restrict: 'E',
scope: {
b: '='
},
link: function ($scope, element, attrs) {
},
template: '<div>Test: {{b}}</div>'
};
})
.controller('ctrl',function($scope){
$scope.click = function(){ // emulating asynchronous request
$scope.test = 'testing';
}
})
See in the plnkr below:
http://plnkr.co/3qs1NpU1aIhKzxyCMXjh?p=preview

Event not triggering for runtime controls in angularjs directive

Created a directive using AngularJS. The directive displays a runtime (dynamic) controls but it is not triggering an event from controller. Can any one help me.
Note: used compile in directive too.
The plnkr link is given below.
module.controller('controllerOne', ['$scope', function ($scope) {
$scope.AddClickButton = function () {
alert("Button Clicked");
};
}]);
module.directive('directiveOne', ['$compile', function ($compile) {
return {
restrict: 'E',
scope: {
data: '='
},
link: function ($scope, $element, $attrs) {
var btnhtml = '<button type="button" ng-click="AddClickButton()">Click Me</button>';
var temp = $compile(btnhtml)($scope);
$element.append(temp);
}
};
}]);
HTML
<form name="form1" >
<directive-one data="dataOne"></directive-one>
</form>
Plunkr
You should be passing AddClickButton method reference to directive using &(expression binding) as directive has been using isolated scope. So it don't have any idea about parent scope directly.
Markup
<form name="form1" >
<directive-one data="dataOne" click-action="AddClickButton()"></directive-one>
And then call that method from directive using that clickAction isoalted scope prop.
Directive
module.directive('directiveOne', ['$compile', function($compile) {
return {
restrict: 'E',
scope: {
data: '=',
clickAction: '&'
},
link: function($scope, $element, $attrs) {
var btnhtml = '<button type="button" ng-click="clickAction()">Click Me</button>';
var temp = $compile(btnhtml)($scope);
$element.append(temp);
}
};
}]);
Forked Plunkr
You can use this version http://plnkr.co/edit/9QCpAQUhOKZ3NtU1rfhY
link: function ($scope, $element, $attrs) {
$scope.AddClickButton = function() {
alert($scope.text);
}
}
and remove the function from the controller

How to invoke ng-click from a directive partial?

I have a directive that has a local scope where a partial contains ng-click.
The Fiddle is there: http://jsfiddle.net/stephanedeluca/QRZFs/13/
Unfortunatelly, since I moved my code to the directive, ng-click does not fire anymore.
The controller and the directive is as follows:
var app = angular.module('myApp', ['ngSanitize']);
app.directive('plantStages', function ($compile) {
return {
restrict: 'E',
transclude: true,
template: '<figure class="cornStages">\
<p ng-transclude style="color: skyblue"></p>\
<hr/>\
<p ng-bind-html="title"></p>\
<p ng-bind-html="subtitle">{{subtitle}}</p>\
<ul>\
<li ng-repeat="stage in stages" ng-click="changePage(stage)">{{stage}}</li>\
</ul>\
</figure>',
scope: {
stages:"=",
title:'#'
},
link: function (scope, element, attrs, ctrl, transclude) {
if (!attrs.title) scope.title = "Default title";
}
};
});
app.controller('myCtrl', function ($scope, $location, $http) {
$scope.stages = ['floraison', 'montaison'];
$scope.changePage = function (page) {
var url = "corn.page.html#/"+page;
console.log("Change page "+page+" with url "+url);
alert("about to change page as follows: document.location.href = "+url);
};
});
The html that invokes it is as follows:
<div ng-controller="myCtrl">
Stages,
<p ng-repeat="stage in stages">{{stage}}</p>
<hr/>
Plant stages
<plant-stages
title="<b>Exploration<br/>du cycle</b>"
subtitle="<em>This is a<br/>sub title</em>"
stages="stages"
>
Inner<br/>directive
</plant-stages>
</div>
Any idea?
You can't access changePage() defined in controller's scope from directive directly, since your directive has isolated scope. However, there are still several ways to do it:
Option 1:
Option 1 is the most simple option. However it is much like a workaround and I don't recommend to use it widely. You can get your controller's scope from element passed to link function and invoke changePage there:
link: function (scope, element, attrs, ctrl, transclude) {
if (!attrs.title) scope.title = "Default title";
scope.changePage = element.scope().changePage; // <= Get parent scope from element, it will have changePage()
}
Option 2:
If you don't have any logic that involves scope defined in the outer controller (as in your example), you can define inner controller for your directive and perform it there:
app.directive('plantStages', function ($compile) {
return {
...
controller: ['$scope', function($scope) {
$scope.changePage = function(page) {
var url = "corn.page.html#/"+page;
console.log("Change page "+page+" with url "+url);
alert("about to change page as follows: document.location.href = "+url);
}
}]
};
});
Option 3:
If you want do reuse logic defined in changePage() in different directives and controllers, the best way to do it is to move the logic to some service that may be injected to both controller and directive:
app.service('changePageService', function() {
this.changePage = function(page) {
var url = "corn.page.html#/"+page;
console.log("Change page "+page+" with url "+url);
alert("about to change page as follows: document.location.href = "+url);
}
});
app.controller('myCtrl', function ($scope, $location, $http, changePageService) {
...
changePageService.changePage('page');
...
});
app.directive('plantStages', function ($compile) {
...
controller: ['$scope', 'changePageService', function($scope, changePageService) {
$scope.changePage = changePageService.changePage;
}]
...
});
Option 4:
You can pass piece of code like changePage(page) as value of some attribute of the directive and inside directive define scope property with '&' that will create a function that will be executed in the outer controller's scope with arguments passed to that function. Example:
JavaScript
app.directive('plantStages', function ($compile) {
return {
restrict: 'E',
transclude: true,
template: '<figure class="cornStages">\
<p ng-transclude style="color: skyblue"></p>\
<hr/>\
<p ng-bind-html="title"></p>\
<p ng-bind-html="subtitle"></p>\
<ul>\
<li ng-repeat="stage in stages" ng-click="changePage({page: stage})">{{stage}}</li>\
</ul>\
</figure>',
scope: {
stages:"=",
title:'#',
changePage:'&'
},
link: function (scope, element, attrs, ctrl, transclude) {
if (!attrs.title) scope.title = "Default title";
}
};
});
HTML
<div ng-controller="myCtrl">
Stages,
<p ng-repeat="stage in stages">{{stage}}</p>
<hr/>
Plant stages
<plant-stages
title="<b>Exploration<br/>du cycle</b>"
subtitle="<em>This is a<br/>sub title</em>"
stages="stages"
change-page="changePage(page)"
>
Inner<br/>directive
</plant-stages>
Plunker: http://plnkr.co/edit/s4CFI3wxs0SOmZVhUkC4?p=preview
The idea of directives is to treat them as reusable components and avoid external dependencies wherever possible. If you have the possibility to define the behavior of your directive in its own controller then do it.
module.directive('myDirective', function () {
return {
restrict: 'E',
controller: function() { /* behaviour here */ },
template: '<div>Directive Template</div>',
scope: {
/* directive scope */
}
};
});
If this is not possible you can pass the function as explained in the linked question (see comment above). Check the updated fiddle.

Resources