Angular+Firebaseでまずはフロントエンドだけ作っていくとすごく楽しい

タイトルのとおりです。 Angular+Firebaseで一旦それっぽくしちゃうと、モチベーションが下がらないのでおすすめです。

導入

$ npm install -g @angular/cli
$ ng new my-app
$ cd my-app
$ ng serve
** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200 **
Hash: c27678c6699304bb4a70
Time: 7560ms
chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 158 kB {4} [initial] [rendered]
chunk    {1} main.bundle.js, main.bundle.js.map (main) 3.63 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 10.5 kB {4} [initial] [rendered]
chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.4 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]
webpack: Compiled successfully.

これでhttp://localhost:4200/にアプリケーションがたちました。

それっぽいUIフレームワークをいれる

僕のおすすめは

Angular Material

Clarity Design System

この2つです。 clarityはcardのダサさにウッっとなるのですが、それ以外は無難であることと、デザインガイドラインが用意されているので良いです。 今回はclarityを入れてすすめます。

$ npm install clarity-icons @angular/animations @webcomponents/custom-elements@1.0.0-rc.3 mutationobserver-shim@0.3.2 clarity-ui clarity-angular --save
//.angular-cli.json
      "styles": [
        "../node_modules/clarity-icons/clarity-icons.min.css",
        "../node_modules/clarity-ui/clarity-ui.min.css",
        ...
      ],
      "scripts": [
        "../node_modules/mutationobserver-shim/dist/mutationobserver.min.js",
        "../node_modules/@webcomponents/custom-elements/custom-elements.min.js",
        "../node_modules/clarity-icons/clarity-icons.min.js",
        ...
      ],
...
//src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ClarityModule } from 'clarity-angular'; //追加
import { AppComponent } from './app.component';

@NgModule({
    imports: [
        BrowserModule,
        ClarityModule.forRoot(), //追加
        ....
     ],
     declarations: [ AppComponent ],
     bootstrap: [ AppComponent ]
})
export class AppModule {    }

これでng serveすれば動きます。てきとうに

<!-- src/app/app/component.html -->
<div class="main-container">
  <header class="header header-6">
    <div class="branding">
      <a href="..." class="nav-link">
        <clr-icon shape="vm-bug"></clr-icon>
        <span class="title">僕のWebアプリ</span>
      </a>
    </div>
  </header>
  <div class="content-container">
    <div class="content-area">
      ...
    </div>
    <nav class="sidenav">
      <section class="sidenav-content">
        <a href="..." class="nav-link active">
                    Nav Element 1
                </a>
        <a href="..." class="nav-link">
                    Nav Element 2
                </a>
        <section class="nav-group collapsible">
          <input id="tabexample1" type="checkbox">
          <label for="tabexample1">Collapsible Nav Element</label>
          <ul class="nav-list">
            <li><a class="nav-link">Link 1</a></li>
            <li><a class="nav-link">Link 2</a></li>
          </ul>
        </section>
        <section class="nav-group">
          <input id="tabexample2" type="checkbox">
          <label for="tabexample2">Default Nav Element</label>
          <ul class="nav-list">
            <li><a class="nav-link">Link 1</a></li>
            <li><a class="nav-link">Link 2</a></li>
            <li><a class="nav-link active">Link 3</a></li>
            <li><a class="nav-link">Link 4</a></li>
            <li><a class="nav-link">Link 5</a></li>
            <li><a class="nav-link">Link 6</a></li>
          </ul>
        </section>
      </section>
    </nav>
  </div>
</div>

とか書いておきましょう。それっぽくなると思います。

f:id:anoChick:20170524164453p:plain

ビルドとデプロイ

ビルドは

$ ng build --prod

これだけです。 何処かにデプロイしたいとおもったら、Firebaseが一番早いと思います

$ npm install -g firebase-tools
$ firebase login
$ firebase init



デプロイ先プロジェクトは、既にあればそこを指定し、特になければ[create a new project]で新たに作ってください。

✔  Firebase initialization complete!

となれば成功。

//firebase.json
{
  "hosting": {
    "public": "dist"
  }
}

と書き加えてから

$ firebase deploy

とコマンドを叩くとデプロイされます。

=== Deploying to 'development-ef09c'...

i  deploying hosting
i  hosting: preparing dist directory for upload...
✔  hosting: 8 files uploaded successfully
i  starting release process (may take several minutes)...

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/development-ef09c/overview
Hosting URL: https://development-ef09c.firebaseapp.com

ログインとかユーザ管理のしくみもFirebaseに乗っかるとすごい楽なんですが、 それはまた別の話。

RailsでGraphQLを使う

結構使いやすくなっていたので紹介

セットアップ

# Gemfile
gem 'graphql'
rails generate graphql:install

まず導入 基本的な構成と、graphiql(GraphQLのクライアントをRailsにマウントするやつ)が入る

http://localhost:3000/graphiql

サンプルフィールドが用意されているのでクエリを叩くと、結果が得られる。 f:id:anoChick:20170521182758p:plain

こんなかんじ。

ActiveRecordとつなげる

Railsで使うからにはGraphQLのObjectとRailsのモデルを繋げなければいけません。 つなぐために

GitHub - goco-inc/graphql-activerecord

このGemを使います。

rails g model Book name:string

こんなモデルを予め作っておいてください。

# Gemfile
gem 'graphql-activerecord'
bundle install

レコード作成

まずはBookモデルに対応したBookTypeを作ります。

# app/graphql/types/book_type.rb
Types::BookType = GraphQL::ObjectType.define do
  name "Book"
  backed_by_model :books do
    attr :name
  end
end

次にBookを新たに作成するためのMutationを定義します。

# app/graphql/mutations/create_book.rb
Mutations::CreateBook = GraphQL::Relay::Mutation.define do
  name "CreateBook"

  return_field :book, Types::BookType
  input_field :name, !types.String

  mutator_definition = GraphQL::Models.define_mutator(self, Book, null_behavior: :leave_unchanged) do
    attr :name
  end
  resolve ->(obj, args, ctx) {
    { book: Book.create(name:args[:name]) }
  }
end

あとは、このMutationを

App名Schemaに認識させます。

# app/graphql/types/mutation_type.rb

Types::MutationType = GraphQL::ObjectType.define do
  name 'Mutation'
  field :CreateBook, field: Mutations::CreateBook.field
end
# app/graphql/アプリ名_schema.rb

アプリ名Schema = GraphQL::Schema.define do
  query(Types::QueryType)
  mutation(Types::MutationType) # 追加

  lazy_resolve(Promise, :sync)
  instrument(:query, GraphQL::Batch::Setup)
  instrument(:field, GraphQL::Models::Instrumentation.new)
end

これで叩ける用にはなりました。

f:id:anoChick:20170521194025p:plain 問題なければDBにレコードが作成されているはずです。

queryを使う。

先程BookTypeは作ってしまったので、QueryTypeを記述するだけです。

# app/graphql/types/query_type.rb

Types::QueryType = GraphQL::ObjectType.define do
  name "Query"
  
  field :book, Types::BookType do
    description "An example field added by the generator"
    argument :id, !types.ID
    resolve ->(obj, args, ctx) {
      Book.find(args[:id])
    }
  end
end

f:id:anoChick:20170521194524p:plain

関連レコードも引っ張ってくる

これだけだとつまらないので関連データもとってこれるようにしたいと思います。 デモ用モデルとして「登場人物」を新たに作ります。

rails g model Character name:string book:references

次に CharacterTypeを定義します。 ちなみに、

rails g graphql:object Character

で作ることも出来る。

# app/graphql/types/book_type.rb
Types::BookType = GraphQL::ObjectType.define do
  name "Book"
  backed_by_model :books do
    attr :name
    has_many_array :characters # 追加
  end
end

今日の雑記はgraphql-activerecord - あのにのに

でも書いたけど Typeの定義の仕方によっては一手間必要・

f:id:anoChick:20170521213806p:plain

できたできた。

今日の雑記はgraphql-activerecord

github.com

このgem使ってるんだけど、

RuntimeError (Could not locate GraphQL type for model Character)

“GraphQL::Models::CharacterTypeはなしんこなしなしだぞ”

って言われる。

graphql-activerecord/activerecord.rb at master · goco-inc/graphql-activerecord · GitHub

ここらへん。

Modelに対応するTypeをTypes::CharacterTypeみたいに定義しちゃってるせい。

# config/initializers/graphql.rb
GraphQL::Models.model_to_graphql_type = -> (model_class) { "Types::#{model_class.name}Type".safe_constantize }

で動いた。

Bash on Ubuntu on Windowsからexeファイルをコマンドとして叩いていくために

anoninoni.hateblo.jp

こっちの記事でdocker.exeをdockerコマンドとして使えるようにしました。 ただコマンドごとに.bashrcに記述していくのも面倒なので、指定ディレクトリ内のwindowsバイナリをまとめて処理するようにしようと思います。

/mnt/c/Users/anoChick/.winbin

ここに*.exeファイルを入れていきます。 ディレクトリをどこに配置するかは自由なのですが、 /usr/local/winbinとかだとWindows側が読めないです。

export PATH=$PATH:/mnt/c/Users/anoChick/.winbin

WIN_BIN_DIR='/mnt/c/Users/anoChick/.winbin'
cd $WIN_BIN_DIR
for file in `ls *.exe`
do
  alias ${file%.*}=$file
done
cd

あとはsource .bashrcするなりターミナルを再起動するなりすれば動くと思います。

Windows Subsystem for LinuxからDocker for Windowsを使う

Docker for WindowsWindowsなので当然Windows Subsystem for Linuxからは実行できない。f:id:anoChick:20170422054043p:plain

と、おもいきやWindows 10 Creators UpdateからLinux側からでもWindowsバイナリ(exeファイル)が実行できるようになった。 なのでPATHさえ通してしまえば良さそう。

export PATH=$PATH:/mnt/c/Program Files/Docker/Docker/resources/bin

ただこれだけだと実行するのにdocker.exeと記述しないといけないので、エイリアスを張る。

alias docker='docker.exe'

これでOK!

exeファイルとして扱われるのでsystem32にもパス通しちゃって良さそう。

WindowsでKitematicを起動するとクラッシュするようになってたんだけど治った。

最近Windowsで開発環境が整ったワイワイしてたんですが、 なんでか突然Kitematicを起動するとOSごとクラッシュ(ブルースクリーン)するようになってしまった。

クラッシュする直前にVirtualBoxを呼び出そうそしている形跡はあったのだけれど、 そもそもWin10のDocker for WindowsでDockerを動かしているのでVirtualBox使わなくね? と思い、issueを漁ったらあった。

github.com

Windowsコンテナを扱うモード(VirtualBox利用)に切り替えた後戻らなくなるバグのようで、 localStorageを書き換えたら無事治った。 f:id:anoChick:20170420205522p:plain

コンソールを開くメニューがWindows版Kitematicには存在しないのだけれど、 幸い私はショートカットを知っていたので開いた。良かった。

WindowsでWeb開発環境を整える雑記

基本的にはWindows Subsystem for Linux乗る

先日のWindows 10 Creators Updateでかなり使えるレベルになったらしいです。 https://blogs.msdn.microsoft.com/commandline/2017/04/11/windows-10-creators-update-whats-new-in-bashwsl-windows-console/

私が常用しているreact-boilerplateもネットワークインタフェース系がサポートされておらず動きませんでした。 https://github.com/Microsoft/BashOnWindows/issues/468

f:id:anoChick:20170416204908p:plain

これが、Creators Updateを適応したら動作するようになりました。嬉しい。

Bash on Ubuntu on Windows

Bash on Ubuntu on Windowsに関しても、 日本語がずれたり切れたりしていて使い物にならなかったんですが、今回のアップデートできれいに表示されるように修正されました。 f:id:anoChick:20170416204821p:plain

ちなみに、修正される前は http://cmder.net/ Cmderをつかったり(tmuxがマウス操作に反応しない)

f:id:anoChick:20170416204846p:plain

それを回避するために Bash on Ubuntu on Windows の最高の端末環境を教えてやるからちょっと来い。 ↑みたいな感じで Cygwin+minttyからBoWを繋いで動かす。みたいなことをしてつかわれていたそうです。

Docker for WIndows

Hyper-vを使うことでWindows上でLinuxコンテナを動かす感じです。 Web開発をする上ではそれで十分だとは思いますが、最近はWindowsコンテナも使えるようになったらしいです。 WindowsのDocker(コンテナ)対応、正しく理解できていますか?

Windowsを積極的に使っていく気持ち

最近Windowsがめちゃめちゃ良くなってきているのを感じます。 せっかく高性能なWindows機を購入したので積極的に使っていきます。