Earlier we have started to build an editor in Angular to edit a record. In the first article we have prepared and editor for a simple input box for free text.
This time we'll add an editor for a record where the user must choose from a fixed list of values.
In the HTML we have two pages in two div
elements. One of them is for the list of records, the
one is the editor. In addition we also have an h2
element called 'Records' which is always visible.
It shows in a rather readable way the content of the records
attribute which contains all the records.
You can try the code by clicking on "Try".
examples/angular/editor_with_selector/edit_record.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, user-scalable=yes">
<title>Edit Record</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="edit_record.js"></script>
</head>
<body ng-app="EditRecordApp" ng-controller="EditRecordController">
<div ng-show="page === 'list'">
<h2>Listing</h2>
<div ng-repeat="r in records">{{r.name}}
<button ng-click="edit($index)">Edit</button>
</div>
</div>
<div ng-show="page === 'editor'">
<h2>Editor</h2>
<div>Name: <input ng-model="editor.name"></div>
Planet: <select ng-options="v as v.name for v in values track by v.id" ng-model="editor.planet"></select>
<div> <button ng-click="save()">Save</button> <button ng-click="cancel()">Cancel</button> </div>
</div>
<h2>Records</h2>
<pre>
{{records | json : pretty}}
</pre>
</body>
</html>
In the JavaScript file we have two function that represent out interaction with the remote servers.
get_data
sets the current list of records as we have received from the server, get_values
returns the list of values available for the planet
field.
$scope.page
holds the name of the current page. We start by showing the list
of the records.
When the user clicks on the Edit
button the $scope.edit
is called, passing the $index
of the current record in the list of records in $scope.records
.
We copy the content of the current record to $scope.editor
and switch to the editor
view.
We use an ng-options
directive to show a select
element and we use
"v as v.name for v in values track by v.id"
to actually list the potential values.
In this case the use of track by
is not critical, as the values themselves are unique,
but if there can be duplication of values, then we must explicitly tell AngularJS which field to use
as unique identifier for the record. Without that ng-options
will break.
If the user clicks Cancel
we switch back to the list view removing the content of the $scope.editor
attribute.
If the user clicks Save
we first copy the content of the $scope.editor
back to current_record
and then clear the content of $scope.editor
.
examples/angular/editor_with_selector/edit_record.js
angular.module("EditRecordApp", [])
.controller("EditRecordController", function($scope) {
$scope.page = 'list';
$scope.records = get_data();
$scope.values = get_values();
$scope.edit = function(idx) {
$scope.editor = angular.copy($scope.records[ idx ]);
$scope.current_record = idx;
$scope.page = 'editor'
}
$scope.cancel = function() {
$scope.editor = null;
$scope.page = 'list';
}
$scope.save = function() {
$scope.records[ $scope.current_record ] = angular.copy($scope.editor);
$scope.editor = null;
$scope.page = 'list';
}
});
var get_data = function() {
return [
{
'name' : 'Foo'
},
{
'name' : 'Bar',
'planet' : {
'id' : 1
}
},
{
'name' : 'Qux',
'planet': {
'name': 'Venus'
}
},
{
'name' : 'ET',
'planet': 'Mars'
}
];
};
var get_values = function() {
return [
{
'id' : 1,
'name' : 'Mercury'
},
{
'id' : 2,
'name' : 'Venus'
},
{
'id' : 3,
'name' : 'Earth'
},
];
};
As you can see there are 4 cases in the original data. 'Foo' does not have any planet in the original data. In this case the list selector will come up empty, but once we selected a value there is no going back. (Well, except if we press cancel and don't save the change.)
In the case of 'Bar' the 'planet' attribute holds an object (hash for Ruby and Perl programmers, dictionary for Python programmers)
with the id of the planet from the $scope.values
list. When we open the editor it will automatically select and display
the proper value. Once we click on save (leaving the same value or selecting another planet) the full value record (both name and id)
will be stored in $scope.records
.
'Qux' has a planet in the original data, but it is represented only by its name. Our solution does not know what to do with it
as the values are identified by their id. So when we try to Edit
this record we'll have no value selected in the planet selector.
The same goes with 'ET', whose planet is not an object but a simple string.