SpacePenって
Write markup on the final frontier
SpacePen is a powerful but minimalistic client-side view framework for CoffeeScript. It combines the "view" and "controller" into a single jQuery object, whose markup is expressed with an embedded DSL similar to Markaby for Ruby.
atom/space-pen
SpacePen(無重力ペン)はView層とControle層を合わせたCoffeeScriptのjQueryオブジェクト扱うフレームワークだとか何とか…
何に使うの?
AtomのPackageとかのUIはこれで書く。
もちろんそのままブラウザ上に出力させるHTMLにも使えちゃう。
とりあえずAtomパッケージ作るにあたって必要になったので学習がてらにReadmeを元に使い方を書いてみる。
どんな感じに書くの
基本的な奴
View
Classの@content
に例えば次のように書く。
class Spacecraft extends View
@content: ->
@div =>
@h1 "Spacecraft"
@ol =>
@li "Apollo"
@li "Soyuz"
@li "Space Shuttle"
見ての通りHTMLのタグ名がそのまま@content
クラス内のメソッドになっている。
また、jQueryで次のように表現するようなものは
view = new Spacecraft
view.find('ol').append('<li>Star Destroyer</li>')
view.on 'click', 'li', ->
alert "They clicked on #{$(this).text()}"
SpacePenでは
class Spacecraft extends View
@content: -> ...
addSpacecraft: (name) ->
@find('ol').append "<li>#{name}</li>"
view = new Spacecraft
view.addSpacecraft "Enterprise"
って出来るらしい。
更にコンストラクタで引数渡したりとかも、ほら。
class Spacecraft extends View
@content: (params) ->
@div =>
@h1 params.title
@ol =>
@li name for name in params.spacecraft
view = new Spacecraft(title: "Space Weapons", spacecraft: ["TIE Fighter", "Death Star", "Warbird"])
jQueryのprototypeをオーバーライドしたり
class Spacecraft extends View
@content: -> ...
hide: ->
console.log "Hiding Spacecraft List"
super()
初期化メソッドも。
class Spacecraft extends View
@content: -> ...
initialize: (params) ->
@title = params.title
アウトレット/イベント
outletイベントを使えばクラス内の他のメソッドからその要素のjQueryオブジェクトが扱える。
class Spacecraft extends View
@content: ->
@div =>
@h1 "Spacecraft"
@ol outlet: "list", =>
@li "Apollo"
@li "Soyuz"
@li "Space Shuttle"
addSpacecraft: (name) ->
@list.append("<li>#{name}</li>")
click
イベントをつけておけばクリックしたらメソッド呼び出せる…ボタンとか。
class Spacecraft extends View
@content: ->
@div =>
@h1 "Spacecraft"
@ol =>
@li click: 'launchSpacecraft', "Saturn V"
launchSpacecraft: (event, element) ->
console.log "Preparing #{element.name} for launch!"
Markup DSLの詳細
-
タグメソッド (
@div
,@h1
みたいなの)
ここまで見てきたようにメソッド名がそのままhtml要素になります。
タグメソッドには次の3種類の引数がわたせます。- 文字列
文字列をHTMLエスケープしてタグ内のテキストにします。(ex:
@div "foo"
→<div>foo</div>
) - ハッシュ
キーと値のペアはHTMLの属性になります。(ex:
@img src: "url"
→<img src="url">
) - function (=>)
関数をそのまま引数として渡すと子要素として扱われます。(ex:
@ol => @li "me"
→<ol><li>me</li></ol>
) - テキストメソッド
-
@text(string)
引数のテキストをHTMLエスケープして文字列として出力します。 -
@raw(string)
テキストをエスケープせずにHTMLとして出力します。事前に形成されたHTMLを使うにはこれを利用します。
- 文字列
文字列をHTMLエスケープしてタグ内のテキストにします。(ex:
Subviews
@subview
を使えばコードをモジュール化出来ます。
@subview(name,view)
メソッドは別のviewを呼び出し、そこに出力します。
class Spacecraft extends View
@content: (params) ->
@div =>
@subview 'launchController', new LaunchController(countdown: params.countdown)
@h1 "Spacecraft"
...
Freeform Markup Generation
SpacePenマークアップにViewクラスを使用する必要はありません。
view.render
をタグメソッドを呼び出す関数(=>
ではなく->
)と呼び出します。(?)
これはアドホック使用用のドキュメントフラグメントを返します。
このメソッドは便宜上グローバル変数$$
に割り当てられています。
view.list.append $$ ->
@li =>
@text "Starship"
@em "Enterprise"
jQuery拡張
$.fn.view
任意のDOMオブジェクトのViewオブジェクトを取得出来ます。
view = new Spacecraft
$('body').append(view)
# assuming no other li elements on the DOM, for example purposes,
# the following expression should be true
$('li').view() == view
After Attach Hooks
The initialize method is always called when the view is still a detached DOM fragment, before it is appended to the DOM. This is usually okay, but occasionally you'll have some initialization logic that depends on the view actually being on the DOM. For example, you may depend on applying a CSS rule before measuring an element's height.
SpacePen extends jQuery manipulation methods like append, replaceWith, etc. to call afterAttach hooks on your view objects when they are appended to other elements. The hook will be called with a boolean value indicating whether the view is attached to the main DOM or just to another DOM fragment. If afterAttach is called with true, you can assume your object is attached to the page.
class Spacecraft extends View
@content: -> ...
afterAttach: (onDom) ->
if onDom
console.log "With CSS applied, my height is", @height()
else
console.log "I just attached to", @parent()
Before Remove Hooks
SpacePen calls the beforeRemove hook whenever a view is removed from the DOM via a jQuery method. This works if the view is removed directly with remove or indirectly when a method like empty or html is called on a parent element. This is a good place to clean up subscriptions and other view-specific state.
class Spacecraft extends View
@content: -> ...
initialize: ->
$(window).on 'resize.spacecraft', -> ...
beforeRemove: ->
$(window).off('.spacecraft')
HTMLで使う
exampleではこのようにしています。
(別途coffee.js,jQuery.js,spec-penのコアファイルが読み込まれているので試す場合は配置に注意してください。--リポジトリをそのままclone or 展開 したのを鯖に上げればおk。)
<!DOCTYPE HTML>
<html>
<head>
<title>Space Pen Example</title>
<!-- coffeescript -->
<script type="text/javascript" src="../vendor/coffee-script.js"></script>
<!-- dependencies -->
<script type="text/javascript" src="../vendor/jquery.js"></script>
<!-- implementation -->
<script type="text/coffeescript" src="../src/space-pen.coffee"></script>
<!-- specs -->
<script type="text/coffeescript" src="../examples/example.coffee"></script>
</head>
<body></body>
</html>
class HelloView extends View
@content: (params) ->
@div =>
@div params.greeting
@label for: 'name', "What is your name? "
@div =>
@input name: 'name', outlet: 'name'
@button click: 'sayHello', "That's My Name"
@div outlet: "personalGreeting"
initialize: (params) ->
@greeting = params.greeting
sayHello: ->
@personalGreeting.html("#{@greeting}, #{@name.val()}")
$('body').append(new HelloView(greeting: "Hi there"))
説明するまでも無いだろうので解説は省略。
Atomのスタイルガイド
メニューのPackage > Styleguide > showとかから見れます。
SpacePenとHTMLのそれぞれのマークアップの仕方が見れますね。
さいごに
SpacePenはAtomのパッケージ作るには必須とも言えますね。めんどくさいわけでもないので書けるように…
この記事はAtomから書いてAtomから投稿しています。これについては近いうちに公開するつもりなのでお楽しみに!(?)
Atomは既にQiita投稿できるMarkdownのメモ帳化しています! 結局公開できないままでした......他の方が似たようなの公開されてましたね。
Atom曰くSpacePenの時代は終わった!これからはWebComponentsだっ!だそうです。
嘘です。そんなことかいてないです(?) そこまでは言ってないけど新しいspace-penが出来る......?
2か月前で止まってるIssueだけれども気になったので貼っておきました。
以上。