AngularJs - Directive - Elements are with the curly braces - angularjs

I have created the directive with the isolated scope.
In view, I am using controller as property.
When I call the directive with the ng repeat and the isolated scope, the elements are with the curly braces. Not updating the element with the model value.
When I debug the directive, I am getting the same curly braces.
<ul id="ul-data-repeat" my-directive>
<li title='test'>
<span>{{homeCtrl.ngRepeatDatasource.name}}</span>
</li>
<li ng-repeat='item in homeCtrl.ngRepeatDatasource.values'>
{{item.name}}
</li>
</ul>
Just for the sample I have added this code. This is the logic.
In my actual scenario, I will call the jquery plugin.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://code.angularjs.org/1.5.8/angular.js"></script>
<script>
angular.module('myApp', [])
.controller('homeController',function($scope){
var vm=this;
vm.data={
name:'Name'
, values:[{val:'values1'},{val:'values2'}]
}
}).directive('myDirective',['$injector',function($injector){
return{
scope: {
data:'=data'
},
link: function ($scope, $element, $attributes) {
debugger
$($element).html($($attributes.myDirective).html());
}
}
}]);
</script>
<div ng-app='myApp'>
<div ng-controller='homeController as homeCtrl'>
<div>
<h1>Without directive</h1>
<div id='data'>
{{homeCtrl.data.name}}
<div ng-repeat='item in homeCtrl.data.values'>
{{item.val}}
</div>
</div>
</div>
<div>
<h1>With directive</h1>
<div my-directive='#data'>
</div>
</div>
</div>
</div>
how to get the updated value?

If you are using scope you can send your data to directive. And you want to use your data without braces you need to compile it with your current scope. This means you need to send your scope to directive again.
1. Data Send
<div my-directive='item'>
</div>
link: function ($scope, $element, $attributes) {
console.log($scope);
}
2. Compile data
<div my-directive='item'>
</div>
link: function ($scope, $element, $attributes) {
$compile($($attributes.myDirective).html())($scope);
}
3. Bind data before run directive
<div ng-bind="item.val" id="#data" myDirective="#data">
</div>
data:'#',
link: function ($scope, $element, $attributes) {
$($element).html($($attributes.myDirective).html());
}

Related

Unable to get content AngularJs dynamic popover inside ngrepeat

I need to display a small popover which should open on click and goaway on clicking anywhere on the page.
I found a plunker (http://plnkr.co/edit/K7cYQSDEBS3cHvDfJNLI?p=preview) which matches this requirement however, unable to get it to work inside ng-repeat.
I saw several answers and Plunker examples but not able to get this to work.
Here is my html
<div ng-controller="TestController">
<div class="row" style="background-color: #ebebeb !Important; ">
<div style="text-align:center">
<table style="width:100% !important;">
<tr ng-repeat="member in TeamMembers" style="font-size:18px !important; height: 108px;">
<td style="display:block;margin-top:30px;text-align:left;"> {{member.FirstName}} {{member.LastName}} <i class="fa fa-info-circle" aria-hidden="true" ng-show="member.Description != null" popover-template="dynamicPopover.templateUrl" popover-placement="bottom" popover-elem descr="{{member.Description}}"></i></td>
</tr>
</table>
</div>
</div>
...
<script type="text/ng-template" id="descriptionModal.html">
<div class="adp-info-dialog">
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-1">
<div class="form-group">
<label class="fieldset-label">Test {{ dynamicPopover.descr }}</label>
</div>
</div>
</div>
</div>
</div>
</script>
Here is the JS
testApp.controller('TestController', function ($scope, $rootScope, $log, $modal, SiebelAccountTeamService, $filter, $element) {
$scope.dynamicPopover = {
templateUrl: 'descriptionModal.html',
descr: null
};
var result = TestService.GetTeamMembers();
result.then(function (data) {
$scope.TeamMembers = data.data;
}, function (e) {
console.log(e);
}).finally(function () {
$scope.CompleteLoading();
});
});
testApp.directive('popoverClose', function ($timeout) {
return {
scope: {
excludeClass: '#'
},
link: function (scope, element, attrs) {
var trigger = document.getElementsByClassName('trigger');
function closeTrigger(i) {
$timeout(function () {
angular.element(trigger[0]).triggerHandler('click').removeClass('trigger');
});
}
element.on('click', function (event) {
var etarget = angular.element(event.target);
var tlength = trigger.length;
if (!etarget.hasClass('trigger') && !etarget.hasClass(scope.excludeClass)) {
for (var i = 0; i < tlength; i++) {
closeTrigger(i)
}
}
});
}
};
});
testApp.directive('popoverElem', function () {
return {
scope: {
descr: '#'
},
link: function (scope, element, attrs) {
$scope.dynamicPopover.descr = scope.descr,
alert($scope.dynamicPopover.descr),
element.on('click', function () {
element.addClass('trigger');
});
}
};
});
Appreciate your help.
Update:
To show the data of the ng-repeat inside the popover content, we need to access the individual objects through the $index of the ng-repeat. Refer the below example.
Plunkr Demo
The problem here is that you are using ng-repeat which creates a new scope read more here.
Since replicating the issue with your code is tedious, I tried replicating the issue with the plunkr!
Solution:
Plunkr Demo
You can simply define a new controller inside the descriptionModal.html like so
HTML:
<script type="text/ng-template" id="myPopoverTemplate.html">
<div class="adp-info-dialog" ng-controller="tester">
<div class="modal-body">
<div class="row">
<div class="col-md-8 col-md-offset-1">
<div class="form-group">
<label class="fieldset-label">Test {{ $parent.$parent.dynamicPopover.content }}</label>
</div>
</div>
</div>
</div>
</div>
</script>
JS:
app.controller('tester', function ($rootScope, $scope) {
console.log($scope.$parent.$parent.dynamicPopover.title);
});
Then, we will be able to access the parent scope, using $parent, the html inside the above script uses the $parent to get the variable!
Please note: It took me two $parent to reach the required $scope to access the scope variable. In your scenario it will also require two, the way to check how many is needed is use console.log($scope), then open the console(F12), then traverse through the objects $parent property till you find the correct $scope. Then count the number of $parent traversed, that will be your required number of $parent to traverse!
P.S:
There is another method you can do this, since this method will require a significant rewrite of your code, I will provide the GIST, you can use the controller as syntax and access the correct scope.
Here is the SO Answer giving the method to do it
SO Answer
I hope this fixes you issue.

How to update a model of directive 1 from directive 2 and see changes on view?

I have two directives and i want to update model (this in controllerAs syntax) in directive 1.
// Linkbox
SApp.directive('linkBox', ['$rootScope', '$compile', '$templateCache', '$state', '$timeout', function($rootScope, $compile, $templateCache, $state, $timeout){
return {
controller: function($scope, $element, $attrs, $transclude) {
var vm = this;
vm.setBoxTitle = function(title){
vm.boxTitle = title;
console.log('setBoxTitle', title); // returns correct value!
// also tried this:
// $timeout(function(){
// vm.boxTitle = title;
// console.log('title of box', vm.boxTitle);
// }, 1);
}
},
restrict: 'A',
templateUrl: 'linkbox.html',
controllerAs: 'lbCtrl',
link: function($scope, iElm, iAttrs, controller) {
console.log('link');
}
};
}]);
// SubCategories linkbox
SApp.directive('subCategories', ['$rootScope', '$compile', '$templateCache', '$state', function($rootScope, $compile, $templateCache, $state){
return {
require: 'linkBox',
restrict: 'A',
link: function($scope, iElm, iAttrs, lbCtrl) {
lbCtrl.setBoxTitle('Title of box...');
}
};
}]);
Using directives:
<div link-box sub-categories ></div>
This is the view (linkbox.html)
<div class="linkbox">
<h3 class="title" ng-bind="lbCtrl.boxTitle"></h3>
<ul> <li> <a>...</a> </li> </ul>
</div>
Communication of directives is ok and console.log returns correct value, But I see no change in view. Can you help please?
Update:
When i manually set controller.boxTitle = 'A title' in link function of linkBox directive, View has updated value. So there is no problem in html code.
Problem solved.
There was another usage of link-box directive like this:
<div link-box sub-categories ></div>
<div class="area" link-box related-searchs ></div>
In second use of link-box, I just added another directive without setting title. I should use isolate scope to prevent colision between models of different modules.
As per code in the jsbin , ng-template has been used , if this is the case then you have provide an id or class for the template as below id="myTpl", and include that using ng-include directive
<script type="text/ng-template" id="myTpl">
<div class="box linkbox" >
<div class="inr">
<div class="content">
<h3 class="title" ng-bind="lbCtrl.boxTitle"></h3>
<ul>
<li><a>link 1</a></li>
<li><a>link 2</a></li>
</ul>
</div>
</div>
</div>
</script>
<div link-box sub-categories ng-include="'myTpl'"></div>
http://jsbin.com/baneyezezu/1/edit?html,js,output

In AngularJS, how do I generate a piece of content from template and insert it in the DOM when a link is clicked

I have a number of links on the page, dynamically generated like so:
<a href="#" class="more-info-item" ng-click="showStats(item, $event)">
More information
</a>
I also have a simple custom template that should show an item's name:
<script type="text/ng-template" id="iteminfo.html">
<div class="item-name">
{{item.name}}
</div>
</script>
What I would like to do is: when the link is clicked, to dynamically compile the template, and insert it in the DOM right after the link. I tried using $compile within showStats method to compile the template, but I got an error that $compile wasn't found. How would I go about doing this (and also provide item as part of the scope for the newly generated template)?
Here is a solution using a custom directive which injects the item dynamically using ng-if:
View Solution with Plunker
html:
<script type="text/ng-template" id="iteminfo.html">
<div class="item-name" ng-if="item.visible">
{{item.name}}
</div>
</script>
<div ng-repeat="item in items" >
<a href="#" class="more-info-item" more-info="item" ng-click="item.visible =!item.visible">
More information
</a>
</div>
script:
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.items = [{name:'apples', visible:false},{name:'pears', visible:false},{name:'oranges', visible:false}];
})
.directive('moreInfo', function($compile,$templateCache) {
return {
restrict: 'A',
scope: '=',
link: function(scope, element, attr) {
var itemInfo = angular.element($templateCache.get('iteminfo.html'));
var lfn = $compile(itemInfo);
element.parent().append(itemInfo);
lfn(scope);
}
};
});
You can use the built-in ngInclude directive in AngularJS
Try this out
Working Demo
html
<div ng-controller="Ctrl">
<a href="#" class="more-info-item" ng-click="showStats(item, $event)">
More information
</a>
<ng-include src="template"></ng-include>
<!-- iteminfo.html -->
<script type="text/ng-template" id="iteminfo.html">
<div class="item-name">
{{item.name}}
</div>
</script>
</div>
script
function Ctrl($scope) {
$scope.flag = false;
$scope.item = {
name: 'Manu'
};
$scope.showStats = function (item, event) {
$scope.item = item;
$scope.template = "iteminfo.html";
}
}

Using ng-switch in directive with transclude

I am trying to create a template that shows some transcluded content. When I use ng-show everything works fine, but using ng-if or ng-switch gives me problems. I get this error message: Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found
I understand that ng-switch creates a new scope. But the transclude should still go up to the parent chain. Is this a defect in angularjs? See http://jsfiddle.net/HgvP7/
Here is my html, modified from the documentation example:
<div ng-app="docsTransclusionExample">
<div ng-controller="Ctrl">
<my-dialog>Check out the contents, {{name}}!</my-dialog>
</div>
<!-- my-dialog.html -->
<script type="text/ng-template" id="my-dialog.html">
<div ng-switch="1+1">
<div ng-switch-when="2">
<div ng-transclude></div>
</div>
</div>
</script>
</div>
And the code:
angular.module('docsTransclusionExample', [])
.controller('Ctrl', function($scope) {
$scope.name = 'Tobias';
})
.directive('myDialog', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
templateUrl: 'my-dialog.html',
link: function (scope, element) {
scope.name = 'Jeff';
}
};
});

Angular Bootstrap Prettyprint directive not being invoked

Does anyone have experience using the angular-bootstrap-prettify.js prettyprint directive? I am having problems geting this to work.
Here is my Example: http://jsfiddle.net/mjxNV/
<div ng-app>
<div ng-controller="Ctrl1">
<pre class="prettyprint linenums">
<code class="lang-html">
<div class="container">
<div class="left_column">
<span>Small Text</span>
</div>
<div class="r_ightcolumn2">
<span>Small Text</span>
</div>
</div>
</code>
</pre>
</div>
</div>
My code is displayed but it never goes through the prettyprint tokenization. What am I doing wrong?
I found a sample and modify as your sample:
angular.module('app', [])
.controller('ctrl', function($scope) { });
ap.directive('prettyprint', function() {
return {
restrict: 'C',
link: function postLink(scope, element, attrs) {
element.html(prettyPrintOne(element.html(),'',true));
}
};
});
http://jsfiddle.net/yAv4f/210/

Resources