2010年1月10日日曜日

Breaking Up the Display

コメントをユーザのweb体験ともっと統合するにはどうすれば良いのでしょう?投稿全部とコメント全部がページに全部表示されている、ってのはちょっと大げさでしょう。多分、ブログのメインページとコメント表示は別々にした方が良いでしょう。投稿の見え方の補助的な「ディティール」と、そこでのコメントのあり方を提案してみましょう。

さて、ブログのトップレベルの見え方はブログのタイトルと本体とします。ついでに、投稿に関係したコメントが何個付いたのか、カウンターも表示しましょう。

そうすると、投稿の詳細ページに到達する何らかの方法が必要となります。一つの手は、各投稿のタイトルにハイパーリンクを貼る事です:ユーザが投稿の詳細ページを見たければ、そこへ行くためタイトルをクリックすれば良い。投稿の詳細ページに、ユーザが新規のコメントを追加出来るフォームを追加すれば良いでしょう。

以下はwebアプリにコメントを追加する簡単なページフローのダイアグラムです。



ダイアグラムの各場所はリクエストを受け取るハンドラに対応しています。予想どおり、もうちょっとsend/suspend/dispatchを使いましょう。ダイアグラム中の矢印はembed/urlで生成されたURLを表しています。

これはちょっとだけ複雑な結果をもたらします:以前は、ハイパーリンクなしの投稿リストをレンダリングしてました。しかし、全ての特殊な移動用URLを生成する機能がembed/urlを使う以上、ハイパーリンクタイトルを作るときembed/urlを使って受け取るrender-postsrender-postを調整する必要があります。

Webアプリは、現時点、次のようになります:


#lang web-server/insta

; ブログは (make-blog posts)
; 投稿リストは (listof post)
(define-struct blog (posts) #:mutable)

; 投稿は (make-post title body comments)
; タイトルは文字列で本体も文字列
; コメントは (listof string)
(define-struct post (title body comments) #:mutable)

; BLOG: blog
; ブログの初期値
(define BLOG
(make-blog
(list (make-post "First Post"
"This is my first post"
(list "First comment!"))
(make-post "Second Post"
"This is another post"
(list)))))

; blog-insert-post!: blog post -> void
; ブログと投稿を受け取り、ブログの頭に投稿を付け加える
(define (blog-insert-post! a-blog a-post)
(set-blog-posts! a-blog
(cons a-post (blog-posts a-blog))))


; post-insert-comment!: post string -> void
; 投稿とコメント文字列を受け取る。副作用として投稿の
; コメントリストの一番下にコメントを付け加える
(define (post-insert-comment! a-post a-comment)
(set-post-comments!
a-post
(append (post-comments a-post) (list a-comment))))

; start: request -> html-response
; リクエストを受け取り、webコンテンツの全てを表示する
; ページを生成する
(define (start request)
(render-blog-page request))

; render-blog-page: request -> html-response
; ブログの内容のhtml-responseページを
; 生成する
(define (render-blog-page request)
(local [(define (response-generator make-url)
`(html (head (title "My Blog"))
(body
(h1 "My Blog")
,(render-posts make-url)
(form ((action
,(make-url insert-post-handler)))
(input ((name "title")))
(input ((name "body")))
(input ((type "submit")))))))

; parse-post: bindings -> post
; 束縛から投稿を抽出する
(define (parse-post bindings)
(make-post (extract-binding/single 'title bindings)
(extract-binding/single 'body bindings)
(list)))

(define (insert-post-handler request)
(blog-insert-post!
BLOG (parse-post (request-bindings request)))
(render-blog-page request))]

(send/suspend/dispatch response-generator)))

; render-post-detail-page: post request -> html-response
; 投稿とリクエストを受け取り投稿の詳細ページを生成する
; ユーザは新規コメントを挿入可能
(define (render-post-detail-page a-post request)
(local [(define (response-generator make-url)
`(html (head (title "Post Details"))
(body
(h1 "Post Details")
(h2 ,(post-title a-post))
(p ,(post-body a-post))
,(render-as-itemized-list
(post-comments a-post))
(form ((action
,(make-url insert-comment-handler)))
(input ((name "comment")))
(input ((type "submit")))))))

(define (parse-comment bindings)
(extract-binding/single 'comment bindings))

(define (insert-comment-handler a-request)
(post-insert-comment!
a-post (parse-comment (request-bindings a-request)))
(render-post-detail-page a-post a-request))]


(send/suspend/dispatch response-generator)))


; render-post: post (handler -> string) -> html-response
; 投稿を受け取り、投稿のhtml-response要素を生成する
; 要素は投稿の詳細ページを示すリンクを含む
(define (render-post a-post make-url)
(local [(define (view-post-handler request)
(render-post-detail-page a-post request))]
`(div ((class "post"))
(a ((href ,(make-url view-post-handler)))
,(post-title a-post))
(p ,(post-body a-post))
(div ,(number->string (length (post-comments a-post)))
" comment(s)"))))

; render-posts: (handler -> string) -> html-response
; make-urlを受け取り、全投稿のhtml-response
; 要素を生成する
(define (render-posts make-url)
(local [(define (render-post/make-url a-post)
(render-post a-post make-url))]
`(div ((class "posts"))
,@(map render-post/make-url (blog-posts BLOG)))))

; render-as-itemized-list: (listof html-response) -> html-response
; アイテムのリストを受け取り未整列のリストとして
; レンダリングを生成する
(define (render-as-itemized-list fragments)
`(ul ,@(map render-as-item fragments)))

; render-as-item: html-response -> html-response
; html-responseを受け取り、リストアイテムとして
; レンダリングを生成する
(define (render-as-item a-fragment)
`(li ,a-fragment))

私たちは極めて洗練されたアプリを手に入れました:今は投稿も出来るしコメントも書けます。しかしながら、まだこの問題があります:ユーザがpost-detail-pageにいるとブラウザの戻るボタンを押すことなくブログに戻る事が出来ないのです!これじゃあ混乱します。ユーザがwebアプリの片隅で立ち往生せず、ブログのメインページへと戻れるようなページフローを提供しなければなりません。



0 件のコメント:

コメントを投稿