accessing other scope variables inside custom template - javascript

I'm trying to display files inside each folder/directory.
I'm using custom directive to disply each of the directories as follows (this part works).
But it fails to resolve the {{file}} variable inside custom template folderListing.html. Can somebody please correct me where I'm going wrong ?
folderListing.js
app.directive('folderListing', function(){
return {
restrict: 'E',
scope: {
listing:'='
},
templateUrl: 'js/directives/folderListing.html'
};
});
RdaController.js
app.controller('RdaController', ['$scope','RdaService', function($scope,RdaService) {
$scope.folders = ['RDA Server Folder','CTP Outbox', 'CTP Inbox', 'CTP Jobs'];
$scope.sendToCTP = function(file){
return RdaService.SendFileToCTP(file);
};
$scope.listOfFiles = ['learn_javascript.pdf', 'HTML Made Easy.pdf', 'AngularJS for everybody.pdf'];
}]);
index.html
<folder-listing listing="folder" ng-repeat="folder in folders"></folder-listing>
folderListing.html
<div class="row">
<div class="col-md-3" id="{{listing}}">
<table class="table table-striped">
<h3> {{ listing }} </h3>
<div class="row">
<div class="col-md-3" ng-repeat="file in listOfFiles">
{{file}}
</div>
</div>
<td><span class="btn-group" role="group"><button type="button" class="btn btn-default" ng-click="sendToCTP(file)">Execute</button></span></td>
</table>
</div>
</div>

With this:
scope: {
listing:'='
},
You have created an isolate scope passing only listing to the directive. You need to change this to:
scope: {
listing: '=',
listOfFiles: '=',
sendToCTP: '&sendToCtp'
},
To pass the function you'll have to add a send-to-ctp="sendToCTP(file)" attribute on your directive. However, in your template your button with ng-click="sendToCTP(file)" is outside your ng-repeat so file will always be undefined.

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.

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.

check value of isolated scope variable in link function - directives in angular

I have the following html:
<div class="jumbotron" ng-controller="protocolCtrl as pctrl">
<!--IN THIS MODAL YOU CAN ADD/CHANGE/DELETE DATA-->
<modal-directive list="pctrl" headers="['ID', 'Protocol']"></modal-directive>
</div>
In my modal-directive.html, in the body, I do this:
<!-- Modal body-->
<div class="modal-body">
<table-directive list=list headers=headers></table-directive>
</div>
I want to check on the list parameter i pass in. if it's equal some value, I want to append some html to the body
My directive looks like this
.directive('modalDirective', function(){
return {
restrict: 'E',
templateUrl: '/directives/modal-directive.html',
scope: {
list: '=',
headers: '='
},
link: function(scope, element, attrs){
if(scope.list == 'pctrl'){
element.find('.modal-body').append('This is just a test.')
}
}
};
});
But this doesn't append anything. If I drop the if check it appends just fine.
I'm fairly new to angular, so if anyone can tell me how I can achieve this, I'd appreciated it.
Edit
this is how i loop through the data in my table-directive.html
<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>
if you put
<modal-directive list="pctrl" headers="['ID', 'Protocol']"></modal-directive>
and
....
scope: {
list: '=',
headers: '='
},
.....
list: '=' check for the list attr of the element and execute the argument as a expression not as a string i think your trying to get 'pctrl' as a string not as a scope variable value so that change to list="'pctrl'" to pass as a string
<modal-directive list="'pctrl'" headers="['ID', 'Protocol']"></modal-directive>
OR
get the attr as a string use #
....
scope: {
list: '#',
headers: '='
},
.....
here is a good Explanation.
here is the angular official DOC
update
if you need to check only the string value of the attr, then you can simply use attrs.list
so use it inside the directive as
if(attrs.list === 'pctrl'){
element.find('.modal-body').append('This is just a test.')
}

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.

AngularJS : ng-repeat inside a directive masks the directive's controller

I have an isolated scope directive that has its own controller.
The directive's template has access to that controller, except inside an ng-repeat where the entire controller is out of scope.
I don't know how to fix it.
If you have an ng-repeat inside an ng-controller, children of the ng-repeat inherit the functions of the ng-controller.
I would have thought that would be the case with custom directives, too - children inside the ng-repeat could still see the directive's controller - but as far as I can tell, they can't.
I'm still getting up to speed with Angular, so a nudge in the right direction would be appreciated. A downright answer would be appreciated even more. Something to do with transclusion? With which is getting compiled first?
Here is the directive:
angular.module('surverApp')
.directive('surveyItems', function () {
return {
restrict: 'E',
scope: {
itemList: '=',
query: '='
},
templateUrl: 'views/directives/survey-items.html',
replace: true,
controller: 'SurveyItemsCtrl',
controllerAs: 'ctrl',
bindToController: true
};
})
.controller('SurveyItemsCtrl', function(){
var ctrl = this;
ctrl.disableEdit = true;
ctrl.disableToolbar = false;
ctrl.showForm = false;
ctrl.showDuplicate = false;
ctrl.showTrash = false;
ctrl.deleteItem = function(item) {
console.log('ctrl.item in DELETE ITEM' ,item)
.... functions removed for brevity
};
});
And here is its template. None of its functions fire.
<div>
<pre>ctrl = {{ctrl | json}}</pre> <<<<<=== THIS SHOWS THE CONTROLLER IS IN SCOPE
<div ng-repeat="item in ctrl.itemList | filter:ctrl.query" class="ubi-box container-fluid">
<pre>ctrl = {{ctrl | json}}</pre> <<<<<=== THIS SHOWS THE CONTROLLER IS NOT IN SCOPE
<standard-right-toolbar ctrl="ctrl"></standard-right-toolbar>
<h4>{{item.name}}</h4>
<div ng-show="ctrl.showForm" class="ubi-box container-fluid">
<!-- <survey-form model="item" disable-edit="ctrl.disableEdit" reset-fn="item = ctrl.resetUpdateFn(item)" submit-fn="ctrl.submitUpdateFn()" close-fn="ctrl.hideFormFn()"></survey-form> -->
<survey-form model-el="item" disable-edit-el="ctrl.disableEdit" reset-fn="ctrl.resetUpdateFn(form,model)" submit-fn="ctrl.submitUpdateFn(form)" close-fn="ctrl.hideFormFn()"></survey-form>
<pre>model = {{item | json}}</pre>
</div>
<div ng-show="ctrl.showDuplicate" class="ubi-box container-fluid">
<standard-right-close-bar close-fn="ctrl.hideDuplicateFn()"></standard-right-close-bar>
<h4 class="col-xs-12">Duplicating a survey will copy all the details and questions over to a new survey.</h4>
<h3 class="col-xs-10">Click the copy button to procede.</h3>
<button class="btn btn-lg btn-primary" type="button" ng-click="ctrl.copy(item)" title="Duplicate">
<span class="glyphicon glyphicon-duplicate"></span>
</button>
</div>
<!-- <div ng-show="ctrl.showTrash" class="ubi-box container-fluid"> -->
<div ng-show="ctrl.showTrash" class="ubi-box container-fluid">
<standard-right-close-bar close-fn="ctrl.hideTrashFn()"></standard-right-close-bar>
<h4 class="col-xs-12">Deleting a survey is a very serious matter. It will permanently remove every question and every answer in every questionnaire in every edition in the survey.</h4>
<h3 class="col-xs-10">Click the trashcan only if you are sure!</h3>
<button class="btn btn-lg btn-danger" type="button" ng-click="ctrl.deleteItem(item)" title="Delete">
<span class="glyphicon glyphicon-trash"></span>
</button>
</div>
</div>
<div>
PS I had it working fine inside an ng-controller. But then I read ng-controllers are being outlawed in Angular 2.0, so I thought I'd get in some practice using directive controllers rather than ng-controllers and I wrapped the ng-repeat in a directive instead of an ng-controller.
So far, so stumped . . .

Categories

Resources