Modify DOM content after Scope Variables have been translated - angularjs

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>

Related

Using same directive with different value in the same view

I have two tabs, each tab contains a set of thumbnails which I have set as a directive. Tab is also coming via a directive, and thumbnails too. The data to be pushed into the thumbnail is stored in an constant file. But each tabs thumbnails data is different. How do I implement that?
For setting tabs and thumbnail:
<tabset> <tab heading="{{heading.title}}" id="heading1" set-thumb="setThumbnail(arg)">
<thumbnail></thumbnail>
</tab> <tab heading="{{heading1.title}}" id="heading2" set-thumb="setThumbnail(arg)">
<thumbnail></thumbnail>
</tab> </tabset>
Thumbnail HTML:
<div class="row" style="padding: 20px;">
<div class="col-md-3" ng-repeat="data in thumbnailDetails">
<div class="thumbnail-sim">
<img src="resources/images/Sim-thumbnail.png" alt="sim"
class="thumbnail-img">
<div class="thumbnail-caption">
<div class="thumbnail-name">{{ data.name }}</div>
<div class="thumbnail-date">{{ data.time }}</div>
<div class="thumbnail-details">
{{ data.details }}
</div>
</div>
</div>
<br>
</div>
Thumbnail directive:
angular.module('thumbnailDirectiveModule', [])
.directive('thumbnail', function() {
return {
restrict: 'E',
templateUrl: 'UI/templates/thumbnail.html'
};
});
service part method:
s.getThumbnail = function(thisId){
if(thisId === 'heading1'){
return thumbnailDetails1;
}else if (thisId === 'heading2'){
return thumbnailDetails2;
}
};
Tab directive:
angular.module('tabsDirectiveModule', [])
.directive('tab', function() {
return {
restrict: 'E',
transclude: true,
template: '<div role="tabpanel" ng-show="active" ng-transclude></div>',
require: '^tabset',
scope: {
heading: '#',
id: '#',
setThumb: '&'
},
link: function(scope, elem, attr, tabsetCtrl) {
scope.active = false;
tabsetCtrl.addTab(scope, attr.id);
}
};
})
.directive('tabset', function() {
return {
restrict: 'E',
transclude: true,
scope: { },
templateUrl: 'UI/templates/tabset.html',
bindToController: true,
controllerAs: 'tabset',
controller: function() {
var self = this;
self.tabs = [];
self.addTab = function addTab(tab, id) {
tab.setThumb({arg: id});
self.tabs.push(tab);
if(self.tabs.length === 1) {
tab.active = true;
}
};
self.select = function(selectedTab) {
angular.forEach(self.tabs, function(tab) {
if(tab.active && tab !== selectedTab) {
tab.active = false;
}
});
selectedTab.active = true;
};
}
};
});

Variable value not passing in a controller using directive with ng-class

I am referencing the value of the variable in a controller in an ng-class template but its not working.
here is the html directive template URl :
<div class="tooltip-anchor">
<div class=" tooltip-content ehub-body" ng-class="{ 'tooltip__content--disabled': tooltipContentValue}" ng-transclude>Tooltip content</div>
</div>
Here is where i am using the directive in the index page
<div style="text-align:center;">
<ehub-tooltip>Hello i am here, and i am her to stay</ehub-tooltip>over here
<ehub-tooltip>Be nice to people on your way up and they will be nice to you on your way down</ehub-tooltip>click me
</div>
And here is the directive:
in this directive i am creating a variable and setting it to false and also trying to use it in an ng-class attribute
(function (window) {
'use strict';
angular
.module('ehub.component.tooltip', [])
.controller('ehubTooltipCtrl', ['$scope', function ($scope) {
$scope.tooltipContentValue = false;
}])
.directive('ehubTooltip', ehubTooltip);
function ehubTooltip() {
var directive = {
controller: "ehubTooltipCtrl",
link: link,
transclude: true,
templateUrl: 'ehub-tooltip.html',
restrict: 'E'
};
return directive;
function link(scope, element, attrs) {
scope.keyupevt = function () {
if (event.keyCode === 27) {
$scope.tooltipContentValue = true;
}
}
}
}
})();
Try this working jsfiddle.
angular.module('ExampleApp', ['ngMessages'])
.controller('ExampleController', function($scope) {
})
.directive('ehubTooltip', function() {
var directive = {
link: link,
transclude: true,
template: '<div class="tooltip-anchor"><div class=" tooltip-content ehub-body" ng-class="{ \'tooltip__content--disabled\': tooltipContentValue}" ng-transclude>Tooltip content</div></div>',
restrict: 'E'
};
function link(scope, element, attrs) {
scope.tooltipContentValue = false;
scope.keyupevt = function() {
if (event.keyCode === 27) {
scope.tooltipContentValue = true;
}
}
}
return directive;
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<div style="text-align:center;">
<a href="" ng-keyup="keyupevt()">
<ehub-tooltip>Hello i am here, and i am her to stay</ehub-tooltip>over here</a>
<a href="" ng-keyup="keyupevt()">
<ehub-tooltip>Be nice to people on your way up and they will be nice to you on your way down</ehub-tooltip>click me</a>
</div>
</div>
</div>

Unable to access the dom element in angular directive template

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!

angular directive data binding

I'm trying to write a simple Angular directive that displays an object properties, but I don't understand how the binding works.
<div ng-repeat="c in cars">
<div car carElement={{c}}></div>
<div>
.directive('car', function() {
return {
template: function(elem, attr){
var car = attr.carElement;
return car.brand + ' ' + car.model;
}
};
});
I can't bind the hole object, just simple properties. For example, this works:
<div ng-repeat="c in cars">
<div car brand={{c.brand}} ></div>
<div>
.directive('car', function() {
return {
template: function(elem, attr){
var brand = attr.brand;
return ''+brand;
}
};
});
How can I send the entire car to the directive template?
You have to pass the object, but not inside an expression:
<div ng-repeat="c in cars">
<div car carElement="c"></div>
<div>
Directive:
.directive('car', function() {
return {
scope: { car: "=carElement" },
template: function(elem, attr){
//you now have 'car' in scope.car
}
};
});

Angular.js directive dynamic templateURL

I have a custom tag in a routeProvider template that that calls for a directive template. The version attribute will be populated by the scope which then calls for the right template.
<hymn ver="before-{{ week }}-{{ day }}"></hymn>
There are multiple versions of the hymn based on what week and day it is. I was anticipating to use the directive to populate the correct .html portion. The variable is not being read by the templateUrl.
emanuel.directive('hymn', function() {
var contentUrl;
return {
restrict: 'E',
link: function(scope, element, attrs) {
// concatenating the directory to the ver attr to select the correct excerpt for the day
contentUrl = 'content/excerpts/hymn-' + attrs.ver + '.html';
},
// passing in contentUrl variable
templateUrl: contentUrl
}
});
There are multiple files in excerpts directory that are labeled before-1-monday.html, before-2-tuesday.html, …
emanuel.directive('hymn', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// some ode
},
templateUrl: function(elem,attrs) {
return attrs.templateUrl || 'some/path/default.html'
}
}
});
So you can provide templateUrl via markup
<hymn template-url="contentUrl"><hymn>
Now you just take a care that property contentUrl populates with dynamically generated path.
You can use ng-include directive.
Try something like this:
emanuel.directive('hymn', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
scope.getContentUrl = function() {
return 'content/excerpts/hymn-' + attrs.ver + '.html';
}
},
template: '<div ng-include="getContentUrl()"></div>'
}
});
UPD. for watching ver attribute
emanuel.directive('hymn', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
scope.contentUrl = 'content/excerpts/hymn-' + attrs.ver + '.html';
attrs.$observe("ver",function(v){
scope.contentUrl = 'content/excerpts/hymn-' + v + '.html';
});
},
template: '<div ng-include="contentUrl"></div>'
}
});
Thanks to #pgregory, I could resolve my problem using this directive for inline editing
.directive("superEdit", function($compile){
return{
link: function(scope, element, attrs){
var colName = attrs["superEdit"];
alert(colName);
scope.getContentUrl = function() {
if (colName == 'Something') {
return 'app/correction/templates/lov-edit.html';
}else {
return 'app/correction/templates/simple-edit.html';
}
}
var template = '<div ng-include="getContentUrl()"></div>';
var linkFn = $compile(template);
var content = linkFn(scope);
element.append(content);
}
}
})
You don't need custom directive here. Just use ng-include src attribute. It's compiled so you can put code inside. See plunker with solution for your issue.
<div ng-repeat="week in [1,2]">
<div ng-repeat="day in ['monday', 'tuesday']">
<ng-include src="'content/before-'+ week + '-' + day + '.html'"></ng-include>
</div>
</div>
I had the same problem and I solved in a slightly different way from the others.
I am using angular 1.4.4.
In my case, I have a shell template that creates a CSS Bootstrap panel:
<div class="class-container panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">{{title}} </h3>
</div>
<div class="panel-body">
<sp-panel-body panelbodytpl="{{panelbodytpl}}"></sp-panel-body>
</div>
</div>
I want to include panel body templates depending on the route.
angular.module('MyApp')
.directive('spPanelBody', ['$compile', function($compile){
return {
restrict : 'E',
scope : true,
link: function (scope, element, attrs) {
scope.data = angular.fromJson(scope.data);
element.append($compile('<ng-include src="\'' + scope.panelbodytpl + '\'"></ng-include>')(scope));
}
}
}]);
I then have the following template included when the route is #/students:
<div class="students-wrapper">
<div ng-controller="StudentsIndexController as studentCtrl" class="row">
<div ng-repeat="student in studentCtrl.students" class="col-sm-6 col-md-4 col-lg-3">
<sp-panel
title="{{student.firstName}} {{student.middleName}} {{student.lastName}}"
panelbodytpl="{{'/student/panel-body.html'}}"
data="{{student}}"
></sp-panel>
</div>
</div>
</div>
The panel-body.html template as follows:
Date of Birth: {{data.dob * 1000 | date : 'dd MMM yyyy'}}
Sample data in the case someone wants to have a go:
var student = {
'id' : 1,
'firstName' : 'John',
'middleName' : '',
'lastName' : 'Smith',
'dob' : 1130799600,
'current-class' : 5
}
I have an example about this.
<!DOCTYPE html>
<html ng-app="app">
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<div class="container-fluid body-content" ng-controller="formView">
<div class="row">
<div class="col-md-12">
<h4>Register Form</h4>
<form class="form-horizontal" ng-submit="" name="f" novalidate>
<div ng-repeat="item in elements" class="form-group">
<label>{{item.Label}}</label>
<element type="{{item.Type}}" model="item"></element>
</div>
<input ng-show="f.$valid" type="submit" id="submit" value="Submit" class="" />
</form>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.min.js"></script>
<script src="app.js"></script>
</body>
</html>
angular.module('app', [])
.controller('formView', function ($scope) {
$scope.elements = [{
"Id":1,
"Type":"textbox",
"FormId":24,
"Label":"Name",
"PlaceHolder":"Place Holder Text",
"Max":20,
"Required":false,
"Options":null,
"SelectedOption":null
},
{
"Id":2,
"Type":"textarea",
"FormId":24,
"Label":"AD2",
"PlaceHolder":"Place Holder Text",
"Max":20,
"Required":true,
"Options":null,
"SelectedOption":null
}];
})
.directive('element', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
scope.contentUrl = attrs.type + '.html';
attrs.$observe("ver", function (v) {
scope.contentUrl = v + '.html';
});
},
template: '<div ng-include="contentUrl"></div>'
}
})

Resources