2012年9月19日水曜日

djangoでjsonpを返す

クロスドメインでjsonデータのやり取りするアプリを開発していた時に
ブラウザからアクセスするとちゃんと返ってくるのに、
「$.getJSON」や「$.ajax」を使うとHTTPレスポンス200なのにエラーが返ってくる現象に出くわした。

しばらく悩んでたらajaxのポリシーでクロスドメインにアクセスするときはjsonpを使う必要があるようだった。

どういったものなのかというと、通常はjsonそのまま渡すのに対して、
jsonpは、queryに書かれたcallback名で囲ってapplication/javascriptで返すようになる。


jsonの場合
 http://127.0.0.1/sample/

 {data1:1, data2:"hello"}

jsonpの場合
 http://127.0.0.1/sample/?callback=jsonp_1234&_=2345

 jsonp_12345({data1:1, data2:"hello"});



実装方法は下にまとめてみた。

まずはjavascript側から


$.getJSON('http://127.0.0.1/sample/?callback=?', function(data){console.dir(data);});

$.ajax({url:'http://127.0.0.1/sample/', dataType='jsonp', success: function(data){console.dir(data);});


次にサーバ側(django)もjsonpに対応

def JSONP(f):
    """
        Usage:

        @JSONP
        def func(request):
            dic = { 'data1': 1, 'data2': 'hello'}
            return dic
    """
    def _func(*args, **kwargs):
        request = args[0]
        callback = request.GET.get('callback', None)
        dic = f(request)
        ret = json.dumps(dic)
       
        if callback is None:
            ct = 'application/json'
        else:
            ret = '%s(%s);' % (callback, ret)
            ct = 'application/javascript'

        return HttpResponse(ret, mimetype=ct)

    return _func

2012年9月14日金曜日

jquery mobile + backbone サンプル(2)

前回の続き。


今回はViewにイベントを追加してみる。

一応Viewのおさらいをしておくと、View=画面になる。
画面にはボタンなどが配置されていて、 マウスやタッチパネルで操作ができ、ユーザからイベント発行される。
今回はそのボタンとイベントを作成してみようと思う。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>example</title>
        <link rel="stylesheet" href="./jquery.mobile-1.1.1/jquery.mobile-1.1.1.min.css" />
        <link rel="stylesheet" href="my.css" />
        <style>
        </style>
        <script src="underscore.js"></script>
        <script src="json2.js"></script>
        <script src="jquery-1.7.1.min.js"></script>
        <script src="backbone-min.js"></script>
        <script src="./jquery.mobile-1.1.1/jquery.mobile-1.1.1.min.js"></script>
        <script src="my.js"></script>
    </head>
    <body>
        <div data-role="page" id="page1">
            <div data-theme="a" data-role="header"><h3>sample2</h3></div>
            <div data-role="content" style="padding: 15px">
        <!-- content start -->
        <div id="myview"></div>
        <!-- content end -->
            </div>
            <div data-theme="a" data-role="footer" data-position="fixed"><h3></h3></div>
        </div>

    <!-- underscore template start -->
    <script type="text/template" id="hello-template">
        <button id="btn_push">Push</button>
        <h3><%= part1 %>, <%= part2 %>!</h3>
        <h3>counter <%= counter %></h3>
        </script>
    <!-- underscore template end -->

    <!-- backbone start -->
        <script>
        (function($){
                MyView = Backbone.View.extend({
            el: '#myview'
            ,initialize: function(){
                this.counter = 0;
                this.render();
            }
            ,render: function(){
                var data = { part1: 'hello', part2: 'backbone!', counter: this.counter }
                var tmpl = _.template($("#hello-template").html(), data);
                this.$el.html(tmpl);
                this.$el.trigger('create');
            }
            ,events: {
                'click button#btn_push': 'evt_push'
                    }
            ,evt_push: function(){
                this.counter++;
                this.render();
            }
            });
            var myview = new MyView();
        })(jQuery);


    </script>
    <!-- backbone end -->
    </body>
</html>


前回のソースから、テンプレートにボタンを配置して、ボタンが押されたらカウンターが+1されるようにした。
 ボタンID = btn_push  
 イベントID=evt_push





1つ注意しておいてほしいのが、render内にタグを書き換えた後にtriggerでcreateしている所。
これを行わないとjquery mobileのボタン装飾などがされなくなるので入れておく必要がある。



関連記事
jquery mobile + backbone サンプル(1)

2012年9月13日木曜日

jquery mobile + backbone サンプル(1)

jquery mobileを使っていろんな情報を見て覚えて使ってみて、やっぱまともじゃない。jqmは使えない!って何度も思ったことか。ほんとWeb周りの技術ってごちゃごちゃしてて苦難の道のりである。
  • jquery mobileをそのまま使ってみると、ページ遷移やajax周りで泣く。(過去記事参照)
  • なんとか普通に動くようになったからコード整理しようと思いテンプレートエンジンの導入を考える。
  • angularJSを使ってみるとjqmと相性が悪くて使えない。angularJS-jquery-adapterなるものを使ってみる。
  • イベント周りとかテンプレートが強力でほんとjqmバイパス感が強いけど使えるんだけど、adapter側の情報が少ないし、画面遷移周りでフリーズすることもあり次を探しに出る。
  • クライアントサイド側のMVCフレームワークで一番情報が豊富と書かれていたbackbone.jsに手を出してみる。←今ココ!

というわけで行き着いた先はbackbone.jsであった。
チュートリアルを見てもちんぷんかんぷんで、なんじゃこりゃー!と思いつつ1つ1つ解いていくことにする。

Backbone.JSって何?
 Javascriptで書かれているMVCフレームワークである。
フレームワークの構成はRouter、Model、Collect、Viewの4構成で出来ている。
各々の説明すると、

 ・ページ単位で切り分けるRouter、
 ・データ定義するModel 、
 ・複数のデータを管理するCollect、
 ・画面周りを担当するView

という感じになる。


次にフレームワークの基本的な流れ。

 ページアクセス
  ↓
 Routerで振り分け
  ↓
 Viewでページ作成
  ↓
 Collect、Model必要なデータを引っ張ってくる。
  ↓
 画面表示

MVCフレームワークを使った事がある人であれば馴染みやすいと思う。

またこの他に、
 HTMLを吐き出すテンプレート「underscore」
 画面のデコレーション担当「jquery mobile」

があって今回のサンプルプログラムで使ったコードを張っておく。


ここまで頭の中を文章に吐き出してみた。
あとは使って覚えて知識の補強を繰り返していけば、実用投入ができるかもしれない。
ちなみに、このbackbone.jsは軽量って謳ってるけど、軽量という言葉は最近の流行なのだろうか。
体重100kg軽量級とか軽量ってタイトルが付くと、一瞬だけ軽そうって気分になるけど。


準備するもの

 jQuery mobile + backboneするのに今回は以下のライブラリを用意する。

http://jquery.com/http://documentcloud.github.com/underscore/
https://github.com/douglascrockford/JSON-js
http://backbonejs.org/
http://jquerymobile.com/


いよいよ Hello, Bavkbone! を書く
 
ベースのHTMLコードはjquery mobile サイトのデザイナツールで作りました。
それに加えてbackboneのViewとunderscoreを今回使用してます。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>example</title>
        <link rel="stylesheet" href="./jquery.mobile-1.1.1/jquery.mobile-1.1.1.min.css" />
        <link rel="stylesheet" href="my.css" />
        <style>
        </style>
        <script src="underscore.js"></script>
        <script src="json2.js"></script>
        <script src="jquery-1.7.1.min.js"></script>
        <script src="backbone-min.js"></script>
        <script src="./jquery.mobile-1.1.1/jquery.mobile-1.1.1.min.js"></script>
        <script src="my.js"></script>
    </head>
    <body>
        <div data-role="page" id="page1">
            <div data-theme="a" data-role="header"><h3>sample1</h3></div>
            <div data-role="content" style="padding: 15px">
        <!-- content start -->
        <div id="myview"></div>
        <!-- content end -->
            </div>
            <div data-theme="a" data-role="footer" data-position="fixed"><h3></h3></div>
        </div>

    <!-- underscore template start -->
    <script type="text/template" id="hello-template">
        <h3><%= part1 %>, <%= part2 %>!</h3>
        </script>
    <!-- underscore template end -->

    <!-- backbone start -->
        <script>
            MyView = Backbone.View.extend({
            el: '#myview'
            ,initialize: function(){
                this.render();
            }
            ,render: function(){
                var data = { part1: 'hello', part2: 'backbone!' }
                var tmpl = _.template($("#hello-template").html(), data);
                this.$el.html(tmpl);
            }
        });

        var myview = new MyView();

    <!-- backbone end -->
    </script>
    </body>
</html>


簡単に解説

   タグで囲った部分が3ブロック構成、コンテンツ、テンプレート、backboneで出来てます。 上から、

    1.コンテンツ部分。
    動的にしたい部分divタグにidを付けたものを挿入します。
   2.テンプレート部分。
    動的に表示させるHTMLをunderscoreの記述にしたがって記述します。
   3.Backbone部分
     どういったデータを加工して表示させるのかを記述します。
     ・elが対応する1のdivタグのID
     ・Viewの定義、initializeと、renderを記述。


動作の説明





実行画面


 できたら最後にビューのインスタンスを作成して終えてます。
 これをやるとどうなるかというと、 Myviewのinitialize()が呼び出されてrender()を呼ぶ。
render()内で今回用意したデータとテンプレートを使ってHTMLを作って、 elにデータセットして終えてます。
 最初に説明したフローで説明すると、


 ページアクセス
 ↓
Routerで振り分け (スキップ)
 ↓
Viewでページ作成
 ↓
Collect、Model必要なデータを引っ張ってくる。 (スキップ)
 ↓
画面表示
だいぶ省略してますが、backboneのViewとテンプレートを使ったHTMLレンダリングの仕組みが見えてくると思う。

2012年9月10日月曜日

jquery mobile のページ切り替えの挙動(イラッ

とにかくajaxの挙動がおかしい!それでも使いたい!って方用にメモを残しておく。

ページ切替イベントに
http://dev.screw-axis.com/doc/jquery_mobile/api/events/page_transition/about/

pagebeforehide
pagebeforeshow
pagehide
pageshow
の4つがあると記載してあるけど、実はもう一つあって
ページ遷移先のdiv[data-role="page"]タグ内にある<script>タグがある。

scriptタグはページを読込んだ直後に実行されるので、
pagebeforchangeよりも早く実行される。
data-prefetchした場合は裏でajax通信した時に読込む。
これが一番の問題。

どういうことかというと、ページ切替前にscriptが解釈されてしまうので、
当然ページ切替後に反映される事はない。

リロードしたときはうまくいくのに、ページ移動だとうまく行かないっていうのは
ここが原因となっているので、
ウェジット回りのイベント処理などは、素直に

jquery mobileでイベント処理をやらない


これが対策。

jquery mobileでイベント処理をやらない

がとても重要。

  具体的には、
1.ページ切替などそういった処理はヘッダー内scriptに記述して、できるだけ
処理を入れないようにする。
2.ウェジット回りのイベントはhtml内のdiv[page]内scriptタグで記述して、
onclickタグやonchangeタグを使って呼び出すようにする。

これだけ制約すれば素直に動いてくれるはず。

2012年9月7日金曜日

jquery mobileの悪い所

ajaxをONにしてうまく使えないか試行錯誤してたらべからず集ができそうなくらい問題が出てきた。

・DOMキャッシュ機能をPOST先のページがおかしくなる。
・location.hrefだと履歴やハッシュが付かなくなって次表示するとき更新されない。
・ときどきページが巻き戻る。
・ときどきGUIが素の状態になる。
・ときどきscriptタグ内の処理がスキップする。
・ときどきブラウザがフリーズする。
・WEBアプリ側でリダイレクトするとものアウト。
・リダイレクトしちゃった場合はcontent内に正式なURLを埋め込まないとリダイレクト前のURLになる。
・contentタグ内のページ名はユニークにしないとだめ。
・ラジオボタンをクリックしたときとかchangepageでページ遷移できない。


 こんだけ問題に直面すると時間がいくらあっても足りないしストレスとカフェインの量が増えてくる。

.NETとかがものすごく洗練された開発環境だなあと思えてきた。

2012年9月5日水曜日

jquery mobileでajaxを使う。

右も左も知らないまま、jquery mobileを使ってajax通信を使って動的なページを作ろうと思ったときに陥った罠を紹介。

今日の問題テーマ
1.pageinitでsetIntervalを使って定期的にajax通信を行う仕組みを入れると、ページ切り替えた後もajax通信を行っている。
2.ページ遷移先のjavascript(pageinit部分)が処理されない。
3.jquery mobile側のページ遷移で使われてるajaxをoffにすれば解決してしまう。(悪くないがせっかくなので使いたい)
4.ページ内でajaxを使うと時々エラーが発生する。

 これくらいの内容を紹介。
ここからたんたんと書いておく。

 jquery mobileとは  
  基本的な部品とページ遷移するときのアニメーションなどを提供してくれるライブラリでバックエンドにはajaxが使われている。ajaxをオフにするとページ遷移時のアニメーションがしなくなるなどの制限が付く。 あと、jquery mobileを使って自分でもajax通信するときは、多重通信するとエラーが起きるのでそのへんの制御を作りこまないといけないし、ページ遷移前には通信してない状態にもっていかないとだめである。 イベントフック自体はjquery mobileに提供されているものを使うことがリファレンスでも書かれているので目を当しておくこと。

jquery mobileがajaxONだとどうなるの?
 最初に開いたページがベースとなるので、ヘッダ、ボディー共に読み込まれる。ページ遷移はajax通信でリンク先のページデータを読み込んで、ヘッダタグはスキップしてボディータグ内にある
タグで囲まれたページを読み込んで、ページを書き換えるついでにアニメーションをはさむようになる。OFFにした場合は通常のページ移動するのでページ遷移先のヘッダーとボディーを再度読み込む。


自分でもajax使う実装はどうやるの?
 ページ遷移先のDivタグ内にscriptタグを仕込んでおいてそこに自分でajax通信をするプログラムを書いてあげればよい。Divタグ内で完結するscriptタグなので、グローバル変数が他ページとかぶっても置き換わる事もないし、引き継がれない。もしグローバル変数を使うならdivタグで囲まれたページタグ以外のエリアにメモしておけばいいと思う。もし、定期タイマーでajax通信する場合は、pageshow、pagehideでタイマーのON/OFF制御してあげればアクティブなときだけ通信するようになる。ちなみにjquery mobile公式ページにある画面レイアウト作成できるのでソースをエクスポートした場合、scriptタグがdivタグの外側にはみ出してるので厳重に注意しておくこと。

ここまでで1~4の解決策は書けた。
最後に、jquery標準のreadyじゃなくpageinitを使え!ってリファレンスに書かれてるからどこの記事でもpageinitで解決って感じに書かれているけど著者の意図通りに使われてないケースをよく見かける。
そもそもpageinitを使うケースは共通の初期化を行う時くらいなもんだろう。delegateとかするときはinitで、より使うほうはページ切り替え時の処理がメインなんじゃないかなと思う。でもまあ、このあたりは必要とされる技術を適材適所に使っていればいいので、どういうイベントがあるのかは頭の隅にでも覚えておくと良いと思います。

あとは全体の実装方法なんだけど、すっきりしたやり方がまだまとまってないので今日はここまで。

Androider