Unable to access the dom element in angular directive template - angularjs

For some reason I'm unable to access the dom element within the directive template. I'm sure there is something very obvious here.
var slider = angular.module('imageSlider', []);
slider.directive('slider', function($timeout) {
return {
restrict : 'AE',
replace : true,
scope : {
images : '='
},
link : function(scope, ele, attrs) {
scope.currentIndex = 0;
scope.next = function() {
scope.currentIndex < scope.images.length - 1 ? scope.currentIndex++ : scope.currentIndex = 0;
};
scope.prev = function() {
scope.currentIndex > 0 ? scope.currentIndex-- : scope.currentIndex = scope.images.length - 1;
};
console.log(document.getElementById('sliderimage'));
console.log(document.getElementById("id"));
console.log(ele.find("li"));
,
template : 'directive/imageSlider/imageSliderTemplate.html'
}
});
Template code :
<secton class="gallery">
<div class="gallery-content row fl no-fixed">
<img ng-src="{{images[currentIndex].src}}" alt="{{images[currentIndex].src}}" class="img-responsive">
<button ng-click="prev()" class="ico ico-arr-left btn"></button>
<button ng-click="next()" class="ico ico-arr-right btn"></button>
<div class="gallery-info row">
<h3 class="fl">{{images[currentIndex].title}}</h3>
<span class="gallery-number fr fs-md">{{currentIndex + 1}}/{{images.length}}</span>
</div>
</div>
<div class="gallery-thumbs hidden-md row">
<button class="btn ico ico-arr-left" title="Click to see previous image"></button>
<button class="btn ico ico-arr-right" title="Click to see next image"></button>
<div class="gallery-thumbs-scroll">
<ul>
<li id="sliderimage" ng-repeat="image in images"><img ng-src="{{image.src}}" alt="{{image.alt == 'undefined' ? image.title : image.alt}}"></li>
</ul>
</div>
</div>

I found couple of problems with above code -
missing "}" before ", template:.. "
you have separated template in the different file, so you should be using "templateurl" instead of "template"
Template content should have only single root element when you are using replace = true in directive. click here for more information.
Please refer below code snippet -
var slider = angular.module('imageSlider', []);
slider.controller('myController',function($scope){
$scope.i = [{title:'Test'},{title:'Demo'},{title:'Okay!'}];
});
slider.directive('slider', function($timeout) {
return {
restrict : 'AE',
scope : {
images : '='
},
replace: true,
template : '<div ng-click="next()" id="sliderimage">{{images[currentIndex].title}} {{currentIndex + 1}}/{{images.length}}</div>',
link : function(scope, ele, attrs) {
scope.currentIndex = 0;
scope.next = function() {
scope.currentIndex < scope.images.length - 1 ? scope.currentIndex++ : scope.currentIndex = 0;
};
scope.prev = function() {
scope.currentIndex > 0 ? scope.currentIndex-- : scope.currentIndex = scope.images.length - 1;
};
console.log(document.getElementById('sliderimage'));
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="imageSlider">
<div ng-controller="myController">
<slider images="i"></slider>
</div>
</body>
Hope this helps!

Related

Missing attributes from directive

I'm trying to get my menu from js directive to my html file with angularJS.
But i dont get all of the attributes like data-toggle, or data-parent or any Id. I'm missing these tags.
data-toggle="collapse"
data-parent="#accordion2"
id="collapseTwo"
Sorry if im unclear, but i've tried to explain it as good as i could.
My js
directives.directive('productTree', ['$compile','$rootScope', function($compile, $rootScope){
return {
scope: {
items: "=",
linkPath : "="
},
template: '<div ng-bind-html="tree"></div>',
replace: true,
link: function (scope) {
var makeTree = function(categories) {
var tree;
var generateNode = function(category, index) {
var children = category.Children.map(function (item, index) {
var childrens = "";
childrens += generateSubNode(item, index)
return childrens
})
var main = "";
main += "<div class=\"accordion-group\">";
main += "<div class=\"accordion-heading\">";
main += "<a class=\"accordion-toggle\" data-toggle=\"collapse\" href=\"#menu-" + category.Item.Id + "\">" + category.Item.Name + "</a>";
main += "</div>";
main += (children.length ? "<div id=\"menu-" + category.Item.Id + "\" class=\"accordion-body collapse\"><div class=\"accordion-inner\">" + children.join("") + "</div></div>" : "");
main += "</div>";
return main;
}
var generateSubNode = function (category, index) {
return '' + category.Item.Name + '';
}
tree = categories.map(function(category, index){
return generateNode(category, index);
});
return tree.join("");
}
scope.tree = makeTree(scope.items);
}
}
}])
My html
<div class="accordion" product-tree items="shop.Menu" link-path="paths.linkPath">
</div>
My output
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" href="#menu-162">Genvägar</a>
</div>
<div class="accordion-body collapse">
<div class="accordion-inner">
Black Friday 2016
Black Friday: iPhone 6S 64GB Prissänkt!
Blåtandshörlurar
iPhone 7/7 Plus
</div>
</div>
</div>
This is how i want it to be
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapseOne">
Rubrik 1
</a>
</div>
<div id="collapseOne" class="accordion-body collapse">
<div class="accordion-inner">
Black Friday 2016
Black Friday: iPhone 6S 64GB Prissänkt!
Blåtandshörlurar
iPhone 7/7 Plus
</div>
</div>
</div>
You'll save yourself a lot of trouble if you use the template functionality of angular instead of trying to concatenate your html together piece by piece.
Set your template to something like this (or load it using templateUrl:
<div class="accordion-group" ng-repeat="category in items">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" ng-click="toggle()">
{{ category.Item.Name }}
</a>
</div>
<div ng-hide="collapsed" class="accordion-body collapse">
<div class="accordion-inner">
<a ng-repeat="child in category.children" href="{{linkPath}}/catalog/browse/shortcuts/{{ child.Item.URIPath }}/">{{ child.Item.Name }}</a>
</div>
</div>
</div>
Then, change your directive to something like this:
directives.directive('productTree', ['$compile','$rootScope', function($compile, $rootScope){
return {
scope: {
items: "=",
linkPath : "="
},
template: 'the template above (or use templateUrl)',
replace: true,
link: function (scope) {
scope.collapsed = true;
scope.toggle = function(){
scope.collapsed = !scope.collapsed;
};
}
}
}]);
Edit: If you're using, Bootstrap, you might also this out from the AngularUI team.

How to toggle edit and save using directive in angular js

I want to edit and save content in selected directive.The directive is populated by ng-repeat. On button click visible items should change to input field and again on click it should reverse
Directive is
.directive('component', function() {
var link = function(scope, element, attrs) {
var render = function() {
var t = scope.layouts[scope.type][attrs.indexs];
var icon = scope.layouts[scope.type][attrs.indexs];
var v = attrs.value;
if(scope.type=="edit"){
element.html('<input type="' + t + '" ng-model="vm.name" value="'+v+'">');
if(attrs.indexs==1){
element.html('<' + t + '>Save');
}}
if(scope.type=="display"){
element.html('<' + t + ' ng-model="'+v+'" >'+v+'</'+t+'>');
if(attrs.indexs==1){
element.html('<' + t + ' >Edit');
}}};
scope.$watch('type', function(newValue, oldValue) {
render();
});
};
return {
restrict : 'E',
link : link
}
});
plunker Link
Problem is on click all directive is changed to editable and vice-versa.
How can I make it work on selected set of directive
Try something like the following. It's much simpler to use a template with a directive than trying to modify the html directly.
Directive
angular.module('myApp', [])
.controller('MyController', MyController)
.directive('component', function(){
return {
template: [
'<div>',
'<span style="font-weight:bold" ng-show="!editing">{{ value }} <button ng-click="editing = true">Edit</button></span>',
'<span ng-show="editing"><input type="input" ng-model="value"><button ng-click="editing = false">Save</button></span>',
'</div>'
].join(''),
restrict: 'E',
scope: {
value: '=value'
},
link: function($scope){
$scope.editing = false;
}
}
});
HTML
<div class="col-sm-12" ng-repeat="s in vm.allCat track by $index">
<div class="col-sm-1 text-muted">Name</div>
<div class="col-sm-9 text-left">
<component value="s.name"></component>
</div>
</div>
</div>
I've forked your plunker here.

Modify DOM content after Scope Variables have been translated

I want to truncate my table cells but fit as much as possible content into them. There is an excellent solution (fiddle) which I want to implement in a directive. I need to transform a table cell from
<td>veryverylongunbreakablecontent</td>
to
<td>
<div class="container">
<div class="content">veryverylongunbreakablecontent</div>
<div class="spacer">veryv­erylon­gunbr­eakab­lecon­tent</div>
<span> </span>
</div>
</td>
This is a simplified example. My table cells consist of angular scope variables:
<td>{{ item.attribute.verylong }}</td>
What I have come up with so far is a directive
.directive('mwTableTruncate', function ($compile) {
return {
restrict: 'A',
templateUrl: 'modules/ui/templates/mwComponents/mwTableTruncate.html',
transclude: true,
compile: function( tElem, tAttrs ){
}
};
});
with a template
<div class="containerTest">
<div class="content">
<div ng-transclude></div>
</div>
<div class="spacer">
<div ng-transclude></div>
</div>
<span> </span>
</div>
Now I need to add soft hyphens (­) every 5 characters to the text in the spacer div. How can I do this?
The problem is I need to access the spacer div text after all the scope variables have been translated to add the soft hyphens.
edit#1 #sirrocco
I have examined the output from the compile, pre-link, link, post-link phase. None of these phases do translate the scope variables.
link phase
.directive('mwTableTruncate', function ($compile) {
return {
restrict: 'A',
link: function (scope, iElem, attrs) {
console.log('link => ' + iElem.html());
console.log('link text => ' + iElem.text());
}
};
});
gives me in the console:
link =>
{{ item.attributes.surname }}
link text =>
{{ item.attributes.surname }}
compile, pre-link, post-link
.directive('mwTableTruncate', function ($compile) {
return {
restrict: 'A',
templateUrl: 'modules/ui/templates/mwComponents/mwTableTruncate.html',
transclude: true,
compile: function( tElem, tAttrs ){
console.log('Compile => ' + tElem.html());
return {
pre: function(scope, iElem, iAttrs){
console.log('pre link => ' + iElem.html());
console.log('pre link text => ' + iElem.text());
},
post: function(scope, iElem, iAttrs){
console.log('post link => ' + iElem.html());
console.log('post link text => ' + iElem.text());
//debugger;
}
};
}
};
});
output in the console:
pre link => <div class="containerTest">
<div class="content">
<div ng-transclude=""></div>
</div>
<div class="spacer">
<div ng-transclude=""></div>
</div>
<span> </span>
</div>
pre link text =>
 
post link => <div class="containerTest">
<div class="content">
<div ng-transclude=""><span class="ng-binding ng-scope">
{{ item.attributes.surname }}
</span></div>
</div>
<div class="spacer">
<div ng-transclude=""><span class="ng-binding ng-scope">
{{ item.attributes.surname }}
</span></div>
</div>
<span> </span>
</div>
post link text =>
{{ item.attributes.surname }}
{{ item.attributes.surname }}
As you can see, none of the {{ item.attributes.surname }} variables got translated.
edit#2
Based on the hint with the timeout function in the post link phase I have come up with this solution:
directive
.directive('mwTableTruncate', function($timeout) {
return {
restrict: 'A',
templateUrl: 'modules/ui/templates/mwComponents/mwTableTruncate.html',
transclude: true,
compile: function() {
var softHyphenate = function (input) {
var newInput = '';
for (var i = 0, len = input.length; i < len; i++) {
newInput += input[i];
if (i % 5 === 0) {
newInput += '­';
}
}
return newInput;
};
return {
post: function (scope, iElem) {
$timeout(function () {
var text = iElem.find('div.spacer').text().trim();
// add tooltips
iElem.find('div.content').prop('title', text);
// add soft hyphens
var textHyphenated = softHyphenate(text);
iElem.find('div.spacer').html(textHyphenated);
});
}
};
}
};
});
template
<div class="containerTest">
<div ng-transclude class="content"></div>
<div ng-transclude class="spacer"></div>
<span> </span>
</div>
How would it look like with an isolated scope sirrocco rbaghbanli?
Do not transclude. Simply set your item.attribute.verylong as ng-model for your directive. Then get the object to manipulate as you wish. In the controller add all spacers you need. Then just display the result in {{ ... }} in your template for directive.
Code:
.directive('truncateString', function ($compile) {
return {
restrict: 'E',
templateUrl: '{{ strWithBreaks }}',
scope: {
str: '=ngModel'
},
controller: ['$scope', function ($scope) {
$scope.strWithBreaks = (function (input) {
var newInput = '';
for (var i = 0, len = input.length; i < len; i++) {
newInput += input[i];
if (i % 5 === 0) {
newInput += '­';
}
}
return newInput;
})(str);
}]
};
});
Usage:
<truncate-string ng-model="myVeryLongString"></truncate-string>
The directive without transclude would probably look something like:
.directive('mwTdTruncate', function() {
return {
restrict: 'A',
templateUrl: 'modules/ui/templates/mwComponents/mwTableTruncate.html',
scope:{
longText: '#mwLongText'
},
replace:true,
link: function(scope) {
var softHyphenate = function (input) {
var newInput = '';
for (var i = 0, len = input.length; i < len; i++) {
newInput += input[i];
if (i % 5 === 0) {
newInput += '­';
}
}
return newInput;
};
scope.softText = softHyphenate(scope.longText);
}
};
});
and the template:
<td>
<div class="container">
<div class="content">{{longText}}</div>
<div class="spacer">{{softText}}</div>
<span> </span>
</div>
</td>
used like:
<td mw-td-truncate mw-long-text='{{yourTextVariable}}'>veryverylongunbreakablecontent</td>

Find clicked div angular directive

I simply want to find the clicked div with using a directive and what I have tried:
.directive('blocks', blocks);
function blocks(){
var linkFunc;
linkFunc = function(scope,element,attrs){
element.bind('click', function(){
//assign clicked div
var selectedDiv = $(this);
if(selectedDiv.hasClass('block1')){
console.log('block1 is clicked!');
}
});
};
return{
restrict: 'E',
link: linkFunc,
template: '<div class="block block1 block_unactive">DIV 1</div>' +
'<div class="block block2 block_unactive">DIV 2</div>' +
'<div class="block block3 block_unactive">DIV 3</div>' +
'<div class="block block4 block_unactive">DIV 4</div>'
};
};
that doesn't work.. How can I assign the clicked div as selectedDiv?
For Exp: On click a div; check the click div and if the clicked div hasClass 'block1' do block1 actions.. if the div hasClass 'block2' do block2 actions... etc..
Thank you in advance!
i've another way to handle issue like that, passing div number to a service which will apply the desired action.
we have three files
the view index.html
<section ng-controller="BlockController as block">
<ul>
<li ng-class="{ selected: block.isSet(1) }">
<a href ng-click="block.setTab(1)">Block 1</a></li>
<li ng-class="{ selected: block.isSet(2) }">
<a href ng-click="block.setTab(2)">Block 2</a></li>
...........
</ul>
</section>
the controller main.js
var app = angular.module('mainApp', ["ServicesModule"]);
app.controller('BolckController',["service",function("service"){
this.block = 1;
this.setBlock = function(newValue){
this.block = newValue;
//service is defined in servicesModule.js
service.doBlockAction(newValue);
};
this.isSet = function(blockName){
return this.block === blockName;
};
}
] );
the service servicesModule.js
var ServiceApp = angular.module('ServicesModule', []);
ServiceApp.factory('service', function() {
var doAction=function(BlockName){
if(BlockName === 1)
{
console.log('block1 is clicked!');
}else{if(BlockName === 2)}
console.log('block2 is clicked!');
};
return {
doBlockAction : function(blockName) {
return doAction(blockName);
}
}
});
I dont test the syntax but the logic make sense to work properly
i hope it helps

Can not reflect changes in model

I have an AngularJS controller like this
var myModule = angular.module('test', []);
myModule.controller('PendataanMainController',
function($scope, $http)
{
$scope.stat1 = "";
$scope.stat2 = "stat2";
$scope.AmbilStat1 = function()
{
$scope.stat1 = "Hallo stat1";
};
$scope.AmbilStat2 = function()
{
$scope.stat2 = "Hallo stat2";
}
}
);
myModule
.directive(
'fonloaded',
function()
{
return {
restrict: 'A',
link: function(scope, element, attrs)
{
var this_element = angular.element(element);
this_element.bind(
'click',
function()
{
//this_element.html('<h2>Hallo dari directive</h2>');
scope.stat1 = "Hallo dari directive";
}
);
scope.$apply();
}
}
}
);
and a HTML like this
<div ng-app="test" ng-controller="PendataanMainController" class="columns clear bt-space15">
<div class="col1-2 fl-space2">
<div class="content-box">
<div class="box-body">
<div fonloaded class="box-header clear">
<h2>{{stat1}}</h2>
</div>
{{AmbilStat2()}}
<div class="box-wrap clear">
{{stat2}}
</div>
</div>
</div>
</div>
<div class="col1-2 lastcol">
<div class="content-box">
<div class="box-body">
<div class="box-header clear">
<h2>Stat 2</h2>
</div>
<div class="box-wrap clear">
{{stat2}}
</div>
</div>
</div>
</div>
</div>
I made a custom directive 'fonloaded' and put it on this part:
<div fonloaded class="box-header clear">
<h2>{{stat1}}</h2>
</div>
and when it got clicked, the directive link function change the value of stat1. I want that new stat1 reflected in the html. But it don't.
I am using AngularJS extension for Chrome. And I can see that the stat1 value has changed, but that change does not reflected on the html.
What would be the problem?
http://plnkr.co/edit/4kiJwrG8Xo0unrNc9Fw7?p=preview
please see fixed sample:
function()
{
return {
restrict: 'A',
link: function(scope, element, attrs)
{
var this_element = angular.element(element);
this_element.on('click',
function(e)
{
// alert('1');
//this_element.html('<h2>Hallo dari directive</h2>');
scope.stat1 = "Hallo dari directive";
scope.$apply();
} );
}
}
}
I must add that I am working within a HTML template (like one from theme forest). I have stripped the ng-app part out of the HTML template to make it stand alone. And now it reflect the changes in the model.
Somehow the HTML template using jQuery to modify the tag which has fonloaded directive. As result {{stat1}} lost by HTML template transformation.

Resources