すぐに言いたいのは、Webアプリケーションの開発にあまり経験がないことです。 私はこれを約2年しかやっておらず、数か月間しか流星を知らなかった。
また、レッスンは非常に膨大であることが判明しましたが、その中のコードはテキストよりも何倍も書かれていなかったことに注意してください。 単純な例を作成するときではなく、流星の使用方法に関する私の経験を共有し、重要だと考えたさまざまなポイントに焦点を当てたいと思います。 したがって、このレッスンでは、開発プロセスを促進する多くのサードパーティパッケージを使用します。
もう1つの警告:このレッスンでは、次のテクノロジーを使用して例を直接記述します。
- jade -htmlプリプロセッサ。
- less -cssプリプロセッサ。
- coffeescriptは、javascriptでコンパイルされたプログラミング言語です。
レッスン中に受け取ったアプリケーションを示すビデオ
そしてまだ興味がある人は、猫にようこそ。
Meteorをインストールする
流星自体はnodejs
と
mongodb
基づいており、流星はWindowsをサポートしていません。流星を感じる場合は、LinuxまたはMacOSオペレーティングシステムを入手する必要があります。
最初に、 nodejsとmongodbをインストールします 。
次のステップは、流星をインストールすることです。 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サイトで見つけることができます。 実際には、パッケージの名前:
-
less
、coffeescript
は公式パッケージであるため、著者の名前は含まれません。 -
mquandalle:jade
しかし、これは公式のパッケージではないため、名前は2つのコンポーネントで構成されていますが、うまく機能し、問題なく使用できました。
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 -
おそらく何かが私たちにとって有用ではないかもしれませんが、いずれにしても、流星はフォルダとファイルの命名に制限がないので、あなたにとって便利な構造を思いつくことができます。 ニュアンスのいくつかについて覚えておくべき主なこと:
- プロジェクトのルートにある
public
フォルダーのすべてのファイルには、ブラウザーからのリンクを介してユーザーがアクセスでき、プロジェクトに自動的に接続されません。 - ルートの
server
フォルダーのすべてのファイル。アプリケーションのサーバー側でのみ使用可能。 - ルートの
client
フォルダのすべてのファイルは、アプリケーションのクライアント部分のみがアクセスできます。 - あらゆる環境で基本的に利用可能な他のすべて。
- ファイルは、次の規則に従ってプロジェクトに自動的に接続されます。
- ダウンロードはサブディレクトリから開始され、最初のディレクトリは常に
lib
という名前のディレクトリで処理されます。その後、すべての*フォルダとファイルがアルファベット順にダウンロードされます。 -
main.
始まるファイルmain.
最後にロードされました。
たとえば、プロジェクトがブラウザにどのように読み込まれるかを考えてみましょう:最初に、プロジェクトルートの
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
。 プロジェクトの説明には、他の一般的なライブラリとの比較があります。
- 通常のテキストテンプレートと比較して、炎は家で機能し、1つの属性を変更するためにテンプレート全体を再レンダリングすることはなく、ネストされたテンプレートには問題ありません。
-
Ember
テンプレートと比較して、blazeは変更のみをレンダリングし、明示的なデータバインディングやテンプレート間の依存関係の説明は必要ありません。 -
angular
およびpolymer
テンプレートと比較して、ブレイズは明確でシンプルな構文、低い入力しきい値を持ち、一般的に将来のテクノロジーとして位置付けられませんが、単に機能します。 -
React
と比較するとReact
シンプルなテンプレート記述構文とシンプルなデータ管理を備えています。
図書館の公式説明から段落を実際に翻訳したので、何かに同意しない場合は石を投げないでください。 私自身はこれらの技術に出くわしました(
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; } }
ちなみに、スタイルを変更する場合、ブラウザでページを更新する必要はありません。ファイルを保存するだけですぐに適用されます。流星のレイアウト設計者向けの便利なツールがあります。
ルーティング
流星自体に標準のルーティングメカニズムはありません。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
ブラウザを開いて、ヘッダーのリンクをクリックしてルーティングが機能することを確認できます。 そして、ページを更新せずに動作します。
このレッスンを開発する過程で、プレゼンテーションのシーケンスに従ってリポジトリのコードにすべての変更を加え、投稿の一部をスキップできるようにプロセス全体をフォローできるようにします。 リポジトリ 。
ここでは、最後に何が起こったのかを見ることができ、 ここでは、現在の状態のプロジェクトコードを見ることができます。
ユーザーと認証
当社に来る多くの技術的なタスク、最初のタスクはユーザーシステムを説明します。 これはかなり一般的なタスクなので、特に流星がこのための標準ツールを提供するため、レッスンではユーザー認証の方法を検討する必要があると考えています。メカニズムについては詳しく説明しませんが、ログイン/パスワードまたは
google
および
github
サービスを介してユーザーを作成できる既製のソリューションを使用します。 私は
omniauth
いくつかのジェネレーターと設定の
omniauth
、Rails
omniauth
と
omniauth
devise
に
omniauth
ています。 そのため、流星は、これをすぐに使用できるようにするだけでなく、サービスの構成も可能な限り簡単になります。
次のパッケージをインストールします。
-
accounts-base
-meteorアプリケーションのユーザー向けの基本パッケージ。 -
accounts-password
、accounts-github
、accounts-github
ログイン/パスワードおよびgithub
およびgoogle
サービスを介した認証のサポートを追加します。 -
ian:accounts-ui-bootstrap-3
ブートストラップアプリケーションでのアカウントの統合を簡素化するパッケージ。
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
そして、次の結果が得られます
構成後、認証トークンがデータベースに保存されていることを確認できます。
$ 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
がありますが、それはまだ生です。 アカウントを自分で維持することができますが、これは複雑なことではなく、ネットワークには多くの例や他のソリューションがあります: one 、 two 、 three 。
アカウントの詳細なドキュメントもあります。パッケージを置いて魔法を見るだけですが、内部ではそれほど難しくありません。
アカウントシステムを表面的に調べたので、あまり私を蹴る必要はありません。複雑なものは何もないことを示したかっただけです。詳細なレビューには別の投稿が必要です。そして、レッスンでは、必要な基本機能を作成し、最終結果に進みます。
次のステップはユーザーのページを処理することですが、それを破る前に、流星にいくつかのことを実装する方法を考慮する必要があります。
コレクション、出版物、購読。
プロジェクトを作成するときに、自動的に二つのパッケージが追加されている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
そして、ブラウザでデータを取得しようとしたときに、認証なしの単一のレコードと、それ以外のレコード(自分のレコード)は見つかりませんでした。
このアプリケーションでは、ユーザーをすべてのユーザーから隠すのではなく、認証トークンなどの個人情報を隠すだけです。
パッケージを削除したため
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')
、結果はすぐに来るでしょう。テンプレートのデータが自動的に更新されるのと同じ方法で、サブスクリプションを更新する必要がある場合
を
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
その結果、ページネーションが機能し、すべてのデータがリアクティブであるため、システムの新しいユーザーは再起動することなくページに自動的に追加されます。コレクションにサブスクライブしたため、サーバー上のデータベースのデータへの変更はすぐに表示されますユーザーページ。新しいユーザーを新しいタブに登録するか、ユーティリティを使用してデータベースの値を直接
mongo
変更することができます。変更はページに表示されるため、何もする必要はありません。
そして、このアプローチが最適に機能することを確認するために、ブラウザのログを見ることができます。1ページあたりのユーザー数を1に設定します。DDPプロトコルは非常にシンプルで読みやすいので、詳細には触れません。ログでは、すべての不必要なサブスクリプションのサブスクリプションが解除され、ユーザーがサブスクリプションの更新ごとに3回だけダウンロードしたことがわかります。
ユーザーページとテンプレートについてもう少し
独自のコレクションを作成できるように、いくつかのデータを変更してユーザーとの作業を完了することができるユーザーページを作成しましょう。これを行うには、ホームページではなく、承認されたユーザーの最初の操作として、現在のユーザーのページを表示し、コントローラーを少し変更します。
# 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」の代わりに、再起動せずにプロファイルがすぐに表示されます。
現時点で何かが明確でない場合は、リポジトリ内のプロジェクトの現在の状態をよく理解することをお勧めします。ファイル内で発生しているすべてのことについてコメントしようとしました。もちろん、この段階でプロジェクトを傾けて、手で触ることもできます。次に、サーバーコードに関連するトピックをさらにいくつか取り上げます。独自のコレクションを作成する方法、コレクション内のデータを不要な編集から保護する方法、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
です。その本質は明らかだと思います。allowとdenyの詳細をご覧ください。
ボードカードを表示するときに、ボード作成者に関するデータをすぐに表示したい。ただし、ボードのみにサブスクライブする場合、このデータはクライアントに送信されません。ただし、流星内の出版物は、コレクションカウンターなど、自動的に計算された任意のデータをサブスクライブする機能を提供します。
# 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ソケットの接続ログまたはこのユーティリティを使用して、このアプローチが最適に機能することを確認できます。ここでは、この場合、ユーザーコレクションの変更がクライアントと同期されないことを理解することが重要ですが、それは意図されたものです。ところで、単純な結合の場合、サブスクリプションの結果としてカーソルの配列を単純に返すことができます。
ユーザーボードを表示するために、新しいサブスクリプションをルーターに追加し、必要なテンプレートを作成しましたが、上記のすべてのポイントを既に検討しました。すべての変更に興味がある場合は、ここで確認できます。しかし、最終的には、パフォーマンスを確認するためにコンソールを介してボードを作成する必要がありますが、次のものを取得する必要があります。
また、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ソリューションを使用できます。
原則として、このレッスンでカバーしたかったのはこれだけですが、完全を期すために、ボードのカードの機能を終了します。このレッスンにないものは使用しません。リポジトリで通常どおり変更を確認できます。
参照資料
- 流星
- 雰囲気 -流星パック
- mquandalle:jade-流星パターンに玉マークアップを使用する機能を追加するパッケージ
- 鉄:ルーター -クライアントとサーバーのルーティング。
- mizzao:bootstrap-3-流星のTwitterブートストラップ。
- ian:accounts-ui-bootstrap-3-ログインおよび登録フォーム用のTwitter Bootstrapベースのテンプレート。
- dburles:collection-helpers — ;
- jparker:gravatar — Gravatar ;
- aldeed:collection2 — ;
- sergeyt:typeahead — ;
- meteorhacks:npm —
npm
; - CollectionFS — ;
- :
- MeteorHacks — ;
- EventedMind — ;
- Meteorpedia — Wiki;
- ;
- ,