Check out this code snippet
https://i.sstatic.net/KgLZP.png
There are several components in action:
- The value is shown as a regular
<span>
(to display ellipsis), and when clicked, the <input>
replaces it (controlled by the editing
variable)
- A directive called
focusMe
is used to focus on the newly created <input>
replacing the <span>
- A filter named
nonBreaking
ensures that the text label is displayed in a single line
- CSS rules utilizing flexbox control the layout
- Properties like
overflow
, text-overflow
handle the appearance of ellipsis
white-space: nowrap
and flex-shrink: 0
prevent breaking the text label into multiple lines
flex-grow: 1
ensures that the <input>
expands to fill extra space available
Disadvantages:
- Clicking on the static
<span>
places the cursor at the beginning of the <input>
, not where the user clicked (especially if clicking in the middle)
- If labels exceed viewport width, the text input won't be visible (assuming the viewport is wider than the label length)
Additional information:
When adding validation, keeping the <input>
visible for invalid data prevents an empty span when the model is invalid.
To achieve this:
Complete code:
HTML:
<!doctype html>
<html ng-app="plunker">
<head>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.js"></script>
<script src="script.js"></script>
<link rel="stylesheet" href="style.css">
<script type="text/ng-template" id="InputTextWithEllipsis.html">
<div class="input-text-with-ellipsis-wrapper">
<label class="mylabel" ng-bind-html="mylabel | nonBreaking"></label>
<span class="dual-input-wrapper">
<span
ng-show="!editing"
ng-click="editing = true"
class="static-text-with-ellipsis">{{mymodel}}</span>
<input
ng-show="editing"
ng-focus="editing = true"
ng-blur="editing = false"
focus-me="editing"
ng-model="mymodel"
class="editable-textinput" />
</span>
</div>
</script>
</head>
<body ng-controller="MainCtrl">
<form name="profile">
<input-text-with-ellipsis
mylabel="'Name'"
mymodel="dataModel.name"
></input-text-with-ellipsis>
<input-text-with-ellipsis
mylabel="'Last Name'"
mymodel="dataModel.lastName"
></input-text-with-ellipsis>
<input-text-with-ellipsis
mylabel="'A very long label here'"
mymodel="dataModel.lastName"
></input-text-with-ellipsis>
</form>
</body>
</html>
JS:
var myModule = angular.module('plunker', []);
myModule.filter('nonBreaking', function($sce) {
return function(inputStr) {
var outputStr = inputStr.replace(/\s/g, ' ');
return $sce.trustAsHtml(outputStr);
};
});
/*
* http://stackoverflow.com/a/14837021/245966
*/
myModule.directive('focusMe', function($timeout, $parse) {
return {
link: function(scope, element, attrs) {
var model = $parse(attrs.focusMe);
scope.$watch(model, function(value) {
if (value === true) {
$timeout(function() {
element[0].focus();
});
}
});
}
};
});
myModule.controller('MainCtrl', function($scope) {
$scope.dataModel = {
name: "Fernando",
lastName: "Fernandez Sanchez de la Frontera"
}
});
myModule.directive('inputTextWithEllipsis', function(){
return {
restrict: 'E',
templateUrl: 'InputTextWithEllipsis.html',
require: ['^form'],
scope: {
mylabel: '=',
mymodel: '='
},
link: function(scope, element, attrs, ctrls) {
scope.editing = false;
}
};
});
CSS:
* {
font: 16pt sans-serif;
border: 0;
padding: 0;
margin: 0;
outline: 0;
}
.input-text-with-ellipsis-wrapper {
background-color: linen;
padding: 10px;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
}
.mylabel {
background-color: #ffddcc;
margin-right: 10px;
flex-basis: auto;
flex-shrink: 0;
min-width: 50px;
}
.dual-input-wrapper {
flex-basis: auto;
flex-grow: 1;
overflow: hidden;
white-space: nowrap;
text-align: right;
}
.editable-textinput {
background-color: #ddf;
width: 100%;
text-align: right;
}
.static-text-with-ellipsis {
background-color: #eeccbb;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}