Angular.js: custom directive - javascript

I am trying to add custom directive in my app. But it is not getting called on button click event.
my controller-
appServices.directive('customClick', function() {
return {
restrict: 'E',
require: 'ngModel',
link: function($scope, element, attrs) {
$scope.deleteFieldMap = function() {
alert('inside click');
}
}
}
}
my jsp-
<button custom-click class="btn btn-danger btn-sm"
data-style="zoom-in"
ng-click="deleteFieldMap(editProductJob,$index)"
name="jobFileKey"
title="Delete" >
<span class="glyphicon glyphicon-remove"></span>
</button>
What i am doing wrong here?

Your directive is restricted to 'E'. Which means to "element".
You should change it to 'A' since you expect an it as an "attribute".
Check out the reference documentation :
https://docs.angularjs.org/guide/directive
Edit : As explained by Medet, you also miss the "ng-model" on your element. Remove the definition if is his unecessary or add the attribute if you really expect it.
Regards

First issue as noted above is element.restrict: 'A', seconds issue - you must have ng-model attribute on your button, demo below
angular.module('app', [])
.run(function($rootScope) {
$rootScope.test = '123qe';
}).directive('customClick', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function($scope, element, attrs, ngModelCtrl) {
$scope.deleteFieldMap = function() {
alert('inside click' + ngModelCtrl.$viewValue);
}
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<div ng-app="app">
<button custom-click ng-click="deleteFieldMap(editProductJob,$index)" ng-model="test">
remove
</button>
</div>

you should use your custom directive as follow:
<custom-click class="btn btn-danger btn-sm"
data-style="zoom-in"
ng-click="deleteFieldMap(editProductJob,$index)"
name="jobFileKey"
title="Delete" >
<span class="glyphicon glyphicon-remove"></span>
</custom-click>
as an element!

Related

Execute a javascript function within a controller from a directive

I need to execute a javascript function that resides inside a controller. I need to call the function from within a directive.
The arguments I'm passing are fine. My method name in the controller is "GetAttachments".
When I'm debugging, and using scope, the method name GetAttachments doesn't appear.
Can someone please help me to be able to execute the named function?
Here is my directive. I need to know the proper syntax of the line: scope.GetAttachments(attrs.downloadType, attrs.downloadId). Note that my arguments are fine...
.directive('download', ['$modal', function ($modal)
{
return {
restrict: 'E',
transclude: false,
replace: true,
template: '<a style="padding-right: 5px; color:#fff !important;" class="pull-right" href="#" ng-click="opendownload()"><i class="fa fa-files-o fa-lg" style="padding-right: 5px"></i>Download</a>',
link: function (scope, elem, attrs, controller)
{
scope.opendownload = function ()
{
$modal.open({
templateUrl: root + 'AccountingModule/modal/attachment/download-modal.html',
size: 'md',
backdrop: true,
controller: 'downloadSPDocumentsController as downloadCtrl',
resolve: {
attributes: function () { return attrs; },
}
});
scope.GetAttachments(attrs.downloadType, attrs.downloadId)
}
}
}
}])
Here is my JS function inside the controller:
module.controller('downloadSPDocumentsController', ['$scope', '$http', '$modalInstance', '$location', '$window', 'attributes',
function ($scope, $http, $modalInstance, $location, $window, attributes)
{
var viewModel = this;
viewModel.attributes = attributes;
var DocumentDownloadarr;
viewModel.GetAttachments = function (CheckID, FileID)
{
Here is the HTML
<!--<p>For Testing Purpose: Download Type: {{downloadCtrl.attributes.downloadType}}</p>
<p>For Testing Purpose: ID: {{downloadCtrl.attributes.downloadId}}</p>-->
<div class="modal-header">
<h3 class="modal-title">File Download</h3>
</div>
<div class="modal-body" cg-busy="{promise:downloadCtrl.promise}">
<ul ng-init="downloadCtrl.Init()" class="list-unstyled">
<li ng-repeat="item in downloadCtrl.DocumentDownloadarr">
<div class="col-sm-12">
<div class="form-group">
<div class="col-sm-10">
<input type="text" class="form-control" ng-value="item.FileDescription" ng-readonly="true" />{{item.ExternalDocumentId}}
</div>
<div class="col-sm-2">
<button type="button" class="btn btn-default" ng-click="downloadCtrl.DownLoadAttachment(item.ExternalDocumentId, item.FileDescription)">Download</button>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="modal-footer">
<div class=" btn-toolbar pull-right" role="toolbar">
<!--<div class="btn-group" role="group">
<button type="button" class="btn btn-default" ng-click="downloadCtrl.GetAttachments(downloadCtrl.attributes.downloadType, downloadCtrl.attributes.downloadId)">List Attachments</button>
</div>-->
<div class="btn-group" role="group">
<button type="button" class="btn btn-default" ng-click="$close()">Close</button>
</div>
</div>
</div>
You need to expose your function GetAttachments in the controller via the scope.
Instead of
viewModel.GetAttachments = function (CheckID, FileID)
try this:
$scope.GetAttachments = function (CheckID, FileID)
However please note that this will work only if the directive and the controller is sharing the scope (which I think is the case on your code). However if you want to use isolated scope (for better modularity and re-usability) of the directive , then you will have to pass a reference of the method (in your controller) to the directive's scope using '&' binding.
Take a look at this fiddler here for a demo of isolated scope and calling function in controller from directives
You might want to consider raising an event in your directive using something like:
$rootScope.$broadcast('myEventHappened');
And then in your controller listen for it with something like:
$scope.$on('myEventHappened', function() {});
This approach will keep you from tightly coupling your directive to your controller.

same template coming up for two different angular directives

I have two separate angular directives that I'm calling (see below markup), but the last template is always the one that appears for both directives in the markup section below.
As you can see, I have the templateUrl set differently for both directives (in the directives section below), yet the last one in the markup section (attachment-modal.html) is always the one that appears.
If I make the download-modal.html the last one, then that template will appear for both directives. This is also seen by placing breakpoints in each of the directives. The first directive that you have defined in the markup, never gets executed even though it gets clicked on.
Both templates have different markup in them. If I comment out one of the directives, then the template associated with that directive comes out for both directives.
After manipulating the markup, no matter what I did, whichever directive was the latter one, is the directive that got executed.
It seems like I can't have two directives on the same web page because only the last one defined in the markup will get executed.
I tried it in both IE & Chrome.
What do I need to do to have the associated templates come out for each of the respective directives?
markup
<h3 class="panel-title">Check Deposit Header Information <download download-type="CK" download-id={{cdmCtrl.copiedRow.CheckDepositHeaderId}}>
</download> <attachment attachment-type="CK" attachment-id={{cdmCtrl.copiedRow.CheckDepositHeaderId}}>
</attachment>
</h3>
templates
download template
<p>For Testing Purpose: Download Type: {{downloadCtrl.attributes.downloadType}}</p>
<p>For Testing Purpose: ID: {{downloadCtrl.attributes.downloadId}}</p>
<div class="modal-header">
<h3 class="modal-title">File Download</h3>
</div>
<div class="modal-footer">
<div class="btn-toolbar pull-right" role="toolbar">
<div class="btn-group" role="group" >
<button type="button" class="btn btn-default" file-download download-type={{downloadCtrl.attributes.downloadType}} download-id={{downloadCtrl.attributes.downloadId}}>Download files</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default" ng-click="$close()">Close</button>
</div>
</div>
</div>
upload template
<p>For Testing Purpose: Attachment Type: {{attachCtrl.attributes.attachmentType}}</p>
<p>For Testing Purpose: ID: {{attachCtrl.attributes.attachmentId}}</p>
<div class="modal-header">
<h3 class="modal-title">File Attachment</h3>
</div>
<div class="modal-body">
<input type="file" id="inpFile" file-model="myFile" />
</div>
<div class="modal-footer">
<div class="btn-toolbar pull-right" role="toolbar">
<div class="btn-group" role="group" >
<button type="button" class="btn btn-default" file-upload attachment-type={{attachCtrl.attributes.attachmentType}} attachment-id={{attachCtrl.attributes.attachmentId}}>Upload</button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-default" ng-click="$close()">Close</button>
</div>
</div>
</div>
directives
.directive('attachment', ['$modal', function($modal) {
return {
restrict: 'E',
transclude: false,
replace: true,
template: '<a style="padding-right: 5px" class="pull-right" href="#" ng-click="open()"><i class="fa fa-files-o fa-lg" style="padding-right: 5px"></i>Attachment</a>',
link: function(scope, elem, attrs, controller) {
scope.open = function() {
$modal.open({
templateUrl: root + 'AccountingModule/modal/attachment/attachment-modal.html',
size: 'md',
backdrop: true,
controller: ['attributes', function(attributes) {
var viewModel = this;
viewModel.attributes = attributes;
}],
controllerAs: 'attachCtrl',
resolve: {
attributes: function() {
return attrs;
}
}
});
}
}
}
}])
.directive('download', ['$modal', function($modal) {
return {
restrict: 'E',
transclude: false,
replace: true,
template: '<a style="padding-right: 5px" class="pull-right" href="#" ng-click="open()"><i class="fa fa-files-o fa-lg" style="padding-right: 5px"></i>Download</a>',
link: function(scope, elem, attrs, controller) {
scope.open = function() {
$modal.open({
templateUrl: root + 'AccountingModule/modal/attachment/download-modal.html',
size: 'md',
backdrop: true,
controller: ['attributes', function(attributes) {
var viewModel = this;
viewModel.attributes = attributes;
}],
controllerAs: 'downloadCtrl',
resolve: {
attributes: function() {
return attrs;
}
}
});
}
}
}
}])
For the ng-click events in the directives, I needed to specify a different name for each scope opened.
For instance, openattachments & opendownloads for the scope.open functions since scope was a global variable and the last one always overwrote the first one.
template: '<a style="padding-right: 5px" class="pull-right" href="#" ng-click="openattachments()"><i class="fa fa-files-o fa-lg" style="padding-right: 5px"></i>Attachment</a>',

How to fix Jquery focus issue using with AngularJS directive?

I have created a directive character-counter for character counter when user enter text into textarea its working as expected but i am having one issue here when user finish typing in textarea i have to click on save to post the data but because of focusOut i have to click twice on save button so on first click it focus out the counter and second click i am able to save the data.
Is there any other solution that can take care of this problem that i can use for this directive ?
main.html
<div class="row">
<div class="col-md-12">
<textarea rows="8" class="textAreaModal" ng-model="ratingQstnResult.rationaleSelect[ratingQstnResult.rationaleSelectedKey]" maxlength="4000" character-counter></textarea>
</div>
</div>
<div class="row">
<div class="modal-footer">
<button type="submit" class="btn btn-primary pull-right" ng-click="rationaleWin.close();">Save</button>
<button class="btn btn-default" ng-click="ratingQstnResult.rationaleSelect[ratingQstnResult.rationaleSelectedKey]=null; rationaleWin.close();">Cancel</button>
</div>
</div>
directive.js
angular.module('riskAssessmentApp').directive('characterCounter', function () {
'use strict';
return{
restrict: 'A',
require: '^ngModel',
link: function (scope, element, attrs, ngmodel) {
var characterCount;
element.after('<p class="character-count" style="display: none;"><span class="characters-left"></span> characters left</p>');
element.focus(function(){
element.next().show();
});
element.focusout(function(){
element.next().hide();
});
scope.$watch(function(){
return ngmodel.$viewValue;
}, function(newVal){
if(newVal){
characterCount = parseInt(attrs.maxlength - newVal.length, 10);
} else{
characterCount = parseInt(attrs.maxlength, 10);
}
element.next().find('.characters-left').text(characterCount);
});
}
};
});
you don't return the event from the focusout() method and the event is not propagating. Try this:
element.focusout(function(event){
element.next().hide();
return event;
});

How to add attribute to custom angular directive

I need to add an attribute to a custom angular directive but I do not know how to bind the attribute (width) from the html part to the javascript that manages the behavior.
this is the html:
<div class="dropdown btn-group">
<button type="button" class="btn btn-default" data-bind="dropdown-label">{{initialValue}}</button>
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-scrollable" role="menu" aria-labelledby="dropdownMenu">
<li role="presentation" ng-repeat="value in values"
ng-click="clickHandler(value,$event)">
<a role="menuitem" tabindex="-1">{{value}}</a>
</li>
</ul>
this is the javascript behind html:
angular.module('platform.directives').directive('dropdownComponent', function() {
'use strict';
return {
restrict: 'E',
scope: {
initialValue: '#',
values: '=',
selectedValue: '='
},
templateUrl: 'modules/directives/dropdown/dropdown.html',
link: function(scope) {
scope.clickHandler = function findAndFillSelectedValueAndCloseDropDownArea(value, event) {
var $target = $(event.currentTarget);
$target.closest('.btn-group')
.find('[data-bind="dropdown-label"]').text($target.text())
.end()
.children('.dropdown-toggle').dropdown('toggle');
scope.selectedValue = value;
return false;
};
}
};
});
this is a usage:
<dropdownComponent
initial-value={{'PERMISSION.CREATE.DROPDOWN.RESOURCE'|translate}}
selected-value="permissionCtrl.permission.resourceId"
values="permissionCtrl.resources"
width="200px">
</dropdownComponent>
So basically I want to add a width attribute to this angular directive.
Thank you for your help!
You just need to pass it to the scope like you do with all 3 other variables:
scope: {
initialValue: '#',
values: '=',
selectedValue: '=',
width: "#"
},
And now you can just use scope.width in the javascript of the directive to add to elements for example.
And in HTML (which you should change dropdownComponent to dropdown-component by the way):
<dropdown-component
initial-value={{'PERMISSION.CREATE.DROPDOWN.RESOURCE'|translate}}
selected-value="permissionCtrl.permission.resourceId"
values="permissionCtrl.resources"
width="200px"></dropdown-component>
EDIT: In your directive HTML, change the first button to:
<button type="button"
class="btn btn-default"
data-bind="dropdown-label"
ng-style="width: {{width}}">{{initialValue}}</button>

angularjs: how does the replace attribute works for directive ?

I'm trying to figure out how the 'replace' attribute work for directive. I've run into a scenario when setting it to true was causing my code to break.
The directive:
angular.module('cdt.dm.directives').directive('referenceFiles', [
function () {
return {
restrict: 'E',
templateUrl: 'app/dm/views/templates/referenceFiles/referenceFiles.html',
scope: {
job: '='
},
link: function (scope, element, attr) {
scope.deleteReferenceFile = function (id) {
scope.job.references.splice(id, 1);
}
}
}
}]);
the referenceFiles.html template:
<div class="grid-action-filter" popover-placement="left" popover-template="app/dm/views/templates/referenceFiles/simple.html">
<span class="badge" style="cursor:pointer" >{{job.references.length}} added</span>
the simple.html template used by the popover directive:
<span>{{job.references.length}} reference files</span>
<table ng-repeat="ref in job.references">
<tr>
<td>{{ref.name}}</td>
<td>
<button class="btn grid-button btn-danger" ng-click="deleteReferenceFile($index);"><i class="fa fa-trash-o"></i></button>
</td>
</tr>
</table>
If I set replace to true in the referenceFiles directive, then the deleteReferenceFile method won't be found on the scope when clicking the button. Instead I have to call it this way:
$parent.$parent.$parent.$parent.deleteReferenceFile($index)
needless to say, it's ugly...
If I remove the replace attribute of the directive, then everything works fine.
Could someone explain this behaviour ?

Categories

Resources