In an effort to learn more about AngularJS directives, I decided to create a simple, edit-in-place directive. I built a simple directive that would display a value, along with an edit link and the edit link would toggle visibility of an input and the save/cancel links. From there, I wanted a setup where I could make multiple properties of an object editable, and I wanted to share the link controls and trigger updates to each property together.
I decided to create a wrapper directive to handle the links and ended up with a working version in which I ended up broadcasting events from the parent(wrapper) directive and taking action in the nested directives based on that. I feel like there is probably a better approach, but this is what I was able to make work.
The HTML:
<html ng-app="editInPlaceApp">
<head>
<script data-require="angular.js@*" data-semver="1.3.6" src="https://code.angularjs.org/1.3.6/angular.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div ng-controller="MainController">
<ul>
<li ng-repeat="p in people">
<wrap-in-place>
<value-in-place value="p.fname"></value-in-place>
<value-in-place value="p.lname"></value-in-place>
</wrap-in-place>
</li>
</ul>
</div>
<script src="script.js"></script>
</body>
</html>
The JavaScript:
var app = angular.module('editInPlaceApp',['myDirectives']);
app.controller('MainController', ['$scope', 'PersonService', function($scope, PersonService){
$scope.people = PersonService.loadPeople();
}])
.factory('PersonService', function(){
var factory = {};
factory.loadPeople = function(){
var peeps = [{"id":"abc0", fname:"John", lname:"Doe"},
{"id":"abc1", fname:"Jane", lname:"Smith"},
{"id":"abc2", fname:"Bob", lname:"Jones"}];
return peeps;
};
return factory;
});
angular.module('myDirectives',[])
.directive('wrapInPlace', function(){
return {
restrict: 'E',
transclude: true,
scope:true,
template: '<span ng-transclude></span> <a href="#" ng-click="edit()" ng-show="!editChildren">edit</a> <span ng-show="editChildren"><a href="#" ng-click="saveChildren()">save</a> <a href="#" ng-click="cancel()">cancel</a></span>',
link: function($scope, element, attrs){
$scope.edit = function(){
$scope.editChildren = true;
$scope.$broadcast('editChildren',{"val":$scope.editChildren});
};
$scope.cancel = function(){
$scope.editChildren = false;
$scope.$broadcast('editChildren',{"val":$scope.editChildren});
};
$scope.saveChildren = function(){
$scope.editChildren = false;
$scope.$broadcast('saveChildren');
};
}
};
})
.directive('valueInPlace', function(){
return {
restrict: 'E',
scope: {
value: '='
},
template: '<span ng-show="!editing">{{value}}</span><input type="text" ng-model="updatedValue" ng-show="editing">',
link: function ( $scope, element, attrs ) {
$scope.editing = false;
$scope.updatedValue = $scope.value;
$scope.$on('editChildren', function(event, args){
$scope.editing = args.val;
if(!args.val){
$scope.updatedValue = $scope.value;
}
});
$scope.$on('saveChildren', function(event, args){
$scope.editing = false;
$scope.value = $scope.updatedValue;
});
}
};
});
The full working example can be found here.