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">veryverylongunbreakablecontent</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
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;
};
}
};
});
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>
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!
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
}
};
});
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>'
}
})