月別アーカイブ: 2022年11月

PaizaAランクLvアップ問題と解説(Ruby)その3

その2から続いています

※スキルチェック問題ではありません。

規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

1:マップからの座標取得 (paizaランク C 相当)

マップの行数 H と列数 W とマップを表す H 行 W 列の文字列 S_1 …S_H が与えられます。
要素が ‘#’ になっているマスが 1 つあるので、その y , x 座標 を答えてください。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値

H W     
S_0     
...     
S_(H-1)

・ 1 行目には盤面の行数を表す整数 H , 盤面の列数を表す整数 W が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、盤面の i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、盤面の i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)
入力値最終行の末尾に改行が1つ入ります。

期待する出力
1行の出力

・ 要素が ‘#’ になっているマスの y , x 座標を 1 行で出力してください。

y x
条件
すべてのテストケースにおいて、以下の条件をみたします。

・ 1 ≦ H, W ≦ 20
・ S は W 文字の文字列
・ S の各文字は '.' または '#'
・ '#' のマスは必ず1つ
入力例1

1 1
#
出力例1

0 0
入力例2

3 3
.#.
...
...
出力例2

0 1

解答と解説(入力例2の場合)

#1行目の入力値を各変数に代入
h,w = gets.split.map(&:to_i)

#縦(盤面の行数)の回数分繰り返す
h.times do |i|

#入力値を受け取り改行を打ち消しバラバラにする(例;[".", "#", "."])
  line = gets.chomp.chars
 
  #横(盤面の列数)の回数分繰り返す
  w.times do |j|
  #"#"のときに現在地を出力する(後置if)
    puts i.to_s + " " + j.to_s if line[j] == "#"
  end
end

2:座標系での移動・方角 (paizaランク C 相当)

開始時点の y , x 座標 と移動の回数 N が与えられます。
続く N 行で移動の方角 d_1 … d_N が与えられるので、与えられた順に移動をしたときの各移動後の y , x 座標 を答えてください。

ただし、図の通り、上側( y 軸の負の向き)を北とします。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値
Y X N       
d_1     
...     
d_N

・ 1 行目には、開始時点の y , x 座標を表す Y , X, 移動の回数 N が与えられます。
・ 続く N 行 (1 ≦ i ≦ N) には、盤面の i 回目の移動の方角を表す文字 d_i が与えられます。入力値最終行の末尾に改行が1つ入ります。

期待する出力
N 行での出力

・ 各移動後の y , x 座標を出力してください。

y_1 x_1
...
y_N x_N
条件
すべてのテストケースにおいて、以下の条件をみたします。

・ 0 ≦ Y, X, N ≦100
・ d_i は、N, S, E, W のいずれかでそれぞれ 北・南・東・西 を意味する。
入力例1

0 0 1
N
出力例1

-1 0
入力例2

5 10 4
N
W
E
S
出力例2

4 10
4 9
4 10
5 10

解答 普通にやる場合

y,x,n = gets.split.map(&:to_i)
command = readlines.map(&:chomp)
command.size.times do |i|
  if command[i] == "N"
     puts (y-1).to_s + " " + x.to_s 
      y += - 1
  elsif command[i] == "W"
     puts y.to_s + " " + (x-1).to_s
     x += - 1
  elsif command[i] == "E"
     puts y.to_s + " " + (x+1).to_s 
     x += + 1
  elsif command[i] == "S"
     puts (y + 1).to_s + " " + x.to_s 
     y += + 1
  end
end

別解 ハッシュを使う場合(こっちのほうがいいかも)

y, x, n = gets.split.map(&:to_i)
direction = { N: [-1, 0], S: [1, 0], E: [0, 1], W: [0, -1] }
n.times do
  command = gets.chomp
  y += direction[command.to_sym][0]
  x += direction[command.to_sym][1]
  puts y.to_s + ' ' + x.to_s
end

その4に続きます

VueとRailsで作成したアプリで得た知見と備忘録5(Firebase)

目標:すぐに思い出せるようにざっくり解説する。

Firebaseのプロジェクト作成

0:Firebaseとは?

iOS/AndroidアプリWebアプリケーションの開発に活用できるプラットフォームです。
利用できるサービスはデータベース、分析、認証、メッセージ送信機能など、様々です。主に認証機能を利用します。

1:プロジェクトの作成

https://firebase.google.com/?hl=ja

↑で公式サイトにアクセスします。

新規登録をして、使ってみるをクリックします。

プロジェクトを新規作成します。

2:NuxtとFirebaseのプロジェクトを関連づけ
</>

↑のようなアイコンをクリックします。

アプリ名を入れ、
FirebaseのHostingにチェックを入れ、
アプリを登録をクリックします。

3:Firebase CLIのインストール

frontディレクトリで以下のコマンドをします。

npm install -g firebase-tools

4:Firebase SDKのインストール

npm install firebase

5:設定ファイルの作成

front/pluginsのディレクトリの中にfirebase.jsという新規ファイルを作成し、
以下のように記述します。

front/plugins/firebase.js

import firebase from "firebase/compat/app"
import "firebase/compat/auth"

const fbConfig = {
    apiKey: process.env.API_KEY,
    authDomain: process.env.AUTH_DOMAIN,
    projectId: process.env.PROJECT_ID,
};
firebase.initializeApp(fbConfig)

export default firebase

firebase.initializeApp(fbConfig)で各種設定を引数にしてFirebase アプリオブジェクト作成しています。

それをexportすることによって、
このnuxtのプロジェクト内からFirebaseへアクセスできるようになります。
それぞれの設定の情報は外部から見えないよう.envファイルに定義します。

FireBaseの歯車のアイコンからプロジェクトの設定

下にスクロールすることで設定情報がわかるので

.envに記述します””の間にコピペする。

API_ENDPOINT="http://localhost:3000"

API_KEY="*************************"
AUTH_DOMAIN="*********************"
PROJECT_ID="**********************"

6:設定が反映できているかを確認

front/pages/index.vueに以下の記述をします。

  export default {
    components: {
      AddTodo,
      TodoList,
    },
    data() {
      return {
        todos: [],
      };
    },
    // 追加
    created() {
      console.log("API_KEY:", process.env.API_KEY);
    },
    methods: {
      async addTodo(title) {
        await axios.post("/v1/todos", { title })
        this.todos.push({
          title
        });
      },
    },
  };

サーバーを立ち上げて確認します。

npm run dev

http://localhost:8080/

↑にアクセスして検証ツールでapikeyが出力されていればOKです!

7:Firebaseの機能を用いた新規登録機能の実装

Firebase Authenticationとは

Firebase Authenticationとはユーザー認証の機能を提供してくれて、
ユーザの情報をFirebase上に保存してくれるサービスです。
メールアドレスパスワードを使った認証方法を使用していきます。

8:Firebase Authenticationのメールアドレス認証方式をオンにする

Firebase→作成したプロジェクトを選択

左側のナビゲーションペインからAuthenticationを選択し、始めるをクリックします。

Sign-in methodをクリックして、次にメール/パス…となっている部分をクリックします。

有効にするチェックをONにした後で保存を押します。

9:新規登録した際にFirebaseの認証を行い、RailsAPIのDBにもユーザーのレコードが保存されるようにする。

新規登録画面の作成

新規登録の画面を作成します。
front/pagesの配下に、signup.vue作成して、フロント部分を記述します

front/pages/signup.vue

  <v-btn class="mr-4" @click="signup">submit</v-btn>
  const res = await firebase
    .auth()
    .createUserWithEmailAndPassword(this.email, this.password)

↑新規登録用の情報を入力してsubmitボタンを押したとき、async signup()が発火します。これにより、メールアドレスとパスワードを使用して、Authenticationの機能を利用できます。

 .catch(error => {
    this.error = (code => {
      switch (code) {
        case "auth/email-already-in-use":
          return "既にそのメールアドレスは使われています";
        case "auth/wrong-password":
          return "※パスワードが正しくありません";
        case "auth/weak-password":
          return "※パスワードは最低6文字以上にしてください";
        default:
          return "※メールアドレスとパスワードをご確認ください";
      }
    })(error.code);
  });

.catchは、エラーがあった際の実装です。各種エラーに対してメッセージを設定し、
<p v-if="error" class="errors">{{ error }}</p>で表示をしています。

v-ifに関しては、指定している値が存在しているときにのみ、
タグで囲んでいる部分が描画されます。
エラーが発生した場合、data()で定義されているerrorプロパティの値が入るので、
この部分が描画されるという訳です。
エラーがない場合はもちろん、表示はされません。

  const user = {
    email: res.user.email,
    name: this.name,
    uid: res.user.uid
  };

  await axios
    .post("/v1/users", {
      user
    })
    .catch(err => {
      console.log({
        err
      });
    });

Firebase側の処理が終わったら、const user =の部分でFirebaseから帰ってきた値を元に、userのオブジェクト情報を作成しています。

その後、await axiosを使用して、作成したユーザー情報をもとに、
APIへpostのリクエストを行なっています。

/v1/usersへのPOSTリクエストはどのアクションに対応しているか調べてみると、UsersControllerのcreateアクションに対応していることがわかります。

api/app/controllers/v1/users_controller.rb
  def create
    user = User.new(user_params)
    if user.save
      render json: user
    else
      render json: user.errors
    end
  end

  # 中略
  private

  def user_params
    params.require(:user).permit(:name, :email, :uid)
  end

Userクラスのインスタンスを、ストロングパラメータで受け取った引数を元に作成し、保存しています。
これでバックエンド側でもユーザーが作成され、DBに保存されます。

Submitをクリックしたら、ブラウザはTODO一覧の画面に移動できます。
こちらは、async signup()の処理の最後にあるthis.$router.push(“/”);の部分が実行されるからです。

VueとRailsで作成したアプリで得た知見と備忘録4(AxiosとCORS)

3から続いています

目標:すぐに思い出せるようにざっくり解説する。

流れ

  1. フロントエンドからサーバーサイドのリクエスト用にAxiosを設定します。
  2. サーバーサイドでリクエストを受け入れられる用にCORSの設定をします。
  3. TODOの登録のリクエストを送り、保存されるか確認します。

前提知識

Axiosとは?

フロントエンドからバックエンドにリクエストを送るときに使っていくライブラリで
ブラウザやNode.js上で動くPromiseベースのHTTPクライアントです。

Promiseとは

JavaScriptの非同期処理の完了もしくは失敗を表すオブジェクトで、
非同期処理が完了したときに結果を返してくれます。

HTTPクライアントとは

GET・POST・PATCHなどのHTTPリクエストを送信して、
そのレスポンスを受信できるものです。

Axiosを利用するJavaScript非同期処理を行う際にHTTPリクエスト送って、レスポンス返してもらい、何らかの処理行うことができるようになります。

4章:APIの連携をする

1:フロントからサーバーサイドのリクエスト用にaxiosを設定する

Axiosの設定:baseURLの設定

baseURLは環境変数を設定することで設定できます。
Nuxt.jsで環境変数を扱う際は@nuxtjs/dotenvというライブラリを入れます。

frontのディレクトリで行います。

npm install --save-dev @nuxtjs/dotenv

front/nuxt.config.jsにbuildModulesという部分があるので、そこに追記します。

'@nuxtjs/dotenv', // 追記

.envを以下のような階層構造で作成します。

nuxt-rails-todo
├── api
└── front
  └── .env

そして以下のように記述します。

API_ENDPOINT="http://localhost:3000"

Railsはサーバーを起動すると、デフォルトでポート3000が使用されるためです。

Nuxtでもポート3000を使っているため衝突してしまいます。

front/nuxt.config.jsを変更することでNuxt側を変更します。

front/nuxt.config.js

import colors from 'vuetify/es5/util/colors'

export default {
 server: {
  port: 8080 
},

これで、npm run devを実行すると、8080番ポートが使用され、
Railsと同じポートでぶつかることもなくなります。

npm run devを実行して、http://localhost:8080/にアクセスして、
ToDoの一覧画面が出ればOKです。

2:サーバーサイドでリクエストを受け入れられるようにCORSの設定をする

CORSとは?

Cross-origin Resource Sharingの略称(オリジン間のリソース共有)

役割は同一オリジンポリシー(Same-Origin Policy)というルールによって
設けられた制限を緩めるものです。

同一制限ポリシーとは?

同一オリジンポリシーとは、ウェブブラウザに設けられた、
セキュリティ攻撃などの防止のための仕組みです。
異なるオリジン間のリソースへのアクセスに制約をかけるものです。

オリジンとは、URIのスキーム、ホスト、ポート番号の組み合わせのことです。

例えば、http://localhost:3000というURIがあったときに、オリジンは以下のように分解できます。

http: スキーム
localhost: ホスト
3000: ポート番号

異なるオリジン間でリソースにアクセスしようとした時に、ウェブブラウザが制限をかけてくれる仕組みのことです。

CORSは何のために設定する?

同一オリジンポリシーというルールによって設けられた制限を緩めるためで
作成しているアプリは、フロントエンドバックエンドが分かれています。
フロントエンドからバックエンドへ何かリクエストを行うと、この同一制限ポリシーに引っかかってしまい、アプリが意図したように動作しなくなってしまいます。
そのため、CORSという部分的に同一オリジンポリシーを解除する設定をしてあげる必要があります。

3:RailsAPIでのCORS設定方法

Gemのインストール

RailsでCORSを許可するために必要なGemを入れていきます。

デフォルトでコメントアウトされているので、それを外します。

api/Gemfile

# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
gem 'rack-cors'

以下のコマンドでインストールします

cd ../api
bundle install

4:CORSの設定ファイルの編集

api/config/initializers/cors.rbで以下のように記述します。

#コメントアウトを外して8080にする
Rails.application.config.middleware.insert_before 0, Rack::Cors do
 allow do
  origins 'localhost:8080' 

  resource '*',
 headers: :any,
 methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end

5:Nuxt側のリクエスト部分を修正する

front/pages/index.vueを開いて、<script>の中に以下のコードを追加します。

front/pages/index.vueのスクリプト部分から追記
<script>
  import AddTodo from "@/components/AddTodo";
  import TodoList from "@/components/TodoList";
  import axios from "@/plugins/axios";  // サーバーサイドにHTTPリクエストをするために、axiosを追加

  export default {
    components: {
      AddTodo,
      TodoList,
    },
    data() {
      return {
        todos: [],
      };
    },
    methods: {
      async addTodo(title) { // フロントエンドの処理を動かしつつ、サーバーサイドでもToDoの登録を行いたいため、非同期でサーバーサイドにリクエストを送る
        await axios.post("/v1/todos", { title }); // awaitで下の処理が先に実行されるのを防ぐ。
        this.todos.push({
          title
        });
      },
    },
  };
</script>

↑のasyncawaitは作成ボタンを押したら、先にサーバーサイド側でToDoを登録する処理を確実に行いたいためです。

axios.post("/v1/todos", { title });

↑ではTODOのタイトルを値として、サーバーサイド(API)のv1/todosというURLにPOSTリクエストが送信できるようにしています。

その5に続く

VueとRailsで作成したアプリで得た知見と備忘録3(ToDoのフロント作成)

2から続いています

目標:処理の流れを忘れた時にすぐに思い出せるようにざっくり解説する。

この章で目指すこと

・AddTodo.vue作成ボタンを押し、$emitを使ってイベント名と共に値を親コンポーネントへ渡す
・親コンポーネントのindex.vue$emitを使って渡ってきた、イベントをトリガーに、methodsと紐づけて、値を子コンポーネントに渡す

・子コンポーネントTodoList.vueで、渡された値をprops登録しておく事で動的な値として扱えるようにする

3章:ToDo機能のページ作成

0:Nuxtの基礎知識(コンポーネント)

Nuxt(Vue.js)のコンポーネントとは?

コンポーネントとは、再利用可能な部品群を指すことが多いです。

Nuxt(Vue.js)におけるコンポーネントは、
再利用可能なVueのインスタンスで、一度作成すれば他の場所で自由に呼び出すことができます。

例えば「ボタンのコンポーネント」を一度作成すれば、
どこのページでも呼び出すことができるようになり
それぞれのページで、毎回ボタンのコードを書く必要がなくなります。
Railsにある部分テンプレートという機能と非常に似ているのがこのコンポーネントです。

0.5:v-タグとVuetifyについて

.vueファイルのコンポーネントでは、<template>というタグの中へ、
コンポーネントとして表示したい内容を書きます。
<v-form>というタグで囲まれたToDo追加のフォームが、
コンポーネントとして表示されます。

例↓

<v-text-field v-model="title" :counter="10" label="todo" required></v-text-field>

v-model="title"とすることで、<script>data()の中で宣言されているtitleとデータを紐づけることができます。

1:AddTodoコンポーネントの作成

frontディレクトリの中に、componentsというディレクトリがあります。
Nuxtのコンポーネントはこのディレクトリの中に作成します。
componentsの中に、AddTodo.vueというファイルを作成し表示したい部分を記述します。

front/components/AddTodo.vue

<template>
 <v-form>
  <v-container>
   <v-row>
    <v-col cols="12" md="4">
     <v-text-field v-model="title" counter="10" label="todo" required></v-text-field>
   </v-col>
    <v-col cols="12" md="4">
      <v-btn @click="handleSubmit">作成</v-btn>
    </v-col>
   </v-row>
  </v-container>
 </v-form>
</template>

<script>
export default {
 data() {
  return {
   title: ""
  };
},
methods: {
  handleSubmit() {
   this.title = "";
  }
 }
};
</script>
<style>
</style>

2:ToDo登録の流れ(↑ので説明)

1:v-model="title"<script>data()の中で宣言されているtitleの値は空なので、
このフォームには初期値として何も入りません。
2:<v-btn @click="handleSubmit">作成</v-btn>
@clickという記法を使って、ボタンをクリックしたとき、
methodsの中で定義した関数を呼び出すことができます。
具体的には、以下の部分が呼び出されます。

methods: {
  handleSubmit() {
  this.title = "";
 },
},

3:ToDoリストコンポーネントの作成

TodoList.vueというファイルを作成します。TodoList.vueに以下の記述を追加します。

<template>
  <v-card>
    <v-card-title>
      Todoリスト
    <v-spacer></v-spacer>
     <v-text-field v-model="search" label="Search" single-line hide-details></v-text-field>
    </v-card-title>
     <v-data-table :headers="headers" :items="todos" :search="search"></v-data-table>
 </v-card>
</template>

<script>
export default {
 data() {
  return {
  todos: [
{
title: "test",
username: "suzutuki"
}
],
search: "",
headers: [
{
  text: "タイトル",
  align: "left",
  sortable: false,
  value: "title"
},
 { text: "ユーザー名", 
 value: "username"
}
]
};
}
};
</script>

<style>
</style>

<v-card>の中にToDoリストのタイトルテーブルを表示しています。

<v-text-field v-model="search" label="Search" single-line hide-details></v-text-field>

v-modelで渡している値は検索バーに入っている初期値です。

a:コロン記法(v-bindの省略)

各プロパティに:(コロン)がたくさんついています。
Vueのv-bindの省略記法で、Vueオブジェクトの変数の値とHTMLを結びつけることができます。
:headersというプロパティにはvueのheadersという変数の値が当てられていますが、
このコロンを消してしまうとエラーが起こります。
vueのオブジェクトが渡されるはずの場所に、”headers”という文字列が渡されてしまうためです。データを渡したいのに、”headers”という文字列が渡されてしまいます。

front/components/TodoList.vue

headers: [
{
 text: "タイトル",
 align: "left",
 value: "title"
},
{ 
 text: "ユーザー名", 
 value: "username" 
}
]

4-1:各プロパティについて(headers)

テーブルのヘッダーとなるプロパティを指定できます。

front/components/TodoList.vue

headers: [
{
text: "タイトル",  // 先頭のヘッダーの内容
align: "left",   // テーブルに対して文字を左寄せ
value: "title"   // どの値をその列に表示させるか
},
{ text: "ユーザー名",
value: "username"
}
]

textには実際にヘッダーの部分に出力させる文字が入りalignは文字の詰め方を指定、
valueは、itemsのプロパティに渡される名前と同様の値が入ります。

※ここを間違えると、値が表示されません(エラーが起こっているようには見えないので注意)。

itemsとして以下のオブジェクトが渡されてきます。

front/components/TodoList.vue

todos: [
{
  title: "test",
  username: "suzutuki"
}
],

titleusernameというプロパティがあります。

titleheadersvalueがtitleの列に、
usernameheadersのvalueがusernameの列へ表示されます。

4-2:各プロパティについて(items)

実際にテーブルへ表示させる値を指定します。
この時、表示させたいプロパティ名を、headersvalueと揃えないとエラーになります!

4-3:各プロパティについて(search)

表示されているToDo(items)をフィルターするときの文字列を指定しています。
v-bindされてsearchという値が指定されています。
これは<v-text-field>で指定されている値です。

front/components/TodoList.vue

<v-text-field v-model="search" label="Search" single-line hide-details></v-text-field>

v-modelsearchという値を指定しています。
このテキストフィールドで入力した値が、searchという値に入り
それがそのまま<v-text-table>searchプロパティの値として利用される訳です。

<v-text-field>に入れた値で、<v-text-table>のToDoをフィルタリングできるという訳です。

5:コンポーネントを表示させるページの作成

index.vueの作成

ページのファイルを作成します。

front/pagesというディレクトリが見つかります。
このディレクトリは、アプリケーションのビューおよび、ルートが格納される場所です。
Nuxtはこのディレクトリ内のすべての .vueファイルを読み込んで、
ルーターの設定を自動的に作成してくれます。

サーバーを立てている状態でhttp://localhost:3000/にアクセスするとindex.vueが表示されます。
これはNuxtで元々の設定で、ルートページに当たるファイルです。

front/pages/index.vueに以下の記述を追加します。

<template>
 <div>
  <AddTodo @submit="addTodo" />
  <TodoList :todos="todos" />
 </div>
</template>

<script>
import AddTodo from "@/components/AddTodo";
import TodoList from "@/components/TodoList";

export default {
 components: {
 AddTodo,
 TodoList,
},
 data() {
  return {
  todos: [],
};
},
methods: {
  addTodo(title) {
   this.todos.push({
    title
  });
 },
},
};
</script>
<style>
</style>

コンポーネントの登録

<script>の中でimportし、importしたコンポーネント
exportの中でcomponentsに登録します。
このように記述することで、<template>の中で作成したコンポーネント
HTMLタグのように使用できます。

@エイリアスと言って、プロジェクトのトップディレクトリを指せる、Nuxtで使える便利な記号
componentsディレクトリの中で作成したAddTodo.vueとTodoList.vueをimportしています。

コンポーネントは、以下のようにタグとして使用しています

front/pages/index.vue

<template>
 <div>
  <AddTodo @submit="addTodo" />
  <TodoList :todos="todos" />
 </div>
</template>

サーバーを立ち上げて表示がおかしくないか確認します。

cd front
npm run dev

http://localhost:3000/

その4に続きます。

6:ToDo登録から表示までの実装

ToDoの追加コンポーネントで作成したToDoを親コンポーネントに伝達してToDoリストコンポーネントに表示

index.vueを親コンポーネントと呼びます。
<AddTodo>コンポーネントと、<TodoList>コンポーネントが、
index.vueに表示されている状態です。
ページの中で、さらにコンポーネントを呼び出しているので、親に当たるのです。

AddTodo.vueから親コンポーネントに登録したいデータを渡す

AddTodo.vue

this.$emit("submit", this.title); // この行を追加します。

↑イベントをトリガーしたいときに使用します。

引数を2つとっており、第一引数のsubmitイベント名、第二引数のthis.titleがイベントとして渡される値になります。
submitというイベント名が親コンポーネントであるindex.vueに渡されます。
親コンポーネント側で「submitと言うイベントが起こったらこの処理を実行する」と言う処理を書いておけば、トリガーになってくれると言う訳です。

7:ToDoの情報を親コンポーネントで受け取って、TodoList.vueに渡す

index.vue

<template>
 <div>
  <!-- divの部分 -->
  <AddTodo @submit="addTodo" /> 
  <TodoList :todos="todos" /> 
 </div>
</template>

@submit=”addTodo”について

v-on:@submit=”addTodo”の省略形です。
AddTodo.vueからsubmitというイベントが来たら、methodsに登録したaddTodo(title)を呼び出します。

リスナーと呼ばれたりします。

このリスナーは、AddTodo.vueを表示するためのコンポーネントに書いてあるので、
AddTodo.vueのイベントを拾ってくれます。

addTodo(title)の引数には自動的にAddTodo.vueで渡したthis.titleが引数として渡されます。

リスナーのコールバック関数に渡されるというのはこれら一連の流れのことです。
$emitを使うだけで、親コンポーネントへ、イベントとコールバックで呼ばれる
メソッドに引数を渡すことができます。

これら一連の動作により、↓の処理が実行されます。

front/pages/index.vue

this.todos.push({
  title
});

data()で宣言されているtodosという配列の中に、AddTodo.vue登録しようとしたToDoの
タイトルが入ります。

最後にそのtodosという値を:todos=”todos”の部分で、TodoList.vue渡しています
=の右側のtodosが、今回渡している値で、左側の:todosは、TodoList.vueで使っている値になります。

8:TodoList.vue内で親コンポーネントから受け取ったTODOを表示させる

front/components/TodoList.vueのscriptの部分を変更します。

<script>
export default {
 props: ["todos"],
 data() {
  return {
  search: "",
// 中略

props: [“todos”]というコードを追加して、todosというプロパティを削除しました。
propsと言うのは、親コンポーネントから子コンポーネントに値を渡す時に使います。

親コンポーネントindex.vueでは:todos=”todos”の部分で、
todosというデータをTodoList.vueに渡していました。
この左側の:todosに当たる部分が、propsに定義しているコードになります。

親から渡してきた値を紐づけることで動的にpropsに登録した値を書き換えています。

親がtodosを更新すると、:todosとpropsにtodosが登録してあることによって、
<v-data-table>の:items=”todos”の部分にて、テーブルのToDoが動的に追加されます。

サーバーを起動して、トップ画面でToDoの値を入れ、送信ボタンを押して
登録できていたらOKです。

その4に続きます

VueとRailsで作成したアプリで得た知見と備忘録2(Nuxtプロジェクトの作成)

1から続いています

目標:忘れた時にすぐに思い出せるようにざっくり解説する。

2章:Nuxtプロジェクトの作成 (Front)

ホームディレクトリに移動し、pwdで場所を確認します。

pwd
/Users/<ユーザー名>/Desktop/アプリ名

1:frontという名前でアプリを作成する(名前はわかりやすければOKです。)

npx create-nuxt-app front

様々な質問に答えていきます。

Need to install the following packages:
create-nuxt-app
Ok to proceed? (y)

yを入力しreturnを入力してください。
Nuxtのプロジェクト作成に必要なパッケージが入っていない場合に出ます。

create-nuxt-app v3.6.0
✨ Generating Nuxt.js project in front
? Project name: (front)

プロジェクト名の決定についてです。returnを入力します。

? Programming language: (Use arrow keys)
❯ JavaScript 
TypeScript

JavaScriptを使用するので、そのままreturnを入力します。

? Package manager: (Use arrow keys)
❯ Yarn 
Npm

Npmを使用したいので、Npmを選択します。

? UI framework: (Use arrow keys)
None 
Ant Design Vue 
BalmUI 
Bootstrap Vue 
Buefy 
Chakra UI 
Element 
Framevuerk 
Oruga 
Tachyons 
Tailwind CSS 
Windi CSS 
Vant 
View UI 
Vuesax 
consoleWindi CSS 
Vant 
View UI 
Vuesax 
❯ Vuetify.js 
None 
Ant Design Vue 
BalmUI 
Bootstrap Vue

Vuetify.jsを選択します。

? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ Axios - Promise based HTTP client
◯ Progressive Web App (PWA)
◯ Content - Git-based headless CMS

何のモジュールを使うかに関してです。
Axiosを使用してapi側と通信をするので、Axiosを選択します。

? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ ESLint
◯ Prettier
◯ Lint staged files
◯ StyleLint
◯ Commitlint

潜在的なエラーについてコードを分析するリンティングツールの選択です。
使用しないため、スペースキーを押さずにreturnを入力します。

? Testing framework: (Use arrow keys)
❯ None 
Jest 
AVA 
WebdriverIO 
Nightwatch

テストのフレームワークの選択です。
使用しないため、Noneを選択します。

? Rendering mode: (Use arrow keys)
❯ Universal (SSR / SSG) 
Single Page App

レンダリングの設定です。
SPAを作成するので、Single Page Appを選択します。

? Deployment target: (Use arrow keys)
❯ Server (Node.js hosting) 
Static (Static/Jamstack hosting)

デプロイホスティングという、デプロイ後のファイルの公開に関する方法の設定です。
Serverを選択します。

? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ jsconfig.json (Recommended for VS Code if you're not using typescript)
◯ Semantic Pull Requests
◯ Dependabot (For auto-updating dependencies, GitHub only)

開発者ツールです。特に使用しないので、retuenを入力します。

? What is your GitHub username? (登録されている場合はあなたのユーザー名が表示されます)

Githubとの連携設定です。名前が表示されている場合はそのままreturnを入力、
ない場合はidを入力してreturnを入力します。

? Version control system: (Use arrow keys)
❯ Git 
None

いよいよ最後です。長かったですね!

バージョン管理システムの確認です。apiとfrontを同時にGitで管理するため、
このプロジェクト内にはGitのリポジトリは作りません。Noneにカーソルを合わせてreturnを入力します。

続きを読む

VueとRailsで作成したアプリで得た知見と備忘録1(環境構築+RailsAPI実装方法)

目標:忘れた時にすぐに思い出せるようにざっくり解説する。

0章:Rails環境構築

0:Homebrewをインストール

Homebrewからインストールする

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew -v

1:rbenvをインストール

brew install rbenv
rbenv --version

2:rbenvにPATHを通す

echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile
source ~/.bash_profile

3:Rubyをインストール

rbenv install 3.0.0

4:ローカルで使うRubyのバージョンを指定

rbenv local 3.0.0
ruby -v

5:Bundlerをインストール

gem install bundler
bundler -v

6:yarnをインストール(yarnはJavaScriptのライブラリの利用に必要なパッケージマネージャ)

brew install yarn
yarn -v

7:Railsをインストール

gem install rails -v 6.1.3.1
rails -v

1:NuxtJSの環境構築(macOS)

パッケージ管理ツールのインストール

Nuxtというフレームワークを使用するためには、
様々なプログラムをインストールする必要があります。

他のフレームワーク(Railsとか)と同じように、パッケージ管理ツールをインストールしていきます。

Railsにはパッケージ管理ツールにGem、
さらにそのGemを管理するBundlerなどが存在しています。
Nuxtの環境構築としてはNpmを利用します。

Npmの説明

Npmの正式名称はNode Package Managerです。

NpmはNode.jsというプログラムに組み込まれています。

Node.jsはJavaScriptを実行する環境です。

本来、JavaScriptはWebブラウザに組み込まれていて、
ブラウザ上でしか実行できませんでした。

しかし、Node.jsはその場でのスクリプトの実行を可能にしてくれます。
さまざまなライブラリが存在しているのですが、それらのインストールを行うツールとして、Node.jsに組み込まれているのが今回使用するNpmなのです。

他のJavaScriptライブラリNpmを使ってインストールができるようになってきています。
NuxtもNpmでインストールできるため、Node.jsをインストールするというわけです。

8:Node.jsとNpmのインストールと確認

brew install node
node -v
npm -v

以上で環境構築は完了です。

1章:作り方の概要(RailsAPI の実装)

1:Todoモデルとテーブルの作成

Todoモデルの作成

rails g model Todo title:string user_id:integer

マイグレートして反映します

rails db:migrate

2:Todoのコントローラーの作成
nuxt-rails-todo/apiで行う

rails g controller v1::todos

v1にした理由:バージョンを指定してできるようにした

3:コントローラーに基本的なCRUD機能を追加してjson形式に変換してユーザーに返すようにする

render json: todo

理由:JavaScriptで非同期通信を行ってSPAを作成する際に、フロントエンド側でJSONのデータを使用するからです。

4:Todoのルーティングを作成する

routes.rbで行う

Rails.application.routes.draw do
  namespace :v1 do
  resources :todos, only: [:index, :create, :destroy]
end
end

Railsのルーティングではnamespaceという機能を使うことができ、URLのHelperとPathに指定した文字がつきます。

http://localhost:3000/v1/todos

↑で[]の配列が表示されていればAPIとして機能していることが確認できます!

5:Userモデルとテーブルの作成

apiディレクトリで行う

モデルの作成とmigrate

rails g model User name:string email:string uid:string
rails db:migrate

6:Userのコントローラーの作成

rails g controller v1::users

indexとcreateの機能とストロングパラメーターを実装して必ず以下の記述を追加する(JSON形式で返す)

render json: user
# create失敗したときはエラーを返す
render json: user.errors

7:Userのルーティングを作成する

api/config/routes.rbで行う

Rails.application.routes.draw do

namespace :v1 do
  resources :todos, only: [:index, :create, :destroy]
  resources :users, only: [:index, :create]
end
end

http://localhost:3000/v1/users

↑にアクセスして空配列[]を確認できればOKです。

8:JSON形式のデータを整形する(active_model_serializer)

active_model_serializersの導入

Gemfileに以下の記述を追加します。

gem 'active_model_serializers'

Gemをインストールします。

bundle install

9:JSON形式のデータを整形する(apiディレクトリで行う)

rails g serializer user

api/app/serializers/user_serializer.rbで以下の記述を追加します。

class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :email
  has_many :todos 
end

todoのserializerも作成します。

rails g serializer todo

user.nameと毎回書かないようにusernameというメソッドを作り、名前の値をTodoと一緒に返せるように設定します。

api/app/serializers/todo_serializer.rb

class UserSerializer < ActiveModel::Serializer
  attributes :id, :title, :user_id, :username
  belongs_to :user

  def username
    object.user.name
  end
end

10:モデルのアソシエーションの実装

api/app/models/todo.rb

class Todo < ApplicationRecord
  belongs_to :user
end

users.rb

class User < ApplicationRecord
  has_many :todos
end

javascript

{ 
  "user" : {
  "user_id" : "ユーザーのID",
  "username": "ユーザーの名前",
  "todo" : 
  {
    "title" : "todoのタイトル"
  }
}

↑のような、綺麗なJSONを返すことができるようになりました。

http://localhost:3000/v1/users

↑にアクセスして[]という配列が帰っていれば正しい実装ができています。

2に続きます。