Webエンジニア susumuis の技術ブログ

このブログの内容は個人の見解であり、所属する組織の公式見解ではありません

[javascript] javascriptテンプレートエンジン(?)表示の繰り返し更新対応

どうも、懲りない僕です。JavaScriptでビュー書いてるんだから、動的にビュー動かしたいよね?だから、TemplateEngineRunnerは繰り返し実行できなきゃ意味ないよね?なんてわけで、対応しました。
それにしてもアレなサンプル……
http://s-ishigami.appspot.com/files/test.html

ソース

TemplateEngine = new function() {
	var currentNode;
	
	this.setCurrentNode = setCurrentNode;
	this.propertyModelValue = function(object, propName) { return new PropertyModel(object, propName).getValue; };
	this.writeText = function (value) { return new WriteText(value); };
	this.writeHtml = function (value) { return new WriteHtml(value); };
	this.setAttribute = function (name, value) { return new SetAttribute(name, value); };
	this.loopWhile = function(condition, subController) { return new LoopFor(emptyFunction, condition, emptyFunction, subController); }
	this.loopFor = function(init, condition, after, subController) { return new LoopFor(init, condition, after, subController); }
	
	function emptyFunction() {}
	function setCurrentNode(node) {
		currentNode = node;
	}
	function functionOrValue(f) {
		if (typeof f == 'function') {
			return f();
		}
		return f;
	}
	function PropertyModel(object, propName) {
		this.getValue = getValue;
		function getValue() {
			return object[propName];
		}
	}
	function WriteText(value) {
		this.execute = execute;
		function execute() {
			currentNode.text(functionOrValue(value));
		}
	}
	function WriteHtml(value) {
		this.value = value;
		this.execute = execute;
		function execute() {
			currentNode.html(functionOrValue(value));
		}
	}
	function SetAttribute(name, value) {
		this.execute = execute;
		function execute() {
			currentNode.attr(name, functionOrValue(value));
		}
	}
	function LoopFor(init, condition, after, subController) {
		this.execute = execute;
		function execute() {
			init();
			while (condition()) {
				var clone = currentNode.clone(true);
				clone.attr('id', null);
				clone.addClass('GENERATED_NODE');
				clone.addClass(currentNode.attr('id') + "_CLONE");
				
				if (subController != null) {
					var thisNode = currentNode;
					TemplateEngineRunner.run(subController, clone);
					setCurrentNode(thisNode);
				}
				clone.find('*').attr('id', null);
				clone.insertBefore(currentNode);
				after();
			}
			currentNode.addClass('ORIGINAL_NODE');
			currentNode.hide();
		}
	}
}

TemplateEngineRunner = new function() {
	this.run = run;
	
	function run(controller, parentNode) {
		clearGeneratedNodes(parentNode);
		if (typeof parentNode == 'String') {
			parentNode = $('#' + parentNode);
		}
			
		for (var key in controller) {
			if (parentNode == null) {
				TemplateEngine.setCurrentNode($('#' + key));
			} else {
				TemplateEngine.setCurrentNode(parentNode.find('#' + key));
			}
			controller[key].execute();
		}
	}
	
	function clearGeneratedNodes(parentNode) {
		
		if (parentNode == null) {
			$('.GENERATED_NODE').remove();
			$('.ORIGINAL_NODE').show();
		} else {
			if (typeof parentNode == 'String') {
				parentNode = $('#' + parentNode);
			}
			parentNode.find('.GENERATED_NODE').remove();
			parentNode.find('.ORIGINAL_NODE').show();
		}
		
	}
}
var myModel = {
	name: 'テスト',
	sex: 'male',
	position: 0,
	goNext: function() {
		this.position += 1;
	},
	nameChange: function() {
		this.name = this.name == "テスト" ? "太郎" : "テスト";
	}
};

with (TemplateEngine) {
	var yamanote = ['大崎', '品川', '田町', '浜松町', '新橋', '有楽町', '東京', '神田', '秋葉原', '御徒町', '上野', '鶯谷', '日暮里', '西日暮里', '田端', '駒込', '巣鴨', '大塚', '池袋', '目白', '高田馬場', '新大久保', '新宿', '代々木', '原宿', '渋谷', '恵比寿', '目黒', '五反田'];
	var i = 0;
	var controller = {
		// オブジェクトのプロパティ出力(動的)
		name: writeText(propertyModelValue(myModel, 'name')),
		
		// 固定値出力
		test: writeText('生麦生米生卵'),
			
		// 動的値出力
		now: writeText(function() {return new Date().toString();}),
			
		// 固定HTML出力
		testHtml: writeHtml('<span style="color: blue;">青</span>巻紙<span style="color: red;">赤</span>巻紙<span style="color: yellow;">黄</span>巻紙'),
			
		// 属性変更
		attr: setAttribute('style', 'font-size: x-large; color: blue;'),
		
		// ループ(indexを外側の変数で定義しなければいけないことが課題)
		'for': loopFor(
			function() {i = 0},
			function() {return i < 10},
			function() {i++;},
			{
				counterFor: writeText(function() {return yamanote[(myModel.position + i) % yamanote.length];})
			}
		)
	}
}

TemplateEngineRunner.run(controller);
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<title>Template Engine Test</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<a href="javascript: myModel.goNext(); TemplateEngineRunner.run(controller)">次の駅へ</a>

<p>現在日時: <span id="now"></span></p>
<table border="1">
	<col width="200">
	<col width="200">
	<tr>
		<th bgcolor="silver">key</th>
		<th bgcolor="silver">val</th>
	</td>
	<tr>
		<th>氏名</th>
		<td align="center"><a href="javascript: myModel.nameChange(); TemplateEngineRunner.run(controller)"><span id="name"></span></a></td>
	</td>
	<tr>
		<th>得意技</th>
		<td><span id="attr"><span id="test"></span></span></td>
	</td>
	<tr>
		<th>得意技2</th>
		<td><span id="testHtml"></span></td>
	</td>
	<tr id="for">
		<th>次は</th>
		<td id="counterFor"></td>
	</tr>
</table>



<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.4.2");</script>
<script type="text/javascript" src="templateEngine.js"></script>  
<script type="text/javascript" src="test.js"></script>  

</body>
</html>


propertyModelValueとか、とてもWicket風。Mayaaを目指したらWicketに似てきた。最初からWicket風につくるべきだったか?