My final solution
angular.module('ui.directives').directive('collapsibleText', function ($window, $timeout) {
return {
restrict: 'A',
require: '^collapsibleText',
scope: { collapsibleText: '@' },
template: '<div style="display: inline-block;height: 20px;white-space: nowrap;position: relative;padding-right: 30px;">'
+ '<i style="cursor:pointer" ng-hide="textSize < parentSize && !collapsed" ng-class="collapsed && \'icon-collapse\' || \'icon-expand\'" ng-click="changeCollapsed()"></i>'
+ '<div ng-class="collapsed && \'no-collapsible\' || \'collapsible-text\'">{{collapsibleText}}</div>'
+ '<span ng-hide="textSize < parentSize">...</span>'
+ '</div>',
controller: ['$scope', function ($scope) {
$scope.collapsed = false;
$scope.getTextSize = function () {
var paddingLeft = $scope.element.css('paddingLeft'),
paddingRight = $scope.element.css('paddingRight');
var $shadow = angular.element('<span></span>').css({
position: 'absolute',
paddingLeft: paddingLeft,
paddingRight: paddingRight,
fontSize: $scope.element.css('fontSize'),
fontFamily: $scope.element.css('fontFamily'),
lineHeight: $scope.element.css('lineHeight'),
resize: 'none',
visibility: 'hidden'
});
angular.element(document.body).append($shadow);
$shadow.html($scope.collapsibleText);
$scope.textSize = $shadow[0].offsetWidth + 40;
return $scope.textSize;
};
$scope.getParentSize = function() {
$scope.parentSize = $scope.parent[0].offsetWidth;
return $scope.parentSize;
};
$scope.changeCollapsed = function () {
$scope.collapsed = !$scope.collapsed;
$timeout(function() {
angular.element($window).triggerHandler('collapseChanged');
}, 100);
};
}],
link: function (scope, element) {
scope.element = $(element);
scope.parent = scope.element.parent();
scope.getTextSize();
var w = angular.element($window);
w.bind('resize shown collapseChanged', function () {
scope.getParentSize();
scope.$apply();
});
}
};
});