Execute a javascript function within a controller from a directive - javascript

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.

Related

ANGULARJS: directive inside another directive doesn't have access to ng-model from HTML

I recently had a coding challenge that I got rejected for because it was garbage. Didn't have a lot of time so I threw everything together in one giant HTML file/angular controller, so I'm in the middle of rewriting it in templates to try to make it more reusable. So far it's going well, but I'm having some trouble with an html template not being able to access ng-model. Whenever I console.log the ng-model, I get undefined.
Here's the top layer HTML:
<div class="col-md-8 box">
<div class="panel panel-default">
<div class="panel-heading">Companies</div>
<div class="panel-body">
<div ng-repeat="company in companies">
<div class="panel panel-default">
<div class="panel-heading">Name: {{company.name}} <button ng-click="companies[$index].editCompany = !companies[$index].editCompany" class="pull-right">EDIT COMPANY</button></div>
<div class="panel-body" ng-if="!companies[$index].editCompany">
<p>Address: {{company.address}}</p>
<p>Revenue: {{company.revenue}}</p>
<p>Phone Number: {{company.phone}}</p>
<button ng-click="getPeople(companies[$index]._id, $index); companies[$index].viewEmployees = !companies[$index].viewEmployees">People Who Work Here</button>
<div ng-if="companies[$index].viewEmployees">
<show-employees-list></show-employees-list>
</div>
</div>
</div>
<div ng-if="companies[$index].editCompany">
<edit-company-directive></edit-company-directive>
</div>
</div>
</div>
</div>
</div>
And here's the HTML for the directive:
<div class="employee-box" ng-repeat="employee in companies[$index].employees">
<span class="glyphicon glyphicon-edit pull-right" ng-click="companies[$index].editEmployee = !companies[$index].editEmployee; clickEdit()"></span>
<span class="glyphicon glyphicon-remove pull-right" ng-click="deletePerson(employee._id, $index, companies[$parent.$index].employees)"></span>
<div ng-if="!companies[$index].editEmployee">
<div>
<p><b>Name:</b> {{employee.name}}</p>
<p><b>Email:</b> {{employee.email}}</p>
</div>
</div>
<div ng-if="companies[$index].editEmployee" class="form-body">
<form name="editPersonForm" ng-submit="editPerson(employee._id, $parent.$parent.index, $parent.index)">
<input type="text" ng-model="nameEdit" id="nameEdit" placeholder="Employee" class="form-control" required></input>
<input type="text" ng-model="emailEdit" id="emailEdit" placeholder="Email" class="form-control" required></input>
<button type="submit" id="submitButton" class="btn btn-success form-actions">Submit</button>
</form>
</div>
</div>
And here's the directive code:
'use strict';
(function() {
angular
.module('sigFig')
.directive('showEmployeesList', showEmployeesList);
function showEmployeesList(sigFigFactory) {
var directive = {
restrict: 'E',
templateUrl: 'Directives/showEmployeesList/showEmployeesList.html',
scope: '=',
require: '^parentDirective',
link: link
};
return directive;
function link(scope, element, attra, controller) {
scope.deletePerson = function(id, index, employees) {
sigFigFactory.deletePerson(id).then(function(response) {
employees.splice(index, 1);
return response;
})
};
scope.editPerson = function(personId, index1, index2) {
scope.person = {
name: scope.nameEdit,
email: scope.emailEdit
};
console.log('person ', scope.person);
};
}
}
})();
I'm thinking it's some sort of scoping issue that I just don't see, and hoping someone can help. When I console.log that person object I get undefined for both properties.
it's good idea to use angular directive, and also you need to read more about it:
you just define scope as variable but it's object, and there isn't scope.nameEdit to console
app.directive("name", function() {
return {
templateUrl: "your.html", //it's string
restrict: 'E',
scope: { //it's object
param1: "=" //var
param2: "#" //string
param3: "&" //method and etc
},
link: function(scope){ //it's function
//scope.param1
//scope.param2
//scope.param3
}
}
})
<name param1="{foo: 'test'}" param2="hello" param3="callback"></name>
with directive you can pass everything from your basic view (controller) to the directive, you can $watch value on change in your controller and more options.

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>',

Angular, share directive template between click functions

I have a directive which, when called, passes in a controller and an array.
In the controller I pass in, there is an object I want to loop over.
my html looks like this:
<div class="row">
<div class="col-md-6">
<div class="jumbotron" ng-controller="protocolCtrl as pctrl">
<button type="button" id="protocol" class="btn btn-primary btn-lg" ng-click="pctrl.getUpdatedList()"
data-toggle="modal" data-target="#modal">Modify Current Protocols</button>
<!--IN THIS MODAL YOU CAN ADD/CHANGE/DELETE DATA-->
<modal-directive list="pctrl" headers="['ID', 'Protocol']"></modal-directive>
</div>
</div>
<div class="col-md-6">
<div class="jumbotron" ng-controller="categoryCtrl as cctrl">
<button type="button" id="category" class="btn btn-primary btn-lg" ng-click="cctrl.getUpdatedList()"
data-toggle="modal" data-target="#modal">Modify Current Categories</button>
<!--IN THIS MODAL YOU CAN ADD/CHANGE/DELETE DATA-->
<modal-directive list="cctrl" headers="['ID', 'Category']"></modal-directive>
</div>
</div>
</div>
My problem is that no matter what I do, it's always the FIRST directive in the html that showes up, no matter what button I press.
My directive looks like this:
.directive('modalDirective', function(){
return {
restrict: 'E',
templateUrl: '/directives/modal-directive.html',
scope: {
list: '=',
headers: '='
},
link: function(scope, element, attrs){
console.log(attrs.list + ' | ' + attrs.headers);
}
};
});
My modal-directive.html looks like this:
<table class="table table-striped">
<thead>
<tr>
<th ng-repeat="h in headers"> {{ h }} </th>
</tr>
</thead>
<tbody>
<!-- Loop through -->
<tr ng-repeat="l in list.list">
<!--Access the actual values inside each of the objects in the array-->
<td ng-repeat="data in l"> {{ data }} </td>
<td>
<button type="button" class="btn btn-primary btn-sm"
data-toggle="modal">Edit</button>
</td>
<td>
<button type="button" class="btn btn-danger btn-sm" ng-click="list.removeData(l)"
data-dismiss="modal">Remove</button>
</td>
</tr>
</tbody>
</table>
Am I using isolated scopes wrong, or is it something else I need to change in order to make this work?
Update
Here is a fiddle, that demonstrates the problem.
No matter which button i click, it displays the same text in the modal body.
You don't really need two controllers and two directives to achieve this. Below is an example of how you can do this. Notice I moved the controller to the row instead of having separate controllers for each column. The controller myCtrl now handles the click functions which are bound to the buttons using the ng-click attribute. This then determines the which text should be placed where by calling there respective functions. IE proto() and cat()
Now this may not be ideal for your situation depending on how you plan on the architecture of your application. But it works for your current problem in terms of what you have provided.
HTML
<body ng-app="TM">
<div class="row" ng-controller="myCtrl as modalControl">
<div class="col-md-6">
<div class="jumbotron" >
<button
ng-click='proto()'
type="button" id="protocol"
class="btn btn-primary btn-lg"
data-toggle="modal"
data-target="#modal">Modify Current Protocols
</button>
</div>
</div>
<div class="col-md-6">
<div class="jumbotron">
<button
ng-click='cat()'
type="button"
id="category"
class="btn btn-primary btn-lg"
data-toggle="modal"
data-target="#modal">Modify Current Categories
</button>
</div>
</div>
<!--IN THIS MODAL YOU CAN ADD/CHANGE/DELETE DATA-->
<modal-directive ctrl="modalControl"></modal-directive>
</div>
</body>
Angular JS
angular.module('TM', [])
.controller('myCtrl', function($scope){
$scope.text ='default';
$scope.proto = function() {
this.text = 'Now looking at the protocol part'
}
$scope.cat = function() {
this.text = 'Now looking at the category part'
}
})
.directive('modalDirective', function(){
return {
restrict: 'E',
scope: true,
template: ['<div id="modal" class="modal fade" role="dialog">',
'<div class="modal-dialog">',
'<div class="modal-content">',
'<div class="modal-header">',
'<h4 class="modal-title">Modal Header</h4>',
'</div>',
'<div class="modal-body">',
'<p> {{ text }} </p>',
'</div>',
'<div class="modal-footer">',
'<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>',
'</div>',
'</div>',
'</div>',
'</div>'].join('')
}
});
Demo:
https://jsfiddle.net/DTcHh/10193/
UPDATE:
Okay, I took another look. And even though the above example works. I noticed that I have a few extra things that I didn't necessarily need. For example myCtrl as modalControl doesn't need the as modalControl part. Below is an updated example. I did this one with some different simplified markup.
HTML:
<body ng-app="TestApp">
<div ng-controller="myCtrl">
<button ng-click="one()">One</button>
<button ng-click="two()">Two</button>
<test-directive></test-directive>
</div>
</body>
Angular Example (without Isolated Scope)
angular.module('TestApp', [])
.controller('myCtrl', function($scope){
$scope.text ='default';
$scope.one = function() {
this.text = 'this is one'
}
$scope.two = function() {
this.text = 'this is two'
}
})
.directive('testDirective', function(){
return {
template: "<div id='test'>{{text}}</div>"
}
});
Demo 2:
https://jsfiddle.net/krishollenbeck/v8tczaea/12/
Note this..
restrict: 'E',
scope: true
Was also not needed because I am not using Isolated scope in this example. More info here https://docs.angularjs.org/guide/directive
Please check this JSFiddle.
The reason is that data-target value points to the DOM element id of the modal. If you fixed this id in the directive template, clicking on the button will always initiate the modal with id modal. So you need to make the modalId as another parameter of the directive.
By the way, you can pass a controller to a directive. Just like this JSFiddle:
angular.module('Joy', [])
.controller('MyCtrl', ['$scope', function ($scope) {
this.value = 'Joy';
}])
.directive('passMeContrller', [function () {
return {
restrict: 'A',
scope: {
ctrl: '=',
},
template: '<div>Value: {{ctrl.value}}</div>'
};
}]);
The HTML:
<div ng-app="Joy" ng-controller="MyCtrl as c">
<div pass-me-contrller ctrl="c"></div>
<hr>
<div ng-bind="c.value"></div>
</div>
Because the controller itself is just a JavaScript object.
Just a reminder: you are using protocolCtrl as pctrl, so you need to specify like this.list=....
If you want to pass in a function to the isolated scope, use &.
However, I suggest not to pass in the whole controller to a directive. A controller is designed to:
Set up the initial state of the $scope object.
Add behavior to the $scope object.
Controllers are not supposed to be reused. Usually there are many properties on the $scope, while some of them passed to the directive will not be used by it.

How to use the moment library in angular in a directive that uses compile instead of link

I have a tree of connected documents (parent to child) in my database from a single model called Actions, they're recursively compiled in an angular directive so that they're nested inside their parents.
I have the following code:
angular.module('crmDashboardApp')
.directive('actionDirective', function ($http, $compile, RecursionHelper) {
return {
scope: {
actionId: '=', // html:action-node, js:actionNode
actionList: '='
},
templateUrl: 'app/main/directive/action/action.directive.html',
replace: true,
restrict: 'E',
compile: function (element) {
return RecursionHelper.compile(element, function(scope, iElement, iAttrs, controller, transcludeFn){
scope.deleteAction = function (_action) {
var id = _action._id;
$http.delete('/api/actions', {
data: {'id':id},
headers: {"Content-Type": "application/json;charset=utf-8"} // we need to do this if we want to send params, otherwise we need to do traditional REST in URL
});
};
// Find for already called action list
scope.findAction = function (_id, _list) {
scope.actionNode = _.findWhere(_list, {_id:_id})
};
scope.findAction(scope.actionId, scope.actionList);
function calculateTimeSince(){
scope.fromNow = moment(scope.actionNode.content).fromNow(true);
}
setInterval(calculateTimeSince, 1000);
scope.fromNow = moment(scope.actionNode.content).fromNow(true);
});
}
};
});
This only compiles once on load and changing anything in the scope after does nothing. I want the setInterval function to change a variable scope.fromNow to be updated every second and update the view (the HTML references this with a simple {{fromNow}})
I believe I'll have to re-compile the directive somehow but doing something like:
$compile(element.contents())(scope)
within the setInterval function doesn't work.
My directive's HTML looks like this:
<div class="action-node">
<header>{{ actionNode.name }}</header>
<div class="row">
<h3 class="col-md-12">{{ actionNode.title }}</h2>
<h5 class="col-md-12">{{ actionNode.description }}</h5>
</div>
<div class="row">
<div class="col-md-3">Time Since: {{fromNow}}</div>
<div class="col-md-3">Content: {{ actionNode.content}}</div>
<div class="col-md-3">Duration Type:{{ actionNode.duration_type }}</div>
<div class="col-md-3">Type: {{ actionNode.type }}</div>
</div>
<div class="row">
<div class="col-md-4">
{{actionNode.children.length > 0 ? actionNode.children : "No children" }}
</div>
<form class="pull-right" ng-submit="deleteAction(actionNode)">
<input class="btn btn-primary" type="submit" value="Delete">
</form>
</div>
<div class="action-wrapper" ng-repeat="child in actionNode.children" ng-if="actionNode.children.length > 0">
<!-- <div class="row" ng-repeat="child in actionNode.children" ng-if="actionNode.children.length > 0" ng-style="{'margin-left': ({{actionNode.nest_level}}+1)*30+'px'}"> -->
<action-directive action-ID="child" action-list="actionList" />
</div>
</div>
You can see that it calls itself again right at the bottom. I am also using RecursionHelper so infinite loop isn't an issue.
Instead of using setInterval, you need to use the Angular wrapper service $interval.
$interval service synchronizes the view and model by internally calling $scope.$apply which executes a digest cycle.

Controller Function is not getting called on ng-click

This is my View Code
<div ng-controller="signupCtrl">
<ul class="list-group" >
<li class="list-group-item">
<div class="form-group">
<input type="text" ng-model="signupCtrl.firstName">
</div>
...
</div>
<div class="form-group">
<div class="pull-right">
<button ng-click="signupCtrl.signupUser()">Register</button>
</div>
</div>
</li>
</ul>
</div>
Update- This is my Controller Code ##
someAppControllers.controller('signupCtrl', [
'$window',
'$scope',
'HttpReqHandlerService',
'$location',
'localStorageService'],
function($window, $scope, HttpReqHandlerService,
$location, localStorageService) {
$scope.signupUser=function signupUser() {
alert("hello");
}]);
The button is not calling signupUser function in my controller
Use $scope.signupUser instead of this.signupUser
Change you code as
someAppControllers.controller('signupCtrl', ['$window', '$scope',
function ($window, $scope) { // Here you have to define function an pass window and scope
$scope.signupUser = function signupUser() {
alert("hello");
};
}
]);
Additionally, You have syntax error.
HTML
Instead of
<input type="text" ng-model="signupCtrl.firstName">
<button ng-click="signupCtrl.signupUser()">Register</button>
Use
<input type="text" ng-model="firstName">
<button ng-click="signupUser()">Register</button>
You've written your markup as though you used the controller as syntax. To make it work just change your ng-controller="signupCtrl" to ng-controller="signupCtrl as signupCtrl";

Categories

Resources