<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@1.4.0-beta.6" data-semver="1.4.0-beta.6" src="https://code.angularjs.org/1.4.0-beta.6/angular.js"></script>
<script data-require="lodash.js@*" data-semver="3.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<user-greeting></user-greeting>
<p>
<small>Double-click on the name above. Hit Enter to save changes.</small>
</p>
</body>
</html>
var m = angular.module('example', []);
// Fake service.
m.service('userService', function ($q) {
this.get = function () {
var deferred = $q.defer();
deferred.resolve({name: 'Bob'});
return deferred.promise;
};
this.save = function (userData) {
var deferred = $q.defer();
deferred.resolve(userData);
return deferred.promise;
};
});
// The container component that talks to userService.
m.directive('userGreeting', function () {
return {
template: '<user-greeting-message user="ctrl.user" ' +
'on-save="ctrl.handleSave(user)"></user-gretting-message>',
controller: function (userService) {
this.userService = userService;
// Data fetching, same as before.
this.userService.get().then((function (user) {
this.user = user;
}).bind(this));
this.handleSave = function (user) {
this.userService.save(user).then((function (data) {
this.user = data;
}).bind(this));
};
},
bindToController: true,
controllerAs: 'ctrl'
};
});
// The UI component that renders UI based on the user data passed into it.
m.directive('userGreetingMessage', function () {
return {
scope: {
user: '=',
saveCallback: '&onSave'
},
template: '<p>Hello ' +
'<editable-user-name ' +
'user="ctrl.user" ' +
'on-save="ctrl.handleSave(user)">' +
'</editable-user-name></p>',
controller: function () {
this.handleSave = function (user) {
this.saveCallback({user: user});
}
},
bindToController: true,
controllerAs: 'ctrl'
};
});
m.directive('editableUserName', function () {
return {
scope: {
user: '=',
saveCallback: '&onSave'
},
template:
'<span>' +
'<span class="view" ng-dblclick="ctrl.edit()" ng-show="!ctrl.isEditing()">' +
'{{ ctrl.user.name }}' +
'</span>' +
'<form ng-submit="ctrl.handleSave()">' +
'<input ng-show="ctrl.isEditing()" ng-model="ctrl.userForm.name" ng-blur="ctrl.reset()" />' +
'</form>' +
'</span>'
,
controller: function ($element) {
this.edit = function () {
this.userForm = _.extend({}, this.user);
setTimeout(function(){
$element.find('input')[0].focus();
}, 10);
};
this.reset = function () {
this.userForm = null;
};
this.isEditing = function () {
return !!this.userForm;
};
this.handleSave = function () {
// Invoke callback from container.
this.saveCallback({user: this.userForm});
// Null out form to force use back to view mode.
this.reset();
};
},
bindToController: true,
controllerAs: 'ctrl'
};
});
angular.element(document).ready(function() {
angular.bootstrap(document, ['example']);
});
body {
font-family: "Helvetica Neue", Arial, sans-serif;
}
form {
display: inline;
}
.view {
border-bottom: 1px dashed black;
}