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

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.')
}

Related

accessing other scope variables inside custom template

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.

Populate angular ui bootstrap popover

How do you populate an angular ui bootstrap popover? I want to populate the popover with a list of actor names. Here is the code:
<div class="container" ng-if="radioModel=='Search for Actor'">
<p>Movies played in:</p>
<table class="table table-bordered">
<tr ng-repeat="name in listOfMovies">
<td>
<p>
<button uib-popover="populateWithListOfData" popover-trigger="mouseenter" type="button" class="btn btn-default"> {{ name }}</button>
</p>
</td>
</tr>
</table>
</div>
I want to be able to populate this popover for each name of the ng-repeat. So I will get a name of a movie and based on that name I want to populate the popover with a list of actors in that movie. Thanks guys.
This is definitely possible.
I have setup a data item called friends in JS
$scope.friends = [
{name:'John'},
{name:'Jessie'},
{name:'Johanna'},
{name:'Joy'}
];
Also , an array was created for the text in the popup
$scope.toolTip =['D','A','C','T'];
If you want to display a pop up for each row.
I've created the ng-repeat and the relevant popover.In order to display all the letters in the first popover.
<div ng-repeat="f in friends">
{{f.name}}
<button uib-tooltip="{{toolTip[$index]}}" type="button" class="btn btn-default">Tooltip {{placement.selected}}</button>
</div>
Here is a working demo
How does it works?
Your tool tip value is set as uib-tooltip="{{toolTip[$index]}}".it accesses each element according to the $index obtained from ng-repeat.
If you want to display all the data in the first pop up
I've created the ng-repeat and the relevant popover.In order to display all the letters in the first popover.
<div ng-repeat="f in friends">
<div ng-if="$index==0">
<button uib-tooltip="{{toolTip}}" type="button" class="btn btn-default">Tooltip {{placement.selected}}</button>
</div>
{{f.name}}
</div>
Here is a working demo
How does it works?
Your tool tip value is set as uib-tooltip="{{toolTip}}".It enters the ng-if , if the condition is met, and thus prints the array.
I couldn't test if it works, but this might give you the idea.
(function ()
{
function moviePopover($compile)
{
return {
restrict: "EA",
scope: true,
templateUrl: '<button uib-popover-template="dynamicPopover.templateUrl" popover-title="{{dynamicPopover.title}}" type="button" class="btn btn-default">Popover With Template</button>',
link: function (scope, element, attrs)
{
scope.dynamicPopover = {
content: "Movies",
templateUrl: "myPopoverTemplate.html",
title: "Title"
};
if (attrs.movieName !== undefined)
{
scope.movieList = getMoviesByName(attrs.movieName);
$compile(element)(scope);
//If 1st leads to infinit loop use this
// $compile(element.contents())(scope);
}
function getMoviesByName(movieName)
{
//return all moviews based on movieName
//here im just returning dummy array(you return your data)
return ["Movie1", "Movie2"];
}
}
}
}
angular.module("myApp").directive("moviePopover", ["$compile", moviePopover]);
}());
<script type="text/ng-template" id="myPopoverTemplate.html">
<ul>
<li ng-repeat="movie in movieList">{{movie}}</li>
</ul>
</script>
<div class="container" ng-if="radioModel=='Search for Actor'">
<p>Movies played in:</p>
<table class="table table-bordered">
<tr ng-repeat="name in listOfMovies">
<td>
<p>
<movie-popover movie-name="{{name}}"></movie-popover>
</p>
</td>
</tr>
</table>
</div>

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.

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 ?

Angular Directive, Scope: True AND Add attribute to scope

my directive is working fine, displaying all the trending tags. The directive looks for a trendingTag property in the $scope object. So I have scope: true
app.directive('ngTrending', function () {
return {
restrict: 'E'
, transclude: true
, replace: true
, scope: true
, templateUrl: '/resources/ngViews/trending.html'
};
});
Now I want to be able to configure it based on options (like read-only="true") based on attribute on the directive. And be able to conditionally change minor aspects of the template based on the attirbute such tht
<ng-trending></ng-trending>
Generates the trending tags with the actions enabled. While
<ng-trending read-only="true"></ng-trending>
generates the tags, but has the clicks disabled. How do I code the scope on the directive so that I still inherit the scope of the controller that is hosting the directive e.g.
<div ng-controller="fancy">
<ng-trending></ng-trending>
</div>
as is the case now (inside of the template of the directive I reference the fancyContrllers $scope.trendingTags property). But inside of the directive's template I want to reference the "read-only" in the $scope.
It just dawns on me that I am approaching this completely wrong, and that I probably want to pass the trending tags in as well... I am confused - please help straighten me out!
Thanks.
The normal procedure is to use an isolate scope to pass in any variables you want in directive (unless you need multiple directives on the element). This will result in a more reusable and testable directive, and clearer code as the directive won't be coupled to its surroundings.
For your case, if you write an isolate scope like this
scope: {
trendingTags: '=',
readOnly: '='
// ...
}
Then you can bind an expression on the outer scope to trendingTags on the inner scope, and the same with readOnly using attributes on the element.
You element would then look something like this
<ng-trending trending-tags="trendingTags" read-only="true"></ng-trending>
There is some more information on isolate scope here http://docs.angularjs.org/guide/directive.
For completeness here is my working solution, including bindings for the actions. Any critiques are welcomed. Thank you andyrooger:
The directive:
app.directive('ngTrending', function () {
return {
restrict: 'E'
, transclude: true
, replace: true
, scope: {
trendingTags: '='
, displayOnly: '='
, inlineLabel: '='
, filterTo: '&'
, isFilteredTo: '&'
}
, templateUrl: '/resources/ngViews/trending.html'
};
});
The template:
<div style="text-align: center">
<div class="btn-group-xs">
<span ng-show="(!!inlineLabel)" style="color: #81B1D9">Tagged: </span>
<button ng-repeat="tag in trendingTags | orderBy:'count':true | limitTo:8" type="button" class="btn btn-default btn-primary"
style="text-wrap: normal; margin: 2px;"
ng-click="filterTo({filterToTag: tag.tagName})" ng-class="{active: isFilteredTo({filterToTag: tag.tagName}), disabled: (!!inlineLabel)}"><span
ng-bind-html-unsafe="tag.tagName"></span> <span class="badge" ng-show="!(!!displayOnly)">{{ tag.count }}</span
</button>
</div>
<button type="button" class="btn btn-xs btn-default" style="width: 150px; text-wrap: normal; margin-top: 3px"
ng-click="filterTo({filterToTag: ''})" ng-show="!(!!displayOnly)">Clear
</button>
</div>
In use:
<ng-trending trending-tags="tags"
filter-to="filterTo(filterToTag)"
is-filtered-to="isFilteredTo(filterToTag)"></ng-trending>

Categories

Resources