From a previous question I asked (http://stackoverflow.com/q/13383552/740318) I came full circle from creating a poorly designed filter, to a (still poorly developed, yet effective) directive, and in doing so, I realized it should have been done as a filter when properly implemented. Now the curious part is that (with my limited understanding) I'm going to have to re-establish a few of the "hackish" techniques implemented originally to get the filter to work when converting it.
The directive is as such:
widget.directive('truncate', function() {
return {
restrict: 'A',
replace: true,
// {{child.display}} will be a truncated copy of child.name
template: '<a class="entity-name" href="{{child.url}}" title="{{child.name}}">{{child.display}}</a>',
link: function(scope, element, attr) {
var widthThreshold = $(element).parent().parent().width() * 0.85,
// get the font-size without the 'px' at the end, what with media queries effecting font
fontSize = $(element).css('font-size').substring(0, $(element).css('font-size').lastIndexOf('px')),
sizeRatio = 29760/20621,
characterCount = Math.floor((widthThreshold / fontSize) * sizeRatio);
scope.$watch('child', function(val) {
// Truncate it and trim any possible trailing white-space
var truncatedName = $.trim(scope.child.name.substring(0, characterCount));
// Make sure characterCount isn't > the current length when accounting for the ...
if (characterCount < scope.child.name.length + 3) {
scope.child.display = truncatedName + '...';
}
});
}
}
});
For simplicity sake, I used jQuery to get the .width()
of the element's parent's parent. This gives me the total screen width I have to work with. Depending on the aforementioned value (as well as things like the font-size CSS property), I truncate the string to display as much as reasonably possible, then end it with three periods ("..."). This was simple given that the element is passed in to the directive, and I could simply use $(element)
with jQuery to get the identifier and work off that.
Now with the filter, I am restricted to only the string of characters being passed in, and no element as I did with a directive. With my limited knowledge, the only ways of accomplishing this in a filter are to either use the original (and likely process intensive) $('a:contains("' + name + '")')
method implemented used before (in the previous question mentioned above), or maybe dynamically assign an id to each <a>
and pass that in to the filter. Maybe something along the lines of (un-tested pseudo-code, hopefully it gets the point across):
<script>
function entityController($scope, $http) {
$http.get('/path/to/json').success(function(res) {
/* Sample return JSON ("uuid" is a unique database stored identifier):
* {
* children: [
* { uuid:"123", url:"path/to/file", name:"File Name", display:"File Name" },
* { uuid:"234", url:"path/to/file2", name:"Second File Name", display:"Second File Name" },
* ...
* ]
* }
*/
$scope.children = res.children
});
}
</script>
<body class="ng-controller:entityController" ng-controller="entityController">
<div ng-repeat="child in children">
<!-- Not sure if {{child.name | truncate:uuid}} would work, hopefully it conveys the point -->
<a id="{{child.uuid}}" href="{{child.url}}" title="{{child.name | truncate:uuid}}">{{child.display}}</a>
</div>
</body>
Now if I wanted to access it via jQuery, I would have to replicate the above directive's functionality by doing something hackish such as:
var app = angular.module('app', []);
app.filter('truncate', function() {
return function(str, uuid) {
var widthThreshold = $('#' + uuid).parent().parent().width() * 0.85,
fontSize = $('#' + uuid).css('font-size').substring(0, $('#' + uuid).lastIndexOf('px')),
...
});
It just seems to spiral downhill from there, where I'm continuing to query the DOM for more and more information that makes me contemplate whether or not a directive would be the improper (yet "cleaner", if it were ever that) approach. When using a filter, is there a proper way to get access to DOM properties, such as the width of a parent element, or font sizes without having to abuse jQuery, and bulk up my syntax just to get access to the DOM in a filter?
AFAIK, there's no way to pass $element into a filter via angular. Directives are what are meant for DOM manipulation. While it might feel like you're just trying to shorten the string in your scope, you're really trying to manipulate what's being added to the DOM, so the directive is the right choice, IMO.
That said, have you looked into the CSS text-overflow? It might do what you're looking to do without JavaScript.