サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

AngularJS: コンポーネントを階層化する


AngularJSでは、アプリケーションをコンポーネントに分けて組み立てることができます。コンポーネントは、Angular 2にも採り入れられている仕組みです。大きなアプリケーションはコンポーネントを階層化して、機能ごとにコンポーネントを分けることにより、開発が柔軟に進められます。本稿は、「AngularJS: コンポーネントを使う」でつくった簡単なデータバインディングのサンプル001(図001)に、階層化したコンポーネントで機能を加えてみます。

図001■コントローラに定めた値がデータバインディングでコンポーネントに表示された

図001

01 コントローラに定めた値をデータバインディングでHTMLドキュメントに表示する

まずは、項目を複数表示できるようにします(図002)。このサンプル001では、ひとつの項目をコンポーネント(heroineDetail)に分けてHTMLドキュメントに加えました。すると、複数の項目をまとめる新たなコンポーネント('heroineList')が間に入ればよいでしょう。

図002■複数の項目のコンポーネントがまとめて示される

図002

メインのHTMLドキュメントに差し込むコンポーネントは、つぎのように複数項目をまとめる新たなコンポーネント(heroineList)に差し替えます。


<body ng-app="heroineApp">
<!--<div ng-controller="mainCtrl as ctrl">
	<h4>ヒロイン</h4>
	<heroine-detail heroine="ctrl.heroine"></heroine-detail>
</div>-->
	<heroine-list></heroine-list>
</body>

そして、複数項目をまとめるコンポーネント(heroineList)は、ngRepeatディレクティブで項目(list)の数だけコンポーネント(heroineDetail)を加えます(「AngularJS入門 04: 動的にリストをつくる」03「モジュールとコントローラをHTMLドキュメントに定める」参照)。また、項目ごとのオブジェクト(heroine)はコンポーネントの属性に加えました。


<!-- heroineList.html -->
<h4>ヒロイン</h4>
<heroine-detail ng-repeat="heroine in $ctrl.list" heroine="heroine"></heroine-detail>

ひとつひとつの項目を示すコンポーネントのテンプレート(heroineDetail.html)は、つぎのようにHTMLコードの構成を少しだけ変えます。


<!-- heroineDetail.html  -->
<hr>
<!--<span>名前: {{$ctrl.heroine.name}}</span>-->
<div>
名前: {{$ctrl.heroine.name}}<br>
</div>

メインのHTMLドキュメントは、コントローラの追加を外すとともに、複数項目をまとめるコンポーネントのJavaScriptファイル(heroineList.js)の読み込みを加えます。コントローラがなくなると、当然そこに設定されていた項目を表すオブジェクトのプロパティも失われます。けれど前述のとおり、複数項目をまとめるコンポーネント(heroineList)が項目ごとのコンポーネント(heroineDetail)に同じ名前のプロパティ(heroine)を与えています。


<script>
angular.module('heroineApp', []);
/* .controller('mainCtrl', function() {
	this.heroine = {
		name: 'シータ'
	};
});*/
</script>
<script src="heroineList.js"></script>
<script src="heroineDetail.js"></script>

複数項目をまとめるコンポーネントのJavaScriptファイル(heroineList.js)には、コントローラ(HeroineListController)に複数の項目をオブジェクトの配列(list)として納めます[*001]


// heroineList.js
angular.module('heroineApp')
.component('heroineList', {
	templateUrl: 'heroineList.html',
	controller: HeroineListController
});
function HeroineListController($scope, $element, $attrs) {
	var ctrl = this;
	ctrl.list = [
		{
			name: 'ナウシカ',

		},
		{
			name: 'シータ',

		}
	];
}

項目ごとのコンポーネントのJavaScriptファイル(heroineDetail.js)では、項目を示すオブジェクトが納められたプロパティ(heroine)にデータバインディングしています。値として与えられた文字列<は、=と異なり、親コンポーネントから子への一方向のデータバインディングを定めます。AngularJS 1.5から加えられた設定で、データ処理の流れを明らかにし、アプリケーションの負荷を下げるのに役立ちます。


// heroineDetail.js
angular.module('heroineApp')
.component('heroineDetail', {

	bindings: {
		heroine: '<'  // '='
	}
});

新たなコンポーネント(heroineList)を加えたことにより、HTMLドキュメントには複数の項目が示されるようになりました(前掲図002)。HTMLコードとJavaScriptファイルの記述は、つぎのコード001にまとめたとおりです。なお、項目ごとのオブジェクトに加えた新たなプロパティ(content)の値は、今のところドキュメントには表示されません。この値の表示と編集の機能を、後でコンポーネントとして増やします。

コード001■新たに加えたコンポーネントにより複数項目が示される

<body>要素

<body ng-app="heroineApp">
	<heroine-list></heroine-list>
</body>

heroineList.html

<h4>ヒロイン</h4>
<heroine-detail ng-repeat="heroine in $ctrl.list" heroine="heroine"></heroine-detail>

heroineDetail.html

<hr>
<div>
名前: {{$ctrl.heroine.name}}<br>
</div>

<script>要素

<script>
angular.module('heroineApp', []);
</script>
<script src="heroineList.js"></script>
<script src="heroineDetail.js"></script>

heroineList.js

angular.module('heroineApp')
.component('heroineList', {
	templateUrl: 'heroineList.html',
	controller: HeroineListController
});
function HeroineListController($scope, $element, $attrs) {
	var ctrl = this;
	ctrl.list = [
		{
			name: 'ナウシカ',
			content: ''
		},
		{
			name: 'シータ',
			content: '天空の城ラピュタ'
		}
	];
}

heroineDetail.js

angular.module('heroineApp')
.component('heroineDetail', {
	templateUrl: 'heroineDetail.html',
	controller: HeroineDetailController,
	bindings: {
		heroine: '<'
	}
});
function HeroineDetailController() {}

[*001] コンポーネントのコントローラに定めたコンストラクタ関数(HeroineListController)には、3つの引数($scopeと$elementおよび$attrs)を与えました。けれど、本稿の作例ではこれらの引数はとくに用いません。引数があることを示すために加えただけですので、省くことができます。

02 コンポーネントから表示項目を削除する

つぎに、複数項目が加えられたコンポーネントから、表示項目を削除できるようにしましょう(図003)。そのために、項目ごとのコンポーネントのテンプレート(heroineDetail.html)には、削除のボタン(<button>要素)を以下のように加えます。そして、ngClickディレクティブで項目削除の関数(onDelete())を呼び出します(「AngularJS入門 07: 項目を調べて削除する」03「チェックのついた項目を要素のクリックで削除する」参照)。

図003■項目削除の機能を加える

図003

<!-- heroineDetail.html -->
<div>
	名前: {{$ctrl.heroine.name}}<br>
	<button ng-click="$ctrl.onDelete()">削除</button>
</div>

呼び出す関数(onDelete())は、JavaScriptファイル(heroineDetail.js)でコンポーネントのbindingsプロパティに文字列の値&を与えることにより、親コンポーネントにイベントとしてコールバックできます。


// heroineDetail.js
angular.module('heroineApp')
.component('heroineDetail', {

	bindings: {

		onDelete: '&'
	}
});

バインディングしたコールバック関数(onDelete())は、コンポーネントのテンプレート(heroineList.html)にハイフン"-"つなぎのチェインケースにして、つぎのようにディレクティブ(on-delete)のかたちで加えられます。その値として、改めてコンポーネントのメソッド(deleteHeroine())呼び出しを定めました。引数には、削除するオブジェクト(heroine)を渡します。


<!-- heroineList.html -->
<h4>ヒロイン</h4>
<heroine-detail ng-repeat="heroine in $ctrl.list" heroine="heroine" 
	on-delete="$ctrl.deleteHeroine(heroine)"></heroine-detail>

項目を削除するメソッド(deleteHeroine())は、複数項目をまとめるコンポーネント(heroineList.js)に定めました。引数に受け取った削除するオブジェクトを、複数項目の配列(list)からArray.indexOf()メソッドによりインデックス番号で特定します(存在しないときの戻り値は-1)。そのうえで、そのエレメントをArray.splice()メソッドで除きます。


// heroineList.js
function HeroineListController($scope, $element, $attrs) {
	var ctrl = this;
	ctrl.list = [
		{
			name: 'ナウシカ',

		},
		{
			name: 'シータ',

		}
	];

	ctrl.deleteHeroine = function(heroine) {
		var index = ctrl.list.indexOf(heroine);
		if (index >= 0) {
			ctrl.list.splice(index, 1);
		}
	};
}

これで、項目ごとのコンポーネント(heroineDetail)のテンプレートに加えられたボタン(<button>要素)を押すと、項目リストのコンポーネント(heroineList)に定めたメソッド(HeroineListController())が呼び出されて、その項目は削除されます。ふたつのコンポーネントのテンプレートとJavaScriptファイルの中身をコード002にまとめました。メインのHTMLドキュメントの記述(<body>および<body>要素)は、前掲コード001のまま変えていません。

コード002■コントローラに定めたオブジェクトの値をコンポーネントで差し込んだテンプレートにデータバインディングする

heroineList.html

<h4>ヒロイン</h4>
<heroine-detail ng-repeat="heroine in $ctrl.list" heroine="heroine" 
	on-delete="$ctrl.deleteHeroine(heroine)"></heroine-detail>

heroineDetail.html

<div>
	名前: {{$ctrl.heroine.name}}<br>
	<button ng-click="$ctrl.onDelete({heroine: $ctrl.heroine})">削除</button>
</div>

heroineList.js

angular.module('heroineApp')
.component('heroineList', {
	templateUrl: 'heroineList.html',
	controller: HeroineListController
});
function HeroineListController($scope, $element, $attrs) {
	var ctrl = this;
	ctrl.list = [
		{
			name: 'ナウシカ',
			content: ''
		},
		{
			name: 'シータ',
			content: '天空の城ラピュタ'
		}
	];
	ctrl.deleteHeroine = function(heroine) {
		var index = ctrl.list.indexOf(heroine);
		if (index >= 0) {
			ctrl.list.splice(index, 1);
		}
	};
}

heroineDetail.js

angular.module('heroineApp')
.component('heroineDetail', {
	templateUrl: 'heroineDetail.html',
	controller: HeroineDetailController,
	bindings: {
		heroine: '<',
		onDelete: '&'
	}
});
function HeroineDetailController() {}

03 項目のデータを増やして新たなコンポーネントで編集する

項目リストのコンポーネント(heroineList)には、すでに前掲コード001でリストの配列(list)に納めた項目オブジェクトに、まだ使っていないプロパティ(content)が加えてありました。この値のテキストを、ただ表示するだけでなく、編集できるようにしましょう。そのためのコンボーネント(editableField)を新たに加え、項目ごとのコンポーネント(heroineDetail)のテンプレートにつぎのように定めます。そして、プロパティ値は、新たなコンポーネントに後で加えるngModelディレクティブのプロパティ(fieldValue)の値としました。


<-- heroineDetail.html >
<div>
	名前: {{$ctrl.heroine.name}}<br>
	作品: <editable-field field-value="$ctrl.heroine.content"></editable-field><br>
	<button ng-click="$ctrl.onDelete({heroine: $ctrl.heroine})">削除</button>
</div>

テキスト編集のテンプレート(editableField.html)には、以下のようにボタン(<button>要素)を加えて、クリックしたときngClickディレクティブでこの後コンポーネント(editableField)に定めるメソッド(handleModeChange())を呼び出します。このメソッドはプロパティ(editMode)のブール(論理)値を切り替えます。そして、この値はngSwitchディレクティブに定めました。すると、このプロパティ値により、要素の表示と非表示が切り替えられます(Build Insider「式の値によって表示を切り替えるには?(ng-switch)」参照)。

<input>要素にはngSwitchWhenディレクティブに文字列trueを与えましたので、プロパティ値がtrueのとき表示されます。type属性に{{ }}で加えたプロパティ(fieldType)の値は"text"としますので、1行入力フィールドとなり、ngModelディレクティブに定めたプロパティ(fieldValue)の値が示されます。値の当てはまるngSwitchWhenディレクティブがないときは、ngSwitchDefaultディレクティブを与えた<span>要素が表示され、{{ }}に納めたプロパティ値がテキストとして加えられます(図004)。

なお、ボタン(<button>要素)のテキストには、{{ }}条件(三項)演算子? :でふたつのテキストを与えましたので、条件のプロパティ(editMode)のブール値によってそれらが切り替わることになります。

図004■編集ボタンでテキストを書き替えて保存できる

図004

<!-- editableField.html -->
<span ng-switch="$ctrl.editMode">
	<input ng-switch-when="true" type="{{$ctrl.fieldType}}" ng-model="$ctrl.fieldValue">
	<span ng-switch-default>{{$ctrl.fieldValue}}</span>
</span>
<button ng-click="$ctrl.handleModeChange()">{{$ctrl.editMode ? '保存' : '編集'}}</button>

テキスト編集のコンボーネント(editableField)には、つぎのようなJavaScriptコードを定めます。コントローラ(EditableFieldController)に、ふたつのプロパティ(fieldValueとfieldType)をバインディングしました。ひとつ(fieldValue)は、項目ごとのコンポーネントのテンプレート(heroineDetail.html)に定められたプロパティで、項目リストのコンポーネント(heroineList)の配列(list)に納めた項目オブジェクトから取り出されたプロパティ(content)の値が割り当てられています。つまり、これが新たに加えるテキストです。

バインディングされたもうひとつのプロパティ(fieldType)は、テンプレートに加えた<input>要素のtype属性の値として用いられています。bindingsプロパティに与えた文字列@は、一方向の文字列のバインディングを示します(「AngularJS Directive Attribute Binding Explanation」5.「Isolate Scope One Way String Binding」参照)。そして、このバインディングされたプロパティの初期値は、コントローラ(EditableFieldController)につぎのように定められた$onInit()メソッドが与えます。このメソッドはHTMLドキュメントのエレメントに加えたコントローラの生成とデータバインディングがすべて済んだときに呼び出されます。


// editableField.js
angular.module('heroineApp')
.component('editableField', {
	templateUrl: 'editableField.html',
	controller: EditableFieldController,
	bindings: {
		fieldValue: '<',
		fieldType: '@'
	}
});
function EditableFieldController($scope, $element, $attrs) {
	var ctrl = this;
	ctrl.editMode = false;
	ctrl.handleModeChange = function() {
		var editMode = ctrl.editMode;
		ctrl.editMode = !editMode;
	};
	ctrl.$onInit = function() {
		if (!ctrl.fieldType) {
			ctrl.fieldType = 'text';
		}
	};
}

メインのHTMLドキュメントには、新たに定めたコンポーネント(editableField)のJavaScriptファイルの読み込みも加えます。これで編集したテキストを保存して表示することができます。


<script src="heroineList.js"></script>
<script src="heroineDetail.js"></script>
<script src="editableField.js"></script>

04 リセットボタンでテキストを編集前に戻す

仕上げにリセットボタンを加えて、編集しているテキストを編集前の値に戻せるようにしましょう。テキスト編集のコンポーネントのテンプレート(editableField.html)には、つぎのようにリセットボタン(<button>要素)を記述します。ngIfディレクティブを与えましたので、定めたプロパティ(editMode)がtrueのとき、つまりテキストを編集する場合にだけ要素はつくられます。そして、ngClickディレクティブにコントローラのメソッド(reset())呼び出しを書き加えました。


<-- editableField.html -->

<button ng-click="$ctrl.handleModeChange()">{{$ctrl.editMode ? '保存' : '編集'}}</button>
<button ng-if="$ctrl.editMode" ng-click="$ctrl.reset()">リセット</button>

テキスト編集のコンポーネントのJavaScriptファイル(editableField.js)に、編集をリセットするメソッド(reset())はつぎのように加えます。編集状態に切り替えるメソッド(handleModeChange())で編集前のプロパティ(fieldValue)のテキストを変数(fieldValueOld)にとっておき、リセットされたらその値を戻すというだけです。なお、変数の初期値は$onInit()メソッドで取り出してあります。


// editableField.js
angular.module('heroineApp')
.component('editableField', {

	controller: EditableFieldController,

});
function EditableFieldController($scope, $element, $attrs) {
	var ctrl = this;
	var fieldValueOld = '';

	ctrl.handleModeChange = function() {
		var editMode = ctrl.editMode;
		if (editMode) {
			var fieldValue = ctrl.fieldValue;
			fieldValueOld = fieldValue;
		}

	};
	ctrl.reset = function() {
		ctrl.fieldValue = fieldValueOld;
	};
	ctrl.$onInit = function() {
		fieldValueOld = ctrl.fieldValue;

	};
}

これで、編集し始めてから、保存前にリセットボタンを推すと、編集前のテキストに戻せます。テキストが空の状態から入力したのなら、リセットボタンでフィールドは再び空になります(図005)。動きとしては、これででき上がったように見えます。けれどこのままでは、データバインディングが正しく行われていません。つぎに項を改めてご説明しましょう。

図005■編集し始めてからリセットボタンで編集前のテキストに戻せる

図005

05 一方向のバインディングでデータの変更を親コンポーネントに反映させる

まず、前述03「項目のデータを増やして新たなコンポーネントで編集する」の機能を使って、初期値が空だった項目のプロパティ(content)に前掲図004のようにテキスト(「風の谷のナウシカ」)を入力して保存します。項目のオブジェクトの初期値は、つぎのように項目リストのコンポーネント(heroineList)に定められていました。保存されたテキストは、正しくテキスト編集のコンポーネント(editableField)のテンプレートに示されます。


// heroineList.js
angular.module('heroineApp')
.component('heroineList', {

	controller: HeroineListController
});
function HeroineListController($scope, $element, $attrs) {
	var ctrl = this;
	ctrl.list = [
		{
			name: 'ナウシカ',
			content: ''
		},
		{
			name: 'シータ',
			content: '天空の城ラピュタ'
		}
	];
	ctrl.deleteHeroine = function(heroine) {

	};
}

つぎに、今保存した項目を項目リストから除きます。コンポーネント(heroineList)に定めた削除のメソッド(deleteHeroine())には、あらかじめつぎのような確認用のふたつのconsoleのメソッドを加えておきました。Chromeのコンソールには、以下の図006のような結果が示されます。


// heroineList.js
function HeroineListController($scope, $element, $attrs) {
	var ctrl = this;

	ctrl.deleteHeroine = function(heroine) {
console.log(heroine);  // 確認用
		var index = ctrl.list.indexOf(heroine);
		if (index >= 0) {
			ctrl.list.splice(index, 1);
		}
console.dir(ctrl.list);  // 確認用
	};
}

削除しようとしている項目のオブジェクトのプロパティ(content)に、編集して保存したテキストが与えられておらず初期値の空のままです。ただ、削除の処理の後に配列の長さが1になったのは、エレメントのオブジェクトが正しく除かれたことを表しています。

図006■コンソールに示された削除する項目のオブジェクトと配列の長さ

図006

テキスト編集のコンポーネント(editableField)に入力した値が、親の項目リストのコンポーネント(heroineList)のオブジェクトに反映されなかったのは、つぎのようにプロパティ(fieldValue)のバインディングが一方向の<で定められていたからです。これを双方向の=に変えれば、親コンポーネントの値も連動するようになります。けれどそうすると、データがいつどこで書き替えられるのか捉えにくくなります(「Components」「Component-based application architecture」参照)。また、双方向でデータを監視することは、アプリケーションの負荷を高めます。


// editableField.js
angular.module('heroineApp')
.component('editableField', {

	bindings: {
		fieldValue: '<',

	}
});

項目の削除が正しくできたのは、データをもつ項目リストのコンポーネント(heroineList)に削除のメソッドを定め、子のコンポーネント(heroineDetail)がコールバックでそのメソッドを呼び出したからです。ですから、テキストの編集についても親コンポーネントにそのためのメソッドを加えて、子のコンポーネントからはコールバックで実行すればよいでしょう。こうすると、データはそのもち主のコンポーネントが操作することになって、処理の流れもわかりやすくなります。

そこでまず、テキスト編集のコンポーネント(editableField)に、コールバック(onUpdate)のバインディング(&)を加えます。そして、このコールバックは、編集と保存の切り替えるメソッド(EditableFieldController())が、テキストを保存するときに呼び出します。引数はオブジェクトで、そのプロパティ(value)の値に編集されたテキストを与えました。


// editableField.js
angular.module('heroineApp')
.component('editableField', {

	controller: EditableFieldController,
	bindings: {

		onUpdate: '&'
	}
});
function EditableFieldController($scope, $element, $attrs) {
	var ctrl = this;

	ctrl.handleModeChange = function() {

		if (editMode) {

			ctrl.onUpdate({value: fieldValue});

		}

	};

}

テキスト編集のコンポーネント(editableField)を差し込んだ項目ごとのコンポーネント(heroineDetail)には、コールバックをディレクティブのかたちで与え、コンポーネントのメソッド(update())を呼び出しています。引数には文字列('content')と、前述のコールバック呼び出しで受け取ったオブジェクトからプロパティ(value)の値を取り出して渡します。


<!-- heroineDetail.html -->
<div>
	名前: {{$ctrl.heroine.name}}<br>
	作品: <editable-field field-value="$ctrl.heroine.content" 
		on-update="$ctrl.update('content', value)"></editable-field><br>
	<button ng-click="$ctrl.onDelete()">削除</button>
</div>

項目ごとのコンポーネント(heroineDetail)は、まだ書き替えるデータのもち主ではありません。そこで、さらにコールバック(onUpdate)のバインディング(&)を加えます。そして、呼び出されたメソッド(update())からバケツリレーのようにコールバックをつなぐのです。


// heroineDetail.js
angular.module('heroineApp')
.component('heroineDetail', {

	controller: HeroineDetailController,
	bindings: {

		onUpdate: '&'
	}
});
function HeroineDetailController($scope, $element, $attrs) {
	var ctrl = this;
	ctrl.update = function(prop, value) {
		ctrl.onUpdate({prop: prop, value: value});
	};
}

データのもち主は、項目リストのコンポーネント(heroineList)です。そこに差し込んだ項目ごとのコンポーネント(heroineDetail)に、つぎのようにコールバックのディレクティブを与え、テキスト編集のメソッド(updateHeroine())を呼び出しました。


<!-- heroineList.html -->
<h4>ヒロイン</h4>
<heroine-detail ng-repeat="heroine in $ctrl.list" heroine="heroine" 
	on-delete="$ctrl.deleteHeroine(heroine)" 
	on-update="$ctrl.updateHeroine(heroine, prop, value)"></heroine-detail>

項目リストのコンポーネント(heroineList)に定めたテキスト編集のメソッド(updateHeroine())は、つぎのように引数に受け取った項目のオブジェクト(heroine)のプロパティ(prop)に新たなテキスト(value)を定めます。これで、前述と同じテキスト編集と保存の操作をすると、コンポーネントのデータが正しく書き替えられました(図007)。3つのコンポーネントのテンプレートとJavaScriptファイルの中身は、以下のコード003にまとめたとおりです(メインのHTMLドキュメントの記述は前掲コード001のまま変えていません)。また、サンプル001に作例をフレームに入れて掲げました。


// heroineList.js
angular.module('heroineApp')
.component('heroineList', {

	controller: HeroineListController
});
function HeroineListController($scope, $element, $attrs) {
	var ctrl = this;

	ctrl.updateHeroine = function(heroine, prop, value) {
		heroine[prop] = value;
	};

}

図007■コンソールに示された削除する項目オブジェクトのプロパティと値

図007

コード003■コントローラに定めたオブジェクトの値をコンポーネントで編集・保存・リセットする

heroineList.html

<h4>ヒロイン</h4>
<heroine-detail ng-repeat="heroine in $ctrl.list" heroine="heroine" 
	on-delete="$ctrl.deleteHeroine(heroine)" 
	on-update="$ctrl.updateHeroine(heroine, prop, value)"></heroine-detail>

heroineDetail.html

<div>
	名前: {{$ctrl.heroine.name}}<br>
	作品: <editable-field field-value="$ctrl.heroine.content" 
		on-update="$ctrl.update('content', value)"></editable-field><br>
	<button ng-click="$ctrl.onDelete()">削除</button>
</div>

editableField.html

<span ng-switch="$ctrl.editMode">
	<input ng-switch-when="true" type="{{$ctrl.fieldType}}" ng-model="$ctrl.fieldValue">
	<span ng-switch-default>{{$ctrl.fieldValue}}</span>
</span>
<button ng-click="$ctrl.handleModeChange()">{{$ctrl.editMode ? '保存' : '編集'}}</button>
<button ng-if="$ctrl.editMode" ng-click="$ctrl.reset()">リセット</button>

heroineList.js

angular.module('heroineApp')
.component('heroineList', {
	templateUrl: 'heroineList.html',
	controller: HeroineListController
});
function HeroineListController($scope, $element, $attrs) {
	var ctrl = this;
	ctrl.list = [
		{
			name: 'ナウシカ',
			content: ''
		},
		{
			name: 'シータ',
			content: '天空の城ラピュタ'
		}
	];
	ctrl.updateHeroine = function(heroine, prop, value) {
		heroine[prop] = value;
	};
	ctrl.deleteHeroine = function(heroine) {
		var index = ctrl.list.indexOf(heroine);
		if (index >= 0) {
			ctrl.list.splice(index, 1);
		}
	};
}

heroineDetail.js

angular.module('heroineApp')
.component('heroineDetail', {
	templateUrl: 'heroineDetail.html',
	controller: HeroineDetailController,
	bindings: {
		heroine: '<',
		onDelete: '&',
		onUpdate: '&'
	}
});
function HeroineDetailController($scope, $element, $attrs) {
	var ctrl = this;
	ctrl.update = function(prop, value) {
		ctrl.onUpdate({prop: prop, value: value});
	};
}

editableField.js

angular.module('heroineApp')
.component('editableField', {
	templateUrl: 'editableField.html',
	controller: EditableFieldController,
	bindings: {
		fieldValue: '<',
		fieldType: '@',
		onUpdate: '&'
	}
});
function EditableFieldController($scope, $element, $attrs) {
	var ctrl = this;
	var fieldValueOld = '';
	ctrl.editMode = false;
	ctrl.handleModeChange = function() {
		var editMode = ctrl.editMode;
		if (editMode) {
			var fieldValue = ctrl.fieldValue;
			ctrl.onUpdate({value: fieldValue});
			fieldValueOld = fieldValue;
		}
		ctrl.editMode = !editMode;
	};
	ctrl.reset = function() {
		ctrl.fieldValue = fieldValueOld;
	};
	ctrl.$onInit = function() {
		fieldValueOld = ctrl.fieldValue;
		if (!ctrl.fieldType) {
			ctrl.fieldType = 'text';
		}
	};
}

サンプル001■項目の値がコンポーネントで編集・保存・リセットできる


作成者: 野中文雄
作成日: 2016年5月20日


Copyright © 2001-2016 Fumio Nonaka.  All rights reserved.