Spine.jsを使用した複雑なインターフェイスの作成

Spine.jsの概要



SpineはMVCスキームで作業できる小さなフレームワークであり、JavaScriptコードでアプリケーションを直接作成します。これにより、論理的なコード分離、クラスおよび拡張機能によるモデルの継承が可能になります。 また、このツールは主にBackbone.js APIに基づいているため、このフレームワークを扱った開発者はSpineを簡単に理解できます(ただし、多くの重要な違いがあります)。 Spine.jsは、HTML5および非同期サーバーリクエストと連携して機能します。



毎日JavaScriptフレームワークが登場しています。 それでは、Spine.jsが特別な理由は何ですか?



サンプルアプリケーションのソースコードを見て、自分で決めてください。



脊椎のクラス、モデル、およびビュー



Spineの公式ドキュメントには、これまで見た中で最も包括的なガイドが含まれています。 そこには多くのものが含まれています:検証、シリアル化、およびチップ全体の処理。 ただし、このレッスンの目的は、 クラスモデル、 ビューという3つの最大の機能を理解することです。







クラス



Spineの中心部では、Object.createのエミュレーションを使用して、オブジェクトが動的に作成され、スクリプトの実行中に使用できるようにします。 このようなクラスの使用は、次の例で見ることができます。



var twitterClient = Spine.Class.create(); //or var twitterClientWithArgs = Spine.Class.create({ testMessage: "If it weren't for WebMD I would have never known what symptoms to mimic so I could get all these prescriptions from my doctor." });
      
      





クラスを初期化するには、init()メソッドを使用します。 「新しい」キーワードを使用すると、クラスをインスタンス化するときに問題が発生する可能性があるため、Spine開発者はコンストラクタ関数を使用しないことにしました。



 var twitterClient = Spine.Class.create({ testMessage: "Hello world" }); var myclient = twitterClient.init();
      
      





オブジェクトの初期化時に使用するすべてのパラメーターは、init()メソッドを介して渡す必要があります。 例:



 var twitterClient = Spine.Class.create({ init:function(testMessage){ this.testMessage = testMessage; } });
      
      





モデル



Spineでは、アプリケーションデータを保存するため、およびこのデータに関連付けられている他のロジックのために、モデルが使用されます。 このアイデアに固執する必要があります。 これは、MVC上に構築されるアプリケーションの要件の1つです。 モデル関連データはModel.recordsレコードに保存され、Spine setup()関数を使用して作成できます。



次の例では、モデル名と属性セットをsetup()メソッドに設定します。



 var Tweets = Spine.Model.setup("Tweet", ["username","tweet_message"]);
      
      





モデルの機能は、次のように特定のクラスのプロパティを使用して拡張できます。



 Tweets.include({ toTweetString: function(){ return("@" + this.username + " " + this.tweet_message); } });
      
      





モデルの作成は、同じ単純な.init()メソッドです。



 var mytweets = Tweets.init({username: "addyosmani", tweet_message: "hai twitter"});
      
      





SpineおよびBackboneには、テンプレートをレンダリングしてDOMモデルに埋め込むためのさまざまなオプションがあります。



コントローラー



SpineコントローラーはSpine.Classを拡張し、そのすべてのプロパティも継承します。 Spineでコントローラーを作成する例:



 var TweetController = Spine.Controller.create({ init:function(){ //initial logic on instantiation } })
      
      





コントローラの初期化は次のとおりです。



 var myTweetController = TweetController.init();
      
      





各コントローラーには「el」と呼ばれる特別な要素があり、インスタンスプロパティを介して渡すことができます。 例:



 var myTweetController = TweetController.init({el: $('#tweets')});
      
      





ドキュメント



クラスモデル、 コントローラーの詳細については、Spineのドキュメントをご覧ください。



SpineとBackboneの主な違い



BackboneとSpineのドキュメントを最初の数分で読む開発者は、根本的な違いを見つけることができません。 ただし、実際のプロジェクトでは、これらの違いは単独で現れる場合があります。



1.バックボーン表現は、アプリケーションの従来のコントローラーに似ており、バックボーンコントローラーはURLルーティングの処理を担当します。 Spineでは、ルーティングサポートが最近追加され(非常に必要な要素であるため)、コントローラーはBackboneのビューに非常に似ています。



2. Backboneはコンストラクター関数とプロトタイプを使用しますが、SpineはObject.createのエミュレートバージョンとシミュレートされたクラスシステムを使用します。これにより、同じ継承効果を実現でき、実際に非常に興味深いトリックです。 これは、Backboneとの基本的な違いの1つです。 どちらのアプローチにも存在する権利があります。



3.多くの開発者は、1つまたは別のライブラリのファイルサイズの違いに注意を払っています。この点で、Spineには、Backboneに含まれるマッピング、フィルタリング、および他の多くの機能が含まれません。 サイズが重要な場合は、スパインを選択する必要があります。 この点で、彼はあらゆる点で勝ちます。



実際のSpine.js



例:Bit.lyクライアント










SPAで作業する場合、外部データ(これは独自のデータまたは一部のAPIから受信したデータである可能性があります)を操作し、対話するのに多くの時間がかかります。 また、ルーティングを使用して、アプリケーションの状態を保存することもできます。 これを行うには、localStorageを使用し、ajaxリクエストを処理する必要があります。



上記のすべてを考慮して、次のことを可能にするbit.lyクライアントを作成します。





背景



bit.lyプラグインの作成


始める前に、bit.lyサービスにアクセスする良い方法を見つける必要があります:1.短縮URLと2.クリック統計。 通常のJavaScriptに悩まされる代わりに、より便利で高速な方法でajaxリクエストを処理するためにjQueryを使用します。 このアプローチにより、より読みやすく理解しやすいアプリケーションを作成することもできます。



store.jsの追加サポート


デフォルトでは、Spineは最新のブラウザーに焦点を合わせているため、localStorageなどの機能はすべてのブラウザーで同等に機能するわけではないため、ブラウザー間の互換性が必要な場合は、古いツールを使用する必要があります。



ただし、この問題はstore.js(およびjson2.jsに基づくもの)を使用して解決できます。 以下はspine.model.local.jsファイルの内容です。このファイルを更新して、以下でマークされた行にコメントを付けて独自のものに置き換えることにより、リポジトリを使用することができます。



 Spine.Model.Local = { extended: function(){ this.sync(this.proxy(this.saveLocal)); this.fetch(this.proxy(this.loadLocal)); }, saveLocal: function(){ var result = JSON.stringify(this); //localStorage[this.name] = result; store.set(this.name, result); }, loadLocal: function(){ //var result = localStorage[this.name]; var result = store.get(this.name); if ( !result ) return; var result = JSON.parse(result); this.refresh(result); } };
      
      





jQueryテンプレートの処理


SpineおよびBackboneフレームワークは、いくつかのアプローチ(マイクロテンプレート処理、mustache.jsなど)と対話できます。 どちらを使用するかを選択します。 この例では、jQuery tmplプラグインを使用して、短縮URLエントリを提示し、テンプレートを使用して統計情報をクリックします。



開発



実装する必要があるもののリスト:





この例では、テンプレートとプラグインを操作するのに理想的であるため、 jQueryを使用していますが、SpineはZeptoまたは他のJavaScriptライブラリも使用できます。 それでは、アプリケーションのコードを見てみましょう。



初期キャッシュ



 var exports = this;
      
      







シンプルなjQueryプラグイン



 $.fn.toggleDisplay = function(bool){ if ( typeof bool == "undefined" ) { bool = !$(this).filter(":first:visible")[0]; } return $(this)[bool ? "show" : "hide"](); };
      
      





URLモデル:



 var Url = Spine.Model.setup("Url", ["short_url", "long_url", "stats"]); Url.extend(Spine.Model.Local); Url.include({ validate: function(){ if ( !this.long_url ) return "long_url required" if ( !this.long_url.match(/:\/\//)) this.long_url = "http://" + this.long_url }, fetchUrl: function(){ if ( !this.short_url ) $.bitly(this.long_url, this.proxy(function(result){ this.updateAttributes({short_url: result}); })); }, fetchStats: function(){ if ( !this.short_url ) return; $.bitly.stats(this.short_url, this.proxy(function(result){ this.updateAttributes({stats: result}); })); } }); Url.bind("create", function(rec){ rec.fetchUrl(); });
      
      





Export.Urlsコントローラー:



 exports.Urls = Spine.Controller.create({ events: { "click .destroy": "destroy", "click .toggleStats": "toggleStats" }, proxied: ["render", "remove"], template: function(items){ return $("#urlTemplate").tmpl(items); }, init: function(){ this.item.bind("update", this.render); this.item.bind("destroy", this.remove); }, render: function(){ this.el.html(this.template(this.item)); return this; }, toggleStats: function(){ this.navigate("/stats", this.item.id, true); }, remove: function(){ this.el.remove(); }, destroy: function(){ this.item.destroy(); } });
      
      





Export.UrlsListコントローラー:



 exports.UrlsList = Spine.Controller.create({ elements: { ".items": "items", "form": "form", "input": "input" }, events: { "submit form": "create", }, proxied: ["render", "addAll", "addOne"], init: function(){ Url.bind("create", this.addOne); Url.bind("refresh", this.addAll); }, addOne: function(url){ var view = Urls.init({item: url}); this.items.append(view.render().el); }, addAll: function(){ Url.each(this.addOne); }, create: function(e){ e.preventDefault(); var value = this.input.val(); if (value) Url.create({long_url: value}); this.input.val(""); this.input.focus(); } });
      
      





コントローラーのエクスポート統計:



 exports.Stats = Spine.Controller.create({ events: { "click .back": "back" }, proxied: ["change", "render"], init: function(){ Url.bind("update", this.render); }, template: function(items){ return $("#statsTemplate").tmpl(items); }, render: function(){ if ( !this.item ) return; this.el.html(this.template(this.item)); }, change: function(item){ this.item = item; this.navigate("/stats", item.id); this.item.fetchStats(); this.render(); this.active(); }, back: function(){ this.navigate("/list", true); } });
      
      





Export.UrlAppコントローラー:



 exports.UrlApp = Spine.Controller.create({ el: $("body"), elements: { "#urls": "urlsEl", "#stats": "statsEl" }, init: function(){ this.list = UrlsList.init({el: this.urlsEl}); this.stats = Stats.init({el: this.statsEl}); this.manager = Spine.Controller.Manager.init(); this.manager.addAll(this.list, this.stats); this.routes({ "": function(){ this.list.active() }, "/list": function(){ this.list.active() }, "/stats/:id": function(id){ this.stats.change(Url.find(id)) } }); Url.fetch(); Spine.Route.setup(); } });
      
      





最後に、コントローラー「アプリ」の初期化を完了するために:



 exports.App = UrlApp.init();
      
      





Bit.lyのURL削減およびクリック統計のコード



 (function($){ var defaults = { version: "3.0", login: "legacye", apiKey: "R_32f60d09cccde1f266bcba8c242bfb5a", history: "0", format: "json" }; $.bitly = function( url, callback, params ) { if ( !url || !callback ) throw("url and callback required"); var params = $.extend( defaults, params ); params.longUrl = url; return $.getJSON("http://api.bit.ly/shorten?callback=?", params, function(data, status, xhr){ callback(data.results[params.longUrl].shortUrl, data.results[params.longUrl], data); }); }; $.bitly.stats = function( url, callback, params ) { if ( !url || !callback ) throw("url and callback required"); var params = $.extend( defaults, params ); params.shortUrl = url; return $.getJSON("http://api.bitly.com/v3/clicks?callback=?", params, function(data, status, xhr){ callback(data.data.clicks[0], data); }); }; })(jQuery);
      
      





アプリケーションインデックス/ HTML:



LABjsを使用してアプリケーションを制御しますが、これを操作に慣れているものに簡単に置き換えることができます。



 <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="css/application.css" type="text/css" charset="utf-8"> <script src="lib/LAB.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $LAB .script("lib/json.js") .script("lib/jquery.js") .script("lib/jquery.tmpl.js") .script("lib/jquery.bitly.js") .script("lib/store.min.js") .script("lib/spine.js") .script("lib/spine.model.local.js") .script("lib/spine.controller.manager.js") .script("lib/spine.route.js") .script("app/models/url.js") .script("app/application.js"); </script> <script type="text/x-jquery-tmpl" id="urlTemplate"> <div class="item"> <div class="show"> <span class="short"> ${long_url} </span> <span class="long"> {{if short_url}} <a href="${short_url}">${short_url}</a> {{else}} Generating... {{/if}} </span> <a class="toggleStats"></a> <a class="destroy"></a> </div> </div> </script> <script type="text/x-jquery-tmpl" id="statsTemplate"> <div class="stats"> <a class="back">Back</a> <h1>Click Statistics</h1> <h1 class="longUrl">${long_url}</h1> <p>Short URL: {{if short_url}} <a href="${short_url}">${short_url}</a> {{else}} Generating... {{/if}} </p> {{if stats}} <p>Global clicks: ${stats.global_clicks}</p> <p>User clicks: ${stats.user_clicks}</p> {{else}} Fetching... {{/if}} </div> </script> </head> <body> <div id="views"> <div id="urls"> <h1>Bit.ly Client</h1> <form> <input type="text" placeholder="Enter a URL"> </form> <div class="items"></div> </div> <div id="stats"> </div> </div> </body> </html>
      
      





注:





以上です!



結果

ソースコード



おわりに



SpineはBackboneの優れた代替手段です。 ドキュメンテーションは、自分で続けるのにとても良いです。



使用した材料:




All Articles