Have a look at this plunk

There are many things at play:
- The value is displayed as a regular
<span>
(to be able to show ellipsis), and when clicked, the <input>
is displayed instead (controlled by editing
variable)
focusMe
directive is used to give focus to the <input>
when it's created to replace the <span>
nonBreaking
filter to make the text label displayed in one line
- various CSS rules based on flexbox control the display
overflow
, text-overflow
take care about ellipsis
white-space: nowrap
and flex-shrink: 0
make sure that text label is not broken into multiple lines
flex-grow: 1
makes sure that <input>
takes all extra space available
Drawbacks:
- when user clicks the static
<span>
for the first time, the cursor is put at the beginning of the <input>
, not in the place where the user clicked (if he clicked in the middle)
- when the labels are longer than viewport width, the textinput won't be displayed (assumption is that viewport width is always much wider than label length)
Additional info:
When you add validation, you probably want to keep the <input>
displayed if the model is in invalid state, otherwise the bound value is empty and span will be empty.
To do it:
- pass "name" to the directive and inside do
<input name="{{name}}">
- change to
<span ng-show="!editing && form.{{name}}.$valid">
- change to
<input ng-show="editing || !form.{{name}}.$valid">
Full 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;
}