流星。 TODOリストの作成

このレッスンでは、特にそうは思わないので、なぜ流星がWebのキラーであるのかについては説明しませんが、このフレームワークには特定の同情があります。 したがって、アプリケーションを開発するときにどこから始めることができるか、どのパッケージが存在するか、一般的にこの流星が何であるかを示したいと思います。



すぐに言いたいのは、Webアプリケーションの開発にあまり経験がないことです。 私はこれを約2年しかやっておらず、数か月間しか流星を知らなかった。



また、レッスンは非常に膨大であることが判明しましたが、その中のコードはテキストよりも何倍も書かれていなかったことに注意してください。 単純な例を作成するときではなく、流星の使用方法に関する私の経験を共有し、重要だと考えたさまざまなポイントに焦点を当てたいと思います。 したがって、このレッスンでは、開発プロセスを促進する多くのサードパーティパッケージを使用します。



もう1つの警告:このレッスンでは、次のテクノロジーを使用して例を直接記述します。





レッスン中に受け取ったアプリケーションを示すビデオ







そしてまだ興味がある人は、猫にようこそ。





Meteorをインストールする

流星自体はnodejs



mongodb



基づいており、流星はWindowsをサポートしていません。流星を感じる場合は、LinuxまたはMacOSオペレーティングシステムを入手する必要があります。



最初に、 nodejsmongodbをインストールします



次のステップは、流星をインストールすることです。 npmリポジトリにはないため、 npm install -g meteor



を急いでコマンドをnpm install -g meteor



必要はありません。この場合、古いバージョンのみがロードされます。適切にインストールするには、コンソールで実行する必要があります。



 $ curl https://install.meteor.com/ | sh
      
      







プロジェクト作成

流星をインストールした後、すぐにコマンドを実行できます



 $ meteor create 'todo-list' todo-list: created. To run your new app: cd todo-list meteor $ cd todo-list $ meteor [[[[[ ~/dev/meteor-getting-started/todo-list ]]]]] => Started proxy. => Started MongoDB. => Started your app. => App running at: http://localhost:3000/
      
      







この結論は、すべてがうまくいったことを意味し、HelloWorldはブラウザで確認できます。



こんににちは



これで、新しいプロジェクトの操作性を確認した後、プロジェクトのルートにあるファイルを削除できます。これらは特に興味深いものではありません。 また、 .meteor



ディレクトリが作成され、プロジェクトに関するさまざまなサービス情報、さらには自動的に生成された.gitignore



も保存されていることに気付くことができます。 ところで、手動でパッケージを管理するためにpackages



ファイルを変更できますが、コンソールユーティリティも非常に便利です。



私と同じ結果が得られれば、meteorプロジェクトを開発するための最小限の環境は準備できています。何かがおかしい場合は、 nodejs



mongodb



meteor



インストールを確認してください。たとえば、コンピューターに次の構成があります:



 $ node -v v0.10.33 $ mongod --version db version v2.4.12 $ meteor --version Meteor 1.0
      
      







これで、手続きを完了し、当社のtuduシートの開発に進むことができます。 便宜上、新しいコンソールタブを開くことをお勧めします。これは、Meteorアプリケーションを再起動する必要がなくなったためですが、フレームワークのコンソールインターフェースを使用してパッケージをインストールします。



パッケージ

繰り返しになりますが、私のパケットマネージャーが流星で使用されている理由と、なぜ彼らがそのような自転車を好むのかについては説明しません。レッスンとは関係ありません。



パッケージのインストールは次のコマンドで行われます



 $ meteor add <package-name>
      
      







上で書いたように、 less



jade



coffeescript



でアプリケーションを開発します。つまり、それらをインストールする時です。 レッスンで使用されるすべてのパッケージとその他のパッケージは、 Atmosphere Webサイトで見つけることができます。 実際には、パッケージの名前:





sourcemap



サポートはless



およびcoffeescript



sourcemap



組み込まれているため、ブラウザーでのsourcemap



プロセスは簡単です。 sourcemap



meteor自体でサポートされます。この機能を接続するために必要なAPIを提供するため、特に設定する必要はありません。



開発の過程で、より人気のあるパッケージをいくつか追加し、それぞれの目的を説明していきます。 ところで、他の多くのパッケージと同様に、 jquery



underscore



すでに流星に含まれています。完全なリストは、作成されたプロジェクトのファイル./.meteor/versions



にあります。



アプリケーション構造

さて、私の意見では、流星がどのようにファイルをプロジェクトに接続するのか、そしてこれをどのように規制するのかを理解する時が来ました。 ここで、テンプレート、スタイル、およびスクリプトをコンパイルするために、 grant



またはgulp



構成ファイルを作成する必要はありません。流星はすでにこれを処理しました。 足場には、Yeomanのプロジェクトがありますが、すべてを手動で作成するのが好きです。 前のプロジェクトでは、おおよそ次のフォルダー構造を使用しました。



 todo-list/ -   ├── client -      │ ├── components -       │ │  ,    │ ├── config -   │ ├── layouts -  ,     │ ├── lib -  ,     │ │  │ ├── routes -   │ └── styles -  ├── collections -     ├── lib - ,     ├── public - : , robots.txt    ├── server -      │ ├── methods -    ,  , │ │   │ ├── publications -     │ ├── routes -  ,    │ │  http  │ └── startup -  
      
      







おそらく何かが私たちにとって有用ではないかもしれませんが、いずれにしても、流星はフォルダとファイルの命名に制限がないので、あなたにとって便利な構造を思いつくことができます。 ニュアンスのいくつかについて覚えておくべき主なこと:





たとえば、プロジェクトがブラウザにどのように読み込まれるかを考えてみましょう:最初に、プロジェクトルートのlib



ディレクトリからファイルがダウンロードされ、次にclient



フォルダーが処理され、 lib



からのファイルが最初に読み込まれ、次にアルファベット順にロードされcomponents



components



> config



> ...-> styles



そして、すでにcollections



フォルダーのファイルの後に。 public



フォルダーとserver



フォルダーのファイルはブラウザーにアップロードされませんが、たとえば、 public



フォルダーに格納されているscript



は、他のプロジェクトで使用されているように、 script



タグを介して接続できますが、フレームワークの開発者はこのアプローチを推奨しません。



次の構成体を使用して、共有ファイルのランタイムを調整することもできます。



 if Meteor.isClient # ,     if Meteor.isServer # ,    
      
      







また、スクリプトの実行時間を調整するには、 Meteor.startup(<func>)



メソッドを使用します。ブラウザーではjQuery



ライブラリの$



関数に類似しており、サーバーでは、これらのファイルがダウンロードされた順序ですべてのスクリプトを読み込んだ直後にこの関数のコードが実行されます これらの変数とメソッドの詳細をご覧ください



基本的なアプリケーションテンプレート

レイアウトにはBootstrapを使用しますが、彼はすべてにうんざりしていることは知っていますが、私はタイプセッターではなく、ブートストラップにはあまり慣れていません。



これを行うには、 mizzao:bootstrap-3



パッケージをインストールしますmizzao:bootstrap-3



これは他の中で最も人気があり、使用に問題はないと思います。



次に、 client/layouts



head.jade



ファイルを作成しhead.jade



。 これは、テンプレート形式を持たないアプリケーション内の唯一のファイルになります。つまり、ページヘッダーを作成するだけで、後でテンプレートが何であるかを分析します。



 //- client/layouts/head.jade head meta(charset='utf-8') meta(name='viewport', content='width=device-width, initial-scale=1') meta(name='description', content='') meta(name='author', content='') title Meteor. TODO List.
      
      







ブラウザを開いて、ファイルを追加した後、ページに指定されたタイトルがあることを確認できます。



構成を始める前に、クライアントルーティングの基本的なセットアップを行うことを提案します。少し後で、この点をより詳細に分析します。 ルーティングには、必要なすべての機能を備えた一般的なソリューションを使用できます。 iron:router



パッケージ( リポジトリ )をインストールします



client/config



ディレクトリにインストールした後、次の内容のrouter.coffee



ファイルを作成します。



 # client/config/router.coffee Router.configure layoutTemplate: "application"
      
      







明らかに、ここではアプリケーションの基本テンプレートを設定しapplication



。これはapplication



と呼ばれapplication



。 したがって、 layouts



フォルダーにapplication.jade



ファイルを作成します。 このファイルでは、アプリケーションのアセンブリの段階でjavascript



コードに変わるテンプレートであるエンティティを記述します。 ちなみに、流星は独自の口ひげを生やしたテンプレートspacebars



blaze



ライブラリを使用します。



要するに、テンプレートを処理するプロセスは次のとおりです(ドキュメントからわかる限り)。 spacebars



テンプレートspacebars



、後でDOM



直接動作するblaze



ライブラリblaze



コンパイルされDOM



。 プロジェクトの説明には、他の一般的なライブラリとの比較があります。





図書館の公式説明から段落を実際に翻訳したので、何かに同意しない場合は石を投げないでください。 私自身はこれらの技術に出くわしました( ember



を除く)。そして、原則として、私は図書館の著者に同意します。炎のマイナス面については、流星の結びつきに気づきたいです。



しかし、プロジェクトでは、 blaze



spacebars



も明示的に使用しません。 jade



テンプレートの場合、コンパイルプロセスの順序は、 jade



-> spacebars



-> blaze



です。



流星内のすべてのパターンはtemplate



タグで記述されます。 template



タグには、テンプレートの名前を持つ属性が必要です。 ルーターの設定でlayoutTemplate: "application"



を指定したことを思い出してください。ここでapplication



はテンプレートの名前です。



流星のパターンが何であるかを理解しているようです。ページのフレームを作成する時が来ました。ヘッダーとフッターで構成されます。



 //- client/layouts/application.jade template(name='application') nav.navbar.navbar-default.navbar-fixed-top .container .navbar-header button.navbar-toggle.collapsed( type='button', data-toggle='collapse', data-target='#navbar', aria-expanded='false', aria-controls='navbar' ) span.sr-only Toggle navigation span.icon-bar span.icon-bar span.icon-bar a.navbar-brand(href='#') TODO List #navbar.collapse.navbar-collapse ul.nav.navbar-nav .container +yield .footer .container p.text-muted TODO List, 2014.
      
      







ここでは、ミックスイン、javascript、インクルージョンなど、これが私たちによく知られていないことを理解する必要があります。 Jade



spacebars



テンプレートにコンパイルする必要があり、これにはいくつかの機能があります。 jade



から、構文のみを使用し、残りは必要ないと言うことができます。 このテンプレートでは、 +yield



構文が使用されます。この構文はyield



パターンが代わりにレンダリングされることを意味します。これはiron:router



機能ですiron:router



、パスに応じて目的のパターンを自動的に置き換えます。少し後でルーターを処理し、レイアウトに外観を変更できるようになりました。結果を見てください。



 // client/styles/main.less html { position: relative; min-height: 100%; } body { margin-bottom: 60px; & > .container{ padding: 60px 15px 0; } } .footer { position: absolute; bottom: 0; width: 100%; height: 60px; background-color: #f5f5f5; .container .text-muted { margin: 20px 0; } }
      
      







ちなみに、スタイルを変更する場合、ブラウザでページを更新する必要はありません。ファイルを保存するだけですぐに適用されます。流星のレイアウト設計者向けの便利なツールがあります。



my_helloworld



ルーティング

流星自体に標準のルーティングメカニズムはありません。 iron:router



パッケージを使用することをお勧めしiron:router



十分に文書化され 、積極的にサポートされ、豊富な機能があり、流星のコンテキストで最も人気のあるルーティングソリューションでもあります。



このライブラリをサーバールーティングに使用することもできます。 たとえば、実際のプロジェクトでは、ユーザーを認証するためにこれが必要でした。メインプロジェクトはRuby on Rails



で作成され、ユーザーはこれらが2つの異なるアプリケーションであると考えてログインする必要がないからです。 一般に、流星用のサーバールーティングおよびREST APIの作成には、いくつかの一般的なアプローチがあります。



このライブラリがどのように機能し、どのような機能を備えているかを例として確認するために、基本的なルーターを作成します。その後、主な機能をそれらに掛けます。



まず、ページへのリンクを設定しましょう。



 //- client/layouts/application.jade //- ... #navbar.collapse.navbar-collapse ul.nav.navbar-nav li a(href='/') Home li a(href='/about') About
      
      







これらが単なるスタブになるまで、クライアントルーターフォルダーにコントローラーを作成します。



 # client/routes/home.coffee Router.route '/', name: 'home' class @HomeController extends RouteController action: -> console.log 'Home Controller' super() # client/routes/about.coffee Router.route '/about', name: 'about' class @AboutController extends RouteController action: -> console.log 'About Controller' super()
      
      







2つのパラメーターをRouter.route



関数に渡す必要があります。最初のパラメーターはパスであり、パスはパターンにすることができます(例: /:user/orders/:id/info



)。パターンのすべてのパラメーターはparams



プロパティを介してコントローラーオブジェクトで使用できます。 2番目のパラメーターは、オプションを持つオブジェクトです。 パスと名前の簡単な説明からすべてのロジックを<RouteName>Controller



ために、コントローラーを作成できます。この場合、これらはシンプルなスタブです。ここでは、デフォルトでiron:router



<RouteName>Controller



という名前のコントローラーを見つけようとするため、プロパティでコントローラーの名前を明示的に指定しませんもちろん、コントローラーはグローバルにアクセスできる必要があります。コーヒースクリプトでは、変数を現在のコンテキストにバインドすることでこれを行います。通常のjsでは、変数をvar



介さずに宣言するだけで十分です。



ちなみに、流星では使用されません。たとえば、 amd



はコードをダウンロードしますが、ファイルは特定の順序で単純にダウンロードされます。 したがって、異なるファイルに記述されているモジュール間のすべての対話は、グローバル変数を介して実行されます。 私にとっては、これは非常に便利であり、コーヒーを使用する場合、誤ってグローバル変数を宣言することは非常に難しく、すぐに気付くでしょう。



iron:router



は、テンプレートをルートの名前で自動的にレンダリングしようとします(ただし、テンプレートは明示的に指定することもできます)、それらを作成します



 //- client/components/home/home.jade template(name='home') h1 Home //- client/components/about/about.jade template(name='about') h1 About
      
      







ブラウザを開いて、ヘッダーのリンクをクリックしてルーティングが機能することを確認できます。 そして、ページを更新せずに動作します。



base_routing



このレッスンを開発する過程で、プレゼンテーションのシーケンスに従ってリポジトリのコードにすべての変更を加え、投稿の一部をスキップできるようにプロセス全体をフォローできるようにします。 リポジトリ



ここでは、最後に何が起こったのかを見ることができ、 ここでは、現在の状態のプロジェクトコードを見ることができます。



ユーザーと認証

当社に来る多くの技術的なタスク、最初のタスクはユーザーシステムを説明します。 これはかなり一般的なタスクなので、特に流星がこのための標準ツールを提供するため、レッスンではユーザー認証の方法を検討する必要があると考えています。



メカニズムについては詳しく説明しませんが、ログイン/パスワードまたはgoogle



およびgithub



サービスを介してユーザーを作成できる既製のソリューションを使用します。 私はomniauth



いくつかのジェネレーターと設定のomniauth



、Rails omniauth



omniauth



devise



omniauth



ています。 そのため、流星は、これをすぐに使用できるようにするだけでなく、サービスの構成も可能な限り簡単になります。



次のパッケージをインストールします。





ian:accounts-ui-bootstrap-3



パッケージを使用すると、アプリケーションに認証/登録フォームを1行で追加できるだけでなく、サードパーティサービスを構成するためのインターフェイスを提供できます。 プロジェクト自体には、フォームの統合とサービスの構成がどのように見えるかについての小さなドキュメントとスクリーンショットがあります。



帽子を変更します



 //- client/layouts/application.jade //- ... #navbar.collapse.navbar-collapse ul.nav.navbar-nav li a(href='/') Home li a(href='/about') About ul.nav.navbar-nav.navbar-right //-     //-    ian:accounts-ui-bootstrap-3 +loginButtons
      
      







そして、次の結果が得られます



base_auth_form





構成後、認証トークンがデータベースに保存されていることを確認できます。



 $ meteor mongo MongoDB shell version: 2.4.9 connecting to: 127.0.0.1:3001/meteor meteor:PRIMARY> show collections meteor_accounts_loginServiceConfiguration meteor_oauth_pendingCredentials system.indexes users meteor:PRIMARY> db.meteor_accounts_loginServiceConfiguration.find() { "service" : "github", "clientId" : "<id>", "secret" : "<secret>", "_id" : "AjKrfCXAioLs7aBTN" } { "service" : "google", "clientId" : "<id>", "secret" : "<secret>", "_id" : "HaERjHLYmAAhehskY" }
      
      







電子メールアドレスの検証を設定したいので、ユーザーシステムを設定します。あなたはsmtp



を設定する必要がありemail



。ところで、電子メールパッケージはemail



送信に使用されemail



。 標準の流星セットには含まれていないため、メールで作業する必要がある場合は手動でインストールする必要があります。



 # server/config/smtp/coffee smtp = username: "meteor-todo-list@yandex.ru" password: "meteor-todo-list1234" server: "smtp.yandex.ru" port: "587" #   _(smtp).each (value, key) -> smtp[key] = encodeURIComponent(value) #  url   smtp url = "smtp://#{smtp.username}:#{smtp.password}@#{smtp.server}:#{smtp.port}" #   ,       process.env.MAIL_URL = url
      
      







そして、流星が電子メールアドレスの確認を求めるようにアカウントを設定します。



 # server/config/accounts.coffee emailTemplates = from: 'TODO List <meteor-todo-list@yandex.ru>' siteName: 'Meteor. TODO List.' #      _.deepExtend Accounts.emailTemplates, emailTemplates #   Accounts.config sendVerificationEmail: true #       Accounts.onCreateUser (options = {}, user) -> u = UsersCollection._transform(user) options.profile ||= {} #   ,        #        options.profile.emailHash = Gravatar.hash(u.getEmail() || "") #  ,     options.service = _(user.services).keys()[0] if user.services #      , #     _.extend user, options
      
      







このアプリケーションでは、微調整が必​​要なため、複数のサービスを1つのアカウントに接続することはできません。 おそらく、この瞬間は流星で解決されるでしょうが、これまでのところ、 mondora:connect-with



に対するmondora:connect-with



、多かれ少なかれ通常のソリューション、 mondora:connect-with



がありますが、それはまだ生です。 アカウントを自分で維持することができますが、これは複雑なことではなく、ネットワークには多くの例や他のソリューションがあります: onetwothree



アカウントの詳細なドキュメントもあります。パッケージを置いて魔法を見るだけですが、内部ではそれほど難しくありません。



アカウントシステムを表面的に調べたので、あまり私を蹴る必要はありません。複雑なものは何もないことを示したかっただけです。詳細なレビューには別の投稿が必要です。そして、レッスンでは、必要な基本機能を作成し、最終結果に進みます。



次のステップはユーザーのページを処理することですが、それを破る前に、流星にいくつかのことを実装する方法を考慮する必要があります。



コレクション、出版物、購読。

プロジェクトを作成するときに、自動的に二つのパッケージが追加されているautopublish



insecure



、彼らは、データベース内のすべてのコレクションへのユーザー無制限のアクセスを提供し、そして唯一のプロトタイピングのために使用することができるようになりました、それらを取り除くための時間です。パケットはコマンドによって削除されます



 $ meteor remove <package-name>
      
      







コレクション



隕石中のコレクションがモングのコレクションと比較することができ、実際に彼らは彼らと仕事と同じであり、彼らはまた、メソッドを持っているfind



insert



update



upsert



(集約はパッケージを使用して、サーバー上で整理することができますzvictor:mongodb-server-aggregation



)。作成済みのコレクションの1つで、Meteor.users



たとえば、ブラウザコンソールで実行してみてくださいMeteor.users.findOne()



。ここで重要なのは、すべてのコレクションデータがブラウザにキャッシュされることです。クライアントMeteor.users.find(options).fetch()



ループ内で100万回実行すると、ブラウザ以外はロードされません。これはminimongo



、クライアントに渡されるパラメーターに応じて選択を行うのに十分スマートなライブラリー使用して実現されます。



裸のデータを扱うのはあまり楽しいことではありません。コレクションのオブジェクトにビジネスロジックを追加したいと思います。これはコレクションの機能を使用_transform



して実行できます。サーバーからオブジェクトを受け取った後、オブジェクトを転送します、packageを使用できますdburles:collection-helpers



。これにより、コレクションhelpers



メソッドが追加され、すべてのデータを継承するオブジェクトを渡すことができます。



パッケージをインストールし、ユーザー情報を更新するためのメソッドを作成します。また、ユーザーを作成するときに、Gravatarサービスにユーザーのアバターのハッシュを含む計算フィールドを追加しました-いくつかのパラメータを使用して画像へのリンクを返すことができるメソッドを追加します。また、ユーザー登録サービスをチェックするメソッドと、さまざまな公開情報を返すメソッドを追加します。



 # collections/users.coffee Users = Meteor.users #     _.extend Users, #      allowFieldsForUpdate: ['profile', 'username'] #       Users.helpers #   ,      update: (data) -> Users.update @_id, data #   ,      #      set: (data) -> d = {} f = _(Users.allowFieldsForUpdate) for key, value of data when f.include(key) d[key] = value @update $set: d #      , #         #     merge: (data) -> current = @get() @set _.deepExtend(current, data) #    ,    , #      get: -> r = {} r[key] = @[key] for key in _(@).keys() r #     getEmails: -> p = [@profile?.email] s = _(@services).map (value, key) -> value?.email e = _(@emails).map (value, key) -> value?.address _.compact p.concat(e, s) #   getEmail: -> @getEmails()[0] #   getUsername : -> @username || @_id getName : -> @profile?.name || "Anonymous" getPublicEmail : -> @profile?.email urlData: -> id: @getUsername() #    ,     #       getAvatar: (size) -> size = Number(size) || 200 options = s: size d: 'identicon' r: 'g' hash = "00000000000000000000000000000000" if email = @getPublicEmail() hash = Gravatar.hash(email) else hash = @profile?.emailHash || hash Gravatar.imageUrl hash, options #      isFromGithub: -> @service == 'github' isFromGoogle: -> @service == 'google' isFromPassword: -> @service == 'password' #     #     isEditable: -> @_id == Meteor.userId() #   @UsersCollection = Users
      
      







流星内のコレクションを把握したようです。コレクション内のすべてのデータはリアクティブであり、データベース内のレコードが変更されると、メモリ内のどこかに保存され、モデルオブジェクトの関連性が失われ、後続の作業が失われるため、モデルに状態を保存することは望ましくないことに注意してください古くなったデータを使用するようになる可能性があります。後の例では、モデルの操作方法を検討します。



刊行物



データベースに3つのユーザーレコードを作成しました



 $ meteor mongo meteor:PRIMARY> db.users.count() 3
      
      







そして、ブラウザでデータを取得しようとしたときに、認証なしの単一のレコードと、それ以外のレコード(自分のレコード)は見つかりませんでした。



fail_publish



このアプリケーションでは、ユーザーをすべてのユーザーから隠すのではなく、認証トークンなどの個人情報を隠すだけです。



パッケージを削除したためautopublish



、データを公開するプロセスを手動で処理する必要があります。これにより、ユーザーに送信されるデータを制御できます。



ユーザーのコレクションを公開します。



 # server/publications/users.coffee Meteor.publish 'users', (limit = 20) -> UsersCollection.find {}, fields: service: 1 username: 1 profile: 1 limit: limit
      
      







このコードはすべてのユーザーにユーザーへのアクセスを提供します。購読する必要があります。配信制限を指定しない場合、ユーザーはこのページを購読するとすぐにアップロードされます。明らかな理由ではあまり良くありませんが、使用するautopublish



と自動的にすべてのコレクションで同じことが起こります。



また、アップロードされるデータの可視性を制限しましたprofile



。登録者は、フィールドとユーザー名からの情報以外は表示できません。ただし、たとえば、現在のユーザーの電子メールアドレスへのアクセスを提供する場合は、別のパブリケーションを作成します。



 # server/publications/profile.coffee Meteor.publish 'profile', -> #    , #   if @userId #       UsersCollection.find { _id: @userId }, fields: service: 1 username: 1 profile: 1 emails: 1 else #  ,    @ready() return
      
      







メソッドに渡される2番目のパラメーターはMeteor.publish



、コレクションカーソルを返す関数です。この関数は任意の数の引数を取ることができ、データのさまざまな変更についてユーザーに通知し、いくつかの接続プロパティへのアクセスを提供できるメソッドが利用可能なオブジェクトのコンテキストで実行されます。たとえば、プロファイルの公開ではready



、ユーザーが承認されていないときにメソッドを使用します。これは、公開のデータの準備ができていることを意味し、クライアント側ではサブスクリプション時にコールバックが呼び出されますが、ユーザーはデータを受信しません。出版物の詳細をご覧ください



サブスクリプション



データを受信して​​変更を追跡するには、まず出版物を購読する必要があることに繰り返し気づきました。一般に、Meteorアプリケーションのデータで発生するすべてのことを簡単に追跡および制御できます。すぐに、いつでもパッケージを使用できますautopublish







サブスクリプションに使用しますiron:router



、そして彼は必要なプロセス全体を制御します。なぜなら、手動制御ではこのプロセスは多くに従う必要があり、このライブラリはすべての問題を解決するからです。一部のデータをページごとに表示することをお勧めします。そのため、ユーザーのコントローラーを作成する前に、少し抽象化し、ページを管理する機能を備え、ライブラリーコントローラーから継承されるクラスを作成しますiron:router







 # client/lib/0.pageable_route_controller.coffee varName = (inst, name = null) -> name = name && "_#{name}" || "" "#{inst.constructor.name}#{name}_limit" class @PagableRouteController extends RouteController pageable: true #  ,     perPage: 20 #      #    limit: (name = null) -> Session.get(varName(@, name)) || @perPage #   incLimit: (name = null, inc = null) -> inc ||= @perPage Session.set varName(@, name), (@limit(name) + inc) #   resetLimit: (name = null) -> Session.set varName(@, name), null #     ? loaded: (name = null) -> true
      
      







ボタンの形でテンプレートを作成してみましょう。をクリックするincLimit



と、この機能をサポートしている場合、メソッドは現在のコントローラーに対して呼び出されます。無限にスクロールすることは可能ですが、簡単です。



 //- client/components/next_page_button/next_page_button.jade template(name='nextPageButton') unless loaded a.btn.btn-primary.btn-lg.NextPageButton(href = '#') | More
      
      







 # client/components/next_page_button/next_page_button.coffee Template.nextPageButton.helpers loaded: -> ctrl = Router.current() if ctrl.pageable ctrl.loaded(@name) else true Template.nextPageButton.events 'click .NextPageButton': (event) -> ctrl = Router.current() if ctrl.pageable ctrl.incLimit(@name, @perPage)
      
      







ここで、コンポーネントのロジックをすでに設定しています。ご覧のとおり、すべてのパターンがグローバル名前空間になりますTemplate



。テンプレートはを介して参照できますTemplate.<template-name>



。テンプレートで使用されるメソッドを説明するには、メソッドをhelpers



持つオブジェクトが転送されるメソッドを使用する必要があります。この例ではloaded



、現在のコントローラーが何であるかをチェックし、すべてのデータがダウンロードされたかどうかを示す結果を返すメソッド1つだけ説明します。テンプレート自体で、このメソッドをデザインunless loaded



で取得します。テンプレートでも、現在のコンテキストからデータを取得できます。テンプレートのヘルパーは、テンプレートで使用される場合、オブジェクトのプロトタイプと比較できますが、各ヘルパーは次のように呼ばれるため、関数自体の内部に制限があります<helper-func>.apply(context, arguments)



、つまり、関数内でテンプレートのすべてのヘルパーに連絡する機会はありません。これは一般に時々干渉する可能性があります。



テンプレートイベントを処理events



するには、次の形式のキーを使用して、オブジェクトが転送されるメソッドそれらを記述する必要があります<event> <selector>



jQuery



イベントと、イベントが呼び出されたテンプレートはハンドラー渡されます。親テンプレートで子イベントを処理できるため、これは便利な場合があります。



これで、すべてのユーザーのリストを含むページを作成し、例でサブスクリプションを管理する方法を確認する準備が整いましたiron:router







 # client/routes/users.coffee Router.route '/users', name: 'users' class @UsersController extends PagableRouteController #      perPage: 20 #    ,   , #      # #     ,  iron:router #     ,     #  subscriptions: -> @subscribe 'users', @limit() #       data: -> users: UsersCollection.find() #    ? loaded: -> @limit() > UsersCollection.find().count() #    ,    onRun: -> @resetLimit() @next()
      
      







メソッドsubscriptions



はパブリケーションをサブスクライブしusers



ます。実質的に同様の方法がまだありwaitOn



、その中でのみ、ルーターはすべてのデータがアップロードされるまで待機し、ページをレンダリングした後、その瞬間にpropertyで設定できるテンプレートを表示しますloadingTemplate



。メソッドによって返されるデータはdata



テンプレートにバインドされ、現在のコンテキストで使用できます。UsersCollection.find()



データ自体ではなくカーソルを返しますが、既に既成のデータを処理しているかのように、blazeがすべての変換を行います。限られた量のデータをサブスクライブするため、呼び出しUsersCollection.find().fetch()



はクライアントにアップロードされたデータのみを返します。つまり、たとえば、制限を1に設定すると、find



ロードされた選択(1つのレコード)でのみ機能し、データベースのコレクション内のすべてのデータでは機能しません。たとえば、ここではメソッドを再定義しloaded



ますが、その本質は明確であると思いますがcount



、ローカルレコードの数を返すこと覚えておく必要があります。つまりlimit



すべてのデータがアンロードされるまで同じになるため、条件は厳密に大きくなります。いくつかのフック



がありiron:router



ます。たとえば、ロードされた制限をリセットするためにユーザーを含むページを開くたびに問題が生じることはありません。そうしないと、以前に大量のデータをアップロードした場合、ページのレンダリングに時間がかかる場合があります。したがって、フックを使用してデータ制限をリセットすると便利です。onRun



ページがロードされると、一度実行されます。コードを含むファイルが保存された後に流星がホットスワップを実行するコードが保存されると、このフックは実行されないため、このフックを使用してコントローラーをデバッグするときにページを手動で更新します(他のユーザーにはこのような問題はありません)。フックサブスクリプションの詳細



リアクティブ変数と関数



だから我々は、出版のためにサインアップしますが、テンプレートからボタンをクリックすると、なぜまだ明らかではないかもしれないnextPageButton



、新しいデータの断片、およびオブジェクトの操作にすべてのおかげでダウンロードするために私達につながるSession



ではPagableRouteController



このオブジェクトのデータはリアクティブでありiron:router



、それらの変更を自動的に追跡します。ブラウザコンソールで入力してみてください。



 Tracker.autorun( function() { console.log( 'autorun test', Session.get('var') ); } )
      
      







そして、呼び出しを使用して値を変更してみてくださいSession.set('var', 'value')



、結果はすぐに来るでしょう。テンプレートのデータが自動的に更新されるのと同じ方法で、サブスクリプションを更新する必要がある場合



reactive_var



iron:router



理解する同様のメカニズムのおかげです。リアクティブ変数に関する詳細は公式ドキュメント詳しく説明されており、変数に加えて、トラッカーとテンプレートによって追跡されるメソッド値を管理Session



するリアクティブオブジェクトを作成することができます。そしてトラッカーは、リスナーのようなもので、リアクティブ変数を含まない関数を作成できますが、トラッカーによって追跡されるため、使用する必要がありますset



get



Tracker.Dependency一般に、このライブラリには他の可能性がありますが、実際には、おそらく無駄にそれらを使用する必要はありませんでした。



ブラウザコンソールで実行できる別の小さな例:



 var depend = new Tracker.Dependency(); var reactFunc = function() { depend.depend(); return 42; } Tracker.autorun(function() { console.log( reactFunc() ); }); // 42 depend.changed() // 42 depend.changed() // 42
      
      







サブスクリプションの詳細



例を使用してサブスクリプションの使用方法について説明しましたiron:router



が、これが唯一のメカニズムではありません。覚えておくべき主なことは、サブスクリプションを慎重に使用する必要があることです。そうしないと、大量のデータをアップロードし、必要のないデータの変更を自動的に追跡する危険があります。iron:router



サブスクリプションを管理する非常に簡単な方法を提供します。不要なサブスクリプションをすべてオフにし、必要なサブスクリプションを接続し、必要に応じて現在のサブスクリプションを更新します。



ユーザーのリストを作成し、これらすべてが実際に機能することを確認しましょう。



 //- client/components/users.jade template( name='users' ) h1 Users .row //-  ,     +each users .col-xs-6.col-md-4.col-lg-3 //-    //-   each  ,   //-        +userCard //-     +nextPageButton //- client/components/user_avatar/user_avatar.jade //-   ,     template(name='userAvatar') img(src="{{user.getAvatar size}}", alt=user.getUsername, class="{{class}}") //- client/components/user_card.jade //-       //-        template(name='userCard') .panel.panel-default .panel-body .pull-left +userAvatar user=this size=80 .user-card-info-block ul.fa-ul //-     li if isFromGithub i.fa.fa-li.fa-github else if isFromGoogle i.fa.fa-li.fa-google else i.fa.fa-li b= getName //-    li i.fa.fa-li @ //-    a(href="{{ pathFor route='users_show' data=urlData }}")= getUsername //-  ,   if getPublicEmail li i.fa.fa-li.fa-envelope = getPublicEmail
      
      







users_page



その結果、ページネーションが機能し、すべてのデータがリアクティブであるため、システムの新しいユーザーは再起動することなくページに自動的に追加されます。コレクションにサブスクライブしたため、サーバー上のデータベースのデータへの変更はすぐに表示されますユーザーページ。新しいユーザーを新しいタブに登録するか、ユーティリティを使用してデータベースの値を直接mongo



変更することができます。変更はページに表示されるため、何もする必要はありません。



そして、このアプローチが最適に機能することを確認するために、ブラウザのログを見ることができます。1ページあたりのユーザー数を1に設定します。DDPプロトコルは非常にシンプルで読みやすいので、詳細には触れません。ログでは、すべての不必要なサブスクリプションのサブスクリプションが解除され、ユーザーがサブスクリプションの更新ごとに3回だけダウンロードしたことがわかります。



users_log



ユーザーページとテンプレートについてもう少し

独自のコレクションを作成できるように、いくつかのデータを変更してユーザーとの作業を完了することができるユーザーページを作成しましょう。



これを行うには、ホームページではなく、承認されたユーザーの最初の操作として、現在のユーザーのページを表示し、コントローラーを少し変更します。



 # client/routers/home.coffee Router.route '/', name: 'home' class @HomeController extends PagableRouteController #   ? isUserPresent: -> !!Meteor.userId() #       #   waitOn: -> if @isUserPresent() @subscribe 'profile' #     ,    data: -> if @isUserPresent() { user: UsersCollection.findOne Meteor.userId() } #       #       action: -> if @isUserPresent() @render 'profile' else @render 'home'
      
      







また、任意のユーザーのプロファイルを表示できるコントローラーを作成します。



 # client/routers/user_show.coffee Router.route '/users/:id', name: 'users_show' class @UsersShowController extends PagableRouteController #     template: 'profile' #     waitOn: -> @subscribe 'user', @params.id #    data: -> user: UsersCollection.findOneUser(@params.id)
      
      







識別子またはログインのいずれかでユーザーを検索するために、コレクションに追加のメソッドを作成しました。1つはカーソルを返し、2つ目のデータを返します。



 # collections/users.coffee # ... _.extend Users, # ... findUser: (id, options) -> Users.find { $or: [ { _id: id }, { username: id } ] }, options findOneUser: (id, options) -> Users.findOne { $or: [ { _id: id }, { username: id } ] }, options
      
      







ユーザーのページのデータを取得しようとしていますが、公開されていません。これを修正しています。



 # server/publications/user.coffee Meteor.publish 'user', (id) -> UsersCollection.findUser id, fields: service: 1 username: 1 profile: 1 limit: 1
      
      







ほとんどすべての準備が整い、テンプレートを作成して結果を確認します。テンプレートを作成するときに、アクセス権に応じてモデルフィールドを編集できるようにするコンポーネントを作成することにしました。



 //- client/components/editable_field/editable_field.jade //-       //-     ,     //-       //-     //-       this.<key> template(name='editableField') .form-group.EditableFiled if data.isEditable div(class=inputGroupClass) if hasIcon .input-group-addon if icon i.fa.fa-fw(class='fa-{{icon}}') else i.fa.fa-fw=iconSymbol input.Field.form-control(placeholder=placeholder, value=value, name=name) else if defaultValue span.form-control-static if hasIcon if icon i.fa.fa-fw(class='fa-{{icon}}') else i.fa.fa-fw=iconSymbol = defaultValue
      
      







テンプレートの行で変数を補間するには、ヒゲのデザインを使用することができますclass='fa-{{icon}}'



icon



-それは変数です。



 # client/components/editable_field/editable_field.coffee Template.editableField.helpers value: -> ObjAndPath.valueFromPath @data, @path name: -> ObjAndPath.nameFromPath @scope, @path hasIcon: -> @icon || @iconSymbol inputGroupClass: -> (@icon || @iconSymbol) && 'input-group' || '' Template.editableField.events #   ,      'change .Field': (event, template) -> data = $(event.target).serializeJSON() $(template.firstNode).trigger 'changed', [data]
      
      







 //- client/components/profile/profile.jade template(name='profile') //-  ,      , //-     +with user .profile-left-side .panel.panel-default .panel-body .container-fluid .row.row-bottom //-  ,    //-  <>=<>,      //-     userAvatar +userAvatar user=this size=200 class='profile-left-side-avatar' .row //-      +editableField fieldUsername +editableField fieldName +editableField fieldEmail .profile-right-side h1 Boards
      
      







 # client/components/profile/profile.coffee Template.profile.helpers fieldUsername: -> data: @ defaultValue: @getUsername() placeholder: 'Username' scope: 'user' path: 'username' iconSymbol: '@' fieldName: -> data: @ defaultValue: @getName() placeholder: 'Name' scope: 'user' path: 'profile.name' icon: 'user' fieldEmail: -> data: @ defaultValue: @getPublicEmail() placeholder: 'Public email' scope: 'user' path: 'profile.email' icon: 'envelope' Template.profile.events #      #    'changed .EditableFiled': (event, template, data) -> user = template.data?.user return unless user data = data.user user.merge data
      
      







流星のパターンのレイアウトjade



は非常に意味があり、多くのことを考えたり、ドキュメントを読んだりする必要はありません。すべてがすでに明白です。あなたは上記のコードを理解する問題が発生した場合でも、私はあなたがパッケージのドキュメントに目を通すことをお勧めヒスイ:mquandalle宇宙・天体バーを流星のテンプレートのレイアウトに精通したとき、問題はなかったというだけです。実際、それらは非常に便利になったと思います。



一般に、すべての準備が整ったら、ヘッダーの認証フォームを開いてログインします。ページのタイトル「Home」の代わりに、再起動せずにプロファイルがすぐに表示されます。



profile





現時点で何かが明確でない場合は、リポジトリ内のプロジェクトの現在の状態をよく理解することをお勧めします。ファイル内で発生しているすべてのことについてコメントしようとしました。もちろん、この段階でプロジェクトを傾けて、手で触ることもできます。次に、サーバーコードに関連するトピックをさらにいくつか取り上げます。独自のコレクションを作成する方法、コレクション内のデータを不要な編集から保護する方法、RPCの使用とnpm



サーバーでのライブラリの使用について少しお話します。



コレクションとサブスクリプションの詳細

コレクションの作成を開始する前に、データベースにデータを挿入/変更するときにいくつかのフィールドを自動的に計算するメカニズムを作成することをお勧めします。これを行うには、aldeed:simple-schemaを含むパッケージaldeed:collection2を追加しますこれらのパッケージにより、データの検証、コレクションへのインデックスの追加などを簡単に行うことができます。



パッケージにaldeed:simple-schema



いくつかの新機能を追加します



 # lib/simple_schema.coffee _.extend SimpleSchema, #        #       build: (objects...) -> result = {} for obj in objects _.extend result, obj return new SimpleSchema result #      , #          #  timestamp: createdAt: type: Date denyUpdate: true autoValue: -> if @isInsert return new Date if @isUpsert return { $setOnInsert: new Date } @unset() updatedAt: type: Date autoValue: -> new Date
      
      







そして、新しいコレクションを作成します



 # collections/boards.coffee #   boardsSchema = SimpleSchema.build SimpleSchema.timestamp, 'name': type: String index: true 'description': type: String optional: true #    #     'owner': type: String autoValue: (doc) -> if @isInsert return @userId if @isUpsert return { $setOnInsert: @userId } @unset() #    'users': type: [String] defaultValue: [] 'users.$': type: String regEx: SimpleSchema.RegEx.Id #      Boards = new Mongo.Collection 'boards' Boards.attachSchema boardsSchema #   Boards.allow #       insert: (userId, doc) -> userId && true #       update: (userId, doc) -> userId && userId == doc.owner #   _.extend Boards, findByUser: (userId = Meteor.userId(), options) -> Boards.find $or: [ { users: userId } { owner: userId } ] , options create: (data, cb) -> Boards.insert data, cb #   Boards.helpers update: (data, cb) -> Boards.update @_id, data, cb addUser: (user, cb) -> user = user._id if _.isObject(user) @update $addToSet: users: user , cb removeUser: (user, cb) -> user = user._id if _.isObject(user) @update $pop: users: user , cb updateName: (name, cb) -> @update { $set: {name: name} }, cb updateDescription: (desc, cb) -> @update { $set: {description: desc} }, cb # joins getOwner: -> UsersCollection.findOne @owner getUsers: (options) -> UsersCollection.find $or: [ { _id: @owner } { _id: { $in: @users } } ] , options urlData: -> id: @_id #  @BoardsCollection = Boards
      
      







まず、コレクションを作成するときにスキームを定義しました。これにより、データを検証し、いくつかのフィールドを自動的に計算できます。検証の詳細については、aldeed:simple-schema package page 見つけることができます。非常に豊富な機能がありaldeed:autoform



ます。同じ作成者から追加パッケージをインストールすると、エントリを作成するときにエラーをすぐに通知するフォームを生成できます。



データベースに新しいコレクションBoards = new Mongo.Collection 'boards'



が存在しない場合は呼び出すか、既存のコレクションに接続して、新しいコレクションを作成します。原則として、これは新しいコレクションを作成するために必要なすべての機能です。作成時に指定できるオプションがいくつかあります。



使用方法allow



コレクションでは、コレクション内のデータを変更するためのアクセスを制御できます。現在の例では、許可されていないすべてのユーザーのコレクションに新しいエントリを作成することを禁止し、ボードの作成者のみがデータを変更できるようにします。これらのチェックはサーバー上で実行されるため、何らかのフッカーがクライアントのこのロジックを変更することを心配する必要はありません。また、あなたが自由に使える方法は、ほぼ同じdeny



です。その本質は明らかだと思います。allowdenyの詳細をご覧ください



ボードカードを表示するときに、ボード作成者に関するデータをすぐに表示したい。ただし、ボードのみにサブスクライブする場合、このデータはクライアントに送信されません。ただし、流星内の出版物は、コレクションカウンターなど、自動的に計算された任意のデータをサブスクライブする機能を提供します。



 # server/publications/boards.coffee Meteor.publish 'boards', (userId, limit = 20) -> findOptions = limit: limit sort: { createdAt: -1 } if userId #    cursor = BoardsCollection.findByUser userId, findOptions else #   cursor = BoardsCollection.find {}, findOptions inited = false userFindOptions = fields: service: 1 username: 1 profile: 1 #        addUser = (id, fields) => if inited userId = fields.owner @added 'users', userId, UsersCollection.findOne(userId, userFindOptions) #    , #       handle = cursor.observeChanges added: addUser changed: addUser inited = true #      , #       userIds = cursor.map (b) -> b.owner UsersCollection.find({_id: { $in: userIds }}, userFindOptions).forEach (u) => @added 'users', u._id, u #    ,    @onStop -> handle.stop() return cursor
      
      







リレーショナルデータベースで発生するように、Mongaは複数のコレクションを介してクエリを実行し、既に処理されたデータを返すことができないため、もう1つのクエリを使用してボードの作成者に関するデータを取得する必要があり、データモデルのフレームワーク内で作業する方が便利です。



まず、リクエストに応じて、データベースから必要なボードを取得します。その後、ユーザーに別のリクエストを送信する必要があります。メソッドadded



changed



およびremoved



パブリケーションのコンテキストでは、クライアントに送信されるデータを管理できます。パブリケーションでコレクションカーソルを返す場合、これらのメソッドはコレクションの状態に応じて自動的に呼び出されるため、カーソルを返しますが、パブリケーション自体に加えて、ボードコレクション内のデータの変更をサブスクライブし、必要に応じてユーザーデータをクライアントに送信します。



Webソケットの接続ログまたはこのユーティリティを使用し、このアプローチが最適に機能することを確認できます。ここでは、この場合、ユーザーコレクションの変更がクライアントと同期されないことを理解することが重要ですが、それは意図されたものです。ところで、単純な結合の場合、サブスクリプションの結果としてカーソルの配列を単純に返すことができます。



ユーザーボードを表示するために、新しいサブスクリプションをルーターに追加し、必要なテンプレートを作成しましたが、上記のすべてのポイントを既に検討しましたすべての変更に興味がある場合は、ここで確認できますしかし、最終的には、パフォーマンスを確認するためにコンソールを介してボードを作成する必要がありますが、次のものを取得する必要があります。



boards



また、mrt:react-publishパッケージを使用してリアクティブパブリケーションを作成することもできます





サーバーの変更

ボードの背景画像を設定する機能を追加しましょう。これを行うには、サーバーがファイルを受信、処理、保存、要求時に提供できるようにサーバーを構成する必要があります。



NPM



画像処理の場合、ImageMagickを使用することに慣れています。ノードには、このライブラリへのインターフェースを提供する対応するパッケージがあります。流星にnpm



パッケージを使用する機会を与えるには、を追加する必要がありmeteorhacks:npm



ますpackages.json



その後、必要なすべてのパッケージをファイルに記述できますたとえば、私はパックのみ必要GMと私はpackages.json



次のようになります。



 { "gm": "1.17.0" }
      
      







Iをnpm



介して接続されたすべてのパッケージmeteorhacks:npm



は1つの流星パッケージにラップされるため、コマンドを使用してアプリケーションをビルドmeteor build



する場合、問題はなく、すべての依存関係は自動的に解決されます。コマンドを



使用npm



してサーバー上のパッケージに接続する必要があります。コマンドは、ノード内のMeteor.npmRequire(<pkg-name>)



機能と同じように機能require



します。



RPCおよび同期非同期関数呼び出し



イメージをダウンロードして処理するには、クライアントから呼び出すことができるサーバーメソッドを作成します。



 # server/lib/meteor.coffee Meteor.getUploadFilePath = (filename) -> "#{process.env.PWD}/.uploads/#{filename}" # server/methods/upload_board_image.coffee #      gm = Meteor.npmRequire 'gm' #     resizeAndWriteAsync = (buffer, path, w, h, cb) -> gm(buffer) .options({imageMagick: true}) .resize(w, "#{h}^", ">") .gravity('Center') .crop(w, h, 0, 0) .noProfile() .write(path, cb) #     resizeAndWrite = Meteor.wrapAsync resizeAndWriteAsync #        Meteor.methods uploadBoardImage: (boardId, data) -> board = BoardsCollection.findOne(boardId) if board.owner != @userId throw new Meteor.Error('notAuthorized', 'Not authorized') data = new Buffer data, 'binary' name = Meteor.uuid() #     path = Meteor.getUploadFilePath name resizeAndWrite data, "#{path}.jpg", 1920, 1080 resizeAndWrite data, "#{path}_thumb.jpg", 600, 400 #     BoardsCollection.update { _id: boardId }, $set: background: url: "/uploads/#{name}.jpg" thumb: "/uploads/#{name}_thumb.jpg" return
      
      







このメソッドでuploadBoardImage



は、画像が追加されるボードの識別子と、この画像のバイナリデータを含む行を受け入れます。



メソッドで例外がスローされると、コールバックの最初のパラメーターであるクライアントのユーザーに例外が渡されます。そして、メソッドによって返されたデータは、2番目のコールバックパラメーターとしてクライアントに送られます。



非同期プログラミングスタイルで例外と関数の戻り値を使用できるように、流星のサーバー側には、ファイバーライブラリを介して非同期関数を同期関数にラップするメソッドがあります。要するに、このライブラリのおかげで、ラップされた関数の呼び出しが実行キューを占有しない場合、サーバー上で同期コードを記述でき、コード実行の間違ったシーケンスを心配することはありません。方法Meteor.wrapAsync(<async-func>)



最後のパラメーターとしてコールバックを取る関数はラップされます。このコールバックでは、最初のパラメーターはエラーであり、2番目の結果は、ノード内のすべての標準ライブラリーのパラメーターの形式などです。エラーが到着すると、ラップされた関数はこのエラーで例外をスローします。そうでない場合、コールバックに返される2番目のパラメーターが関数から返されます。



ルーティング

多くの理由で既製の​​組み込みのソリューションを使用してサーバーから静的を発行する方が良いことを理解していますが、ここでは静的をノードに提供します。



流星にはサーバールーティング用の標準webappパッケージがありますが、フォームにはもっと便利なソリューションが既にインストールされていiron:router



ます。同様に、クライアントと同様に、サーバールートを作成します。



 # server/routes/uploads.coffee fs = Meteor.npmRequire 'fs' Router.route '/uploads/:file', where: 'server' action: -> try filepath = Meteor.getUploadFilePath(@params.file) file = fs.readFileSync(filepath) @response.writeHead 200, { 'Content-Type': 'image/jpg' } @response.end file, 'binary' catch e @response.writeHead 404, { 'Content-Type': 'text/plain' } @response.end '404. Not found.'
      
      







ここでの主なことは、プロパティをrouteに渡すことですwhere: 'server'



。そうしないと機能しません。実際には、指定されたファイルをディスクから読み取ろうとしています。このディレクトリには同じ形式の画像しか存在しないため、この方法を可能な限り簡略化しました。ルートのコンテキストで使用できる



オブジェクトrequest



response



オブジェクトはそれぞれhttp.IncomingMessageおよびhttp.ServerResponseノードの標準ライブラリのクラスオブジェクトです。



REST APIを作成するためiron:router



インターフェースあります。



RPCを使用する



使用するには、新しいボードを追加するためのフォームを作成しましょう。



次に、ユーザーをボードに追加するためのオートコンプリートも作成しました。そこではRPCも使用されていますリポジトリの実装について詳しく知ることができます



 //- client/components/new_board_form/new_board_form.jade template(name='newBoardForm') //-     .panel.panel-default.new-board-panel(style='{{panelStyle}}') .panel-body h1 New board form(action='#') .form-group input.form-control(type='text',placeholder='Board name',name='board[name]') .form-group textarea.form-control(placeholder='Description',name='board[description]') .form-group //-    ,      ,   label.btn.btn-default(for='newBoardImage') Board image .hide input#newBoardImage(type='file', accept='image/*') button.btn.btn-primary(type='submit') Submit
      
      







 # client/components/new_board_form/new_board_form.coffee #      currentImage = null currentImageUrl = null currentImageDepend = new Tracker.Dependency #    resetImage = -> currentImage = null currentImageUrl = null currentImageDepend.changed() #     uploadImage = (boardId) -> if currentImage reader = new FileReader reader.onload = (e) -> #    Meteor.call 'uploadBoardImage', boardId, e.target.result, (error) -> if error alertify.error error.message else alertify.success 'Image uploaded' reader.readAsBinaryString currentImage #    Template.newBoardForm.helpers #     , #    ,     panelStyle: -> currentImageDepend.depend() currentImageUrl && "background-image: url(#{currentImageUrl})" || '' #     ,      Template.newBoardForm.rendered = -> resetImage() #   Template.newBoardForm.events #   ,     #    ,  , #    'submit form': (event, template) -> event.preventDefault() form = event.target data = $(form).serializeJSON() BoardsCollection.create data.board, (error, id) -> if error alertify.error error.message else form.reset() alertify.success 'Board created' resetUsers() uploadImage(id) resetImage() #       #     'change #newBoardImage': (event, template) -> files = event.target.files image = files[0] unless image and image.type.match('image.*') resetImage() return currentImage = image reader = new FileReader reader.onload = (e) => currentImageUrl = e.target.result currentImageDepend.changed() reader.readAsDataURL(image)
      
      







ここでは、イメージをダウンロードして処理するために、を通じてリモートメソッドを実行しMeteor.call



ます。ご覧のとおり、クライアントでリモートプロシージャを呼び出すことは、通常の関数呼び出しとそれほど変わらず、引数で渡されるすべてのデータはWebソケットを介してサーバーにダウンロードされます。ユーザーファイルを読み取るために、HTML5仕様のFile APIを使用しました



画像をロードする例は、最も成功するとは限りませんが、流星のサーバー側の機能をよく示しています。本番用に作成している場合は、既製のCollectionFSソリューションを使用できます



new_board





原則として、このレッスンでカバーしたかったのはこれだけですが、完全を期すために、ボードのカードの機能を終了します。このレッスンにないものは使用しません。リポジトリで通常どおり変更を確認できます



tasks







参照資料




All Articles