20190502のvue.jsに関する記事は9件です。

初心者がVue.jsのVeeValidateでハマったこと

送信ボタンを押してもバリデートがかからない。

という問題。

フォーム自体にはリアルタイムでちゃんとバリデーションがかかる。

でも送信ボタンを押したらバリデーションスルーして進んじゃう。

なんでだ…。

VueやVeeValidateのインストールは割愛しますが、CDNで読み込んでいます。

new Vue({
  el: '#valid',
  methods: {
    validate() {
      this.$validator.validateAll()
       .then((result) => {

  //ここにifとかでtureの場合falseの場合の処理を書かないといけなかった! 
        if(result=== true)
        {
        trueの時の処理 }  
        else{  falseの時の処理   }

      });
    }
  }
});

なぜか巷の情報の多くではこの分岐させる処理がなくて、バリデーションスルーして次のページに行ってしまっていたので、一応そこでつまっている方のために残しておきます。

(もしかしたらCDN版特有でnpmとかなら必要ない処理なのかな…?)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js&Nuxt.jsで初めてアプリを自作したときに躓いた点

はじめに

Vue.js&Nuxt.js超入門(秀和システム)を読んで勉強したのでアプリを作った。

20190501171617.png
「囀暦」というアプリで「てんれき」と読む。Twitterでつぶやかれている内容の中で「○月○日にxxします!」という告知をカレンダー上にプロットするもの。以前にもRailsでちょこっと作っていたが今回改めて作り直してみた次第。

教科書通り作るのとはわけが違う。エラー&エラーに悩まされましたのでここにまとめます。

構成

  • Vue.js(フロント)
  • Nuxt.js(バック)
  • Firebase(認証)

また、ライブラリとして下記を使用。

  • Materialize
  • fullcalendar

初期設定

$ npx create-nuxt-app tenreki
> Generating Nuxt.js project in /Users/hoshito/RubymineProjects/tenreki
? Project name tenreki
? Project description My primo Nuxt.js project
? Use a custom server framework none
? Choose features to install (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Use a custom UI framework none
? Use a custom test framework none
? Choose rendering mode spa
? Author name hoshito
? Choose a package manager npm

ディレクトリ構成

初めて扱うフレームワークだったので何をどこに置いたらいいのか、あのファイルどこにいったっけと探すのが大変だった。
20190502005558.png

nuxt.config.jsはいろいろ書いたので全部のせておく。

nuxt.config.js
import pkg from './package'
require('dotenv').config();
const {
  CONSUMER_KEY,
  CONSUMER_SECRET,
  ACCESS_TOKEN_KEY,
  ACCESS_TOKEN_SECRET
} = process.env;

export default {
  mode: 'spa',

  /*
  ** Headers of the page
  */
  head: {
    title: pkg.name,
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: pkg.description }
    ],
    script: [
      { src: "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js" },
      { src: "https://unpkg.com/popper.js"},
      { src: "https://unpkg.com/tooltip.js"}
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
      { rel: "stylesheet", href: "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"}
    ]
  },

  /*
  ** Customize the progress-bar color
  */
  loading: { color: '#fff' },

  /*
  ** Global CSS
  */
  css: [
  ],

  /*
  ** Plugins to load before mounting the App
  */
  plugins: [
    { src: '~/plugins/calendar.js', ssr: false }
  ],

  /*
  ** Nuxt.js modules
  */
  modules: [
    '@nuxtjs/dotenv',
  ],

  /*
  ** Build configuration
  */
  build: {
    /*
    ** You can extend webpack config here
    */
    extend(config, ctx) {
      config.node = {
        fs: "empty" // TwitterAPIを使うのに必要
      };
    }
  },
  serverMiddleware: [
    '~/server'
  ],
  env : {
    CONSUMER_KEY,
    CONSUMER_SECRET,
    ACCESS_TOKEN_KEY,
    ACCESS_TOKEN_SECRET
  }
}

躓いたところを一つずつ記載。

環境変数を使う

  • npm install --save @nuxtjs/dotenv
  • nuxt.config.jsに情報を記載
    • dotenvとかenvとか書かれているところ
    • この設定だけでけっこう散らばって記載しているけどこれがベストなのかは疑問
  • .envファイルに環境変数を記載(このファイルはコミットしない)
    • 下のような感じ CONSUMER_KEY="xxx" CONSUMER_SECRET="xxx" ACCESS_TOKEN_KEY="xxx" ACCESS_TOKEN_SECRET="xxx"

サーバ起動前に環境変数を設定する方法もありそうだったが、毎回設定するよりは外部ファイルに置いておく方が好みなのでこの方法を取った。

localStorage, sessionStorageを使用する際はSPAモードで実行

これは書籍にもあった。SPAとSSRの違いがよく分かっていないので(サーバレンダリングの有無だけ?)もう少し勉強する必要がある。

今回はsessionStorageにTwitterのトークン情報を保存している。

https://developer.twitter.com/en/docs/basics/authentication/guides/securing-keys-and-tokens.html

We would not normally recommend using LocalStorage, although there may be a few exceptions to this.

Using SessionStorage is normally more preferable than LocalStorage when thinking in terms of security.

Twitterのdevelopページにこう書かれていたので、localStorageではなくsessionStorageを使った。

TwitterAPIを使用するにあたって

  • npm install --save twitter
  • npm install --save axios fs net tls(依存するライブラリたち)

fsが使えずに下記のようなエラーが出た。

ERROR in ./node_modules/request/lib/har.js Module not found: Error: Can't resolve 'fs' in '/Users/hoshito/RubymineProjects/tenreki/node_modules/request/lib'

fsはファイルシステムのことで、SPAつまりクライアント側でファイルシステムモジュールを使おうとしてエラーになっていた。

下のように書くとエラーを回避できた。

nuxt.config.js
    extend(config, ctx) {
      config.node = {
        fs: "empty" // TwitterAPIを使うのに必要
      };
    }

SPAだとこれが動かずSSRだとこれが動かず、というのが多く混乱してしまう。

node-sassは(今だけ?)使えない

sassを使いたくてnpm install node-sassをしたところ脆弱性エラーで進めなかった。調べてみると最近出たエラーっぽい。解決策はいろいろ出てきている。

https://github.com/sass/node-sass/issues/2625
https://zatsuzen.com/nodejs/node-sass/

package.jsonでnode-gypを書き換えろ、という内容であるが、私のpackage.jsonは以下のようなものであり、どこをどう書き換えればよいのか分からずsassの導入を断念しました。

package.json
{
  "name": "tenreki",
  "version": "1.0.0",
  "description": "My primo Nuxt.js project",
  "author": "hoshito",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate"
  },
  "dependencies": {
    "@fullcalendar/core": "^4.1.0",
    "@fullcalendar/daygrid": "^4.1.0",
    "@fullcalendar/vue": "^4.1.0",
    "@nuxtjs/dotenv": "^1.3.0",
    "axios": "^0.18.0",
    "cross-env": "^5.2.0",
    "firebase": "^5.10.1",
    "fs": "0.0.1-security",
    "net": "^1.0.2",
    "nuxt": "^2.4.0",
    "sass": "^1.19.0",
    "tar": "^4.4.8",
    "tls": "0.0.1",
    "twitter": "^1.7.1"
  },
  "devDependencies": {
    "nodemon": "^1.18.9"
  }
}

ビジネスロジック

serverMiddlewareを使った。
https://ja.nuxtjs.org/api/configuration-servermiddleware/

page/list.vueから自身にAPIを叩くように使っている。

page/list.vueの一部
    methods: {
      getTweets(token, secret) {
        const self = this;
        const params = {token: token, secret: secret};
        axios.post("/api/tweets", params).then(res => {
          self.events = res.data;
        }).catch(err => {
          console.log(err);
        });
      },
server/index.jsの全部
import express from 'express'
import bodyParser from 'body-parser'
import {getClient, getTweetInfoForFullCalendar} from '../plugins/twitter'

const app = express()
// requestでjsonを扱えるように設定
app.use(bodyParser.urlencoded({extended: true}))
app.use(bodyParser.json())

app.post('/tweets', (req, res) => {
  let client = getClient(req.body.token, req.body.secret);
  // TODO: リクエストパラメータに入れる
  let params = {owner_screen_name: "hoshitostar", slug: "Love", count: 200};
  client.get('lists/statuses', params).then(tweets => {
    let result = getTweetInfoForFullCalendar(tweets);
    res.send(result);
  }).catch((errors) => {
    // TODO: error処理
    console.log(errors);
  });
})

module.exports = {
  path: '/api',
  handler: app
}

これも最初全然リクエストを投げられず、投げることができたと思ったらリクエストパラメータ取れなくて苦労した。

server/index.jsはRailsでいう(別にRailsじゃなくてもいいけど)Controllerの役割を担っていると思ったので、ロジックはplugins/twitterに入れた。本当はclient.get...の部分も含めて外に出そうと思ったけど、client.getの非同期処理でハマってしまったのでやめにした。

plugins/twitterの一部
// TwitterAPIのクライアントクラスを取得
function getClient(token, secret) {
  const Twitter = require('twitter');
  return new Twitter({
    consumer_key: process.env.CONSUMER_KEY,
    consumer_secret: process.env.CONSUMER_SECRET,
    access_token_key: token,
    access_token_secret: secret
  });
}

// --- 省略 ---

export {getClient, getTweetInfoForFullCalendar}

余談

2019/04/08から技術系のBlogとTwitterを始めてます。このQiitaの記事はブログに記載していたものをまとめたものになります。

Blog: https://hoshitostar.hatenablog.jp/
Twitter: @hoshitostar

Qiita vs ブログの話題はたまに見るけどやっぱり難しいですね。今回はまとまった記事になったのでQiitaに投稿しました。もっと良い書き方があれば教えていただけると嬉しいです。

アウトプットがんばるぞー

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vueでドロップダウンメニューを作成する

目標

下記のようなドロップダウンメニューを作成しましたので記載します。
今回はjQueryではなくVue.jsを使って作成してみました。
Rails+Vueあれば使い方はjQueryに近く、コードの記述量はjQueryよりも少なくて済む(気がする)と思っています:thinking:

・カテゴリ一覧をドロップダウンで表示させる
・カテゴリーの値はデータベースから取得する
・mouseoverでカテゴリの子要素を表示、mouseleaveで非表示となる
・クリックで各カテゴリページに遷移する

・Rails+Vue.jsで作成する

初学者のため不備等多々あるかと思いますが、参照記事は良い記事ばかりです。
間違いやより良い記述方法があればご教示ください:relieved:

スクリーンショット 2019-04-23 17.17.16.png
https://i.gyazo.com/24baf3b1c603ee655eb45ecf4d8e4fdf.mp4

①Vue.js導入

Webpackerのインストール

gem 'webpacker', github: 'rails/webpacker'
bundle install

yarnのインストール

brew install yarn

Webpackerインストール

rails webpacker:install


create  config/webpacker.yml
Copying webpack core config
      create  config/webpack
      create  config/webpack/development.js
      create  config/webpack/environment.js
      create  config/webpack/production.js
      create  config/webpack/test.js
Copying postcss.config.js to app root directory
      create  postcss.config.js
Copying babel.config.js to app root directory
      create  babel.config.js
Copying .browserslistrc to app root directory
      create  .browserslistrc
Creating JavaScript app source directory
      create  app/javascript
      create  app/javascript/packs/application.js
       apply  /Users/sotatakahashi/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/bundler/gems/webpacker-2387331b33c6/lib/install/binstubs.rb
  Copying binstubs
       exist    bin
      create    bin/webpack
      create    bin/webpack-dev-server
      append  .gitignore
Installing all JavaScript dependencies [4.0.2]
         run  yarn add @rails/webpacker from "."

:
✨  Done in 7.46s.
Webpacker successfully installed ? ?

↑終わったら絵文字が出ます(かわいい)

Vueインストール

rails webpacker:install:vue

これでRails上でVueを使う準備はOKです!
詳しくは下記記事にて。

参考記事

https://qiita.com/cohki0305/items/582c0f5ed0750e60c951
https://qiita.com/saongtx7/items/fdb77901e7fcf291e2ad

②カテゴリーメニューの作成

マウスオーバーとかは後にして、とりあえず見た目の部分を作っていきます。
(CSS割愛します)

.category_box
 .genre-box
   %ul.genre
     - @parents.each do |parent|
      = link_to category_path(parent.id), method: :get do
        %li.genre_list
         =parent.name
        .next-box
         .sub-box
          %ul.sub
           %li.sub_list
            .detail-box
             %a
              %ul.detail
               %li.detail_list

親、子、孫にそれぞれulとliを持たせて、リストを作成します。
基本はこの形を使ってドロップダウンメニューを作成していきます!

③マウスオーバーで表示/非表示を切り替える

.category_box{"v-show": "menuBool","v-on:mouseleave": "hiddenMenu"}
 #v-showなどはvueで使われるアクション、menuの表示非表示をこちらで設定
 .genre-box
  %ul.genre
   - @parents.each do |parent|
    = link_to category_path(parent.id), method: :get do
     %li.genre_list{'v-on:mouseover': "setCategoryInfo(#{parent.id})"}
      =parent.name
     .next-box{'v-show': 'categoryInfoBool',"v-on:mouseleave": "hiddenSubInfo"}
    #子カテゴリーのメニューの表示、非表示をこちらで設定
      .sub-box
       %a
        %ul.sub
         %li.sub_list{"v-on:mouseover": "setSubInfo(category.id)"}
          {{category.name}}
      #マウスオーバーした時に値を取得、表示させる。{{}}には表示する内容が入り、vueで切り替えられる!
       .detail-box{'v-show': 'SubInfoBool'}
        %a{"v-for": "sub in SubInfo","v-bind:href": "'/categories/' + sub.id"}
         %ul.detail
          %li.detail_list
           {{sub.name}}
new Vue({
      el:"#app",
      data:{
       categoryInfo:[],
        //子カテゴリーの情報を入れる箱
       SubInfo:[],
     //孫カテゴリーの情報を入れる箱
       menuBool: false,
       categoryInfoBool: false,
       SubInfoBool: false,
     //基本非表示にさせるため、falseとしておく
      },
      methods: {
        setCategoryInfo(id){
          axios.get(`/api/genres/${id}.json`)
      //axiosでapiを使う(理屈はまだわかっていません、、)
          .then(res => {
            this.categoryInfo = res.data;
            this.categoryInfoBool = true;
          });
        },
        setSubInfo(id){
          axios.get(`/api/genres/${id}.json`)
          .then(res => {
            this.SubInfo = res.data;
            this.SubInfoBool = true;
          });
        },
        showMenu:function(){
          return this.menuBool = true
        },
        hiddenMenu:function(){
          return this.menuBool = false;
        },
        hiddenCategory:function(){
          return this.categoryInfoBool = false;
        },
        hiddenSubInfo:function(){
          return this.SubInfoBool = false
        },
       //viewに乗っかった時にtrue,falseを切り替える
      }
    });

api/genre
show.json.jbuilder

json.array! @category_children do |category_child|
  json.name category_child.name
  json.id  category_child.id
end

④それぞれにリンクを貼る

%header#app.header
    .header__top
      .header__top__logo
        = link_to root_path do
          %img{alt: "mercari", src: "//www-mercari-jp.akamaized.net/assets/img/common/common/logo.svg?228111635"}/
      .header__top__search
        = form_tag(search_items_path, method: :get, class: "header__top__search__box") do
          %input.header__top__search__box__input{name: "keyword", placeholder: "何かお探しですか?", type: "search"}/
          %button.header__top__search__box__submit{type: "submit"}= fa_icon ("search lg")
          %form{"accept-charset" => "utf-8", :action => "/", :method => "get"}
    .header__bottom
      .header__bottom__leftside{"v-on:mouseleave": "hiddenMenu"}
        .header__bottom__leftside__left_box{"v-on:mouseover": "showMenu","v-on:mouseleave": "hiddenCategory"}
          = link_to categories_path, class: "header__bottom__leftside__left_box__content" do
            = fa_icon ("list-ul")
            %span カテゴリから探す

          -# ここからドロップダウンメニュー
          .category_box{"v-show": "menuBool","v-on:mouseleave": "hiddenMenu"}
            .genre-box
              %ul.genre
              - @parents.each do |parent|
                = link_to category_path(parent.id), method: :get do
                  %li.genre_list{'v-on:mouseover': "setCategoryInfo(#{parent.id})"}
                    =parent.name
            .next-box{'v-show': 'categoryInfoBool',"v-on:mouseleave": "hiddenSubInfo"}
              .sub-box
                %a{"v-for": "category in categoryInfo","v-bind:href": "'/categories/' + category.id"}
 #v-bindでaタグにリンクを貼っつける!
                  %ul.sub
                    %li.sub_list{"v-on:mouseover": "setSubInfo(category.id)"}
                      {{category.name}}
              .detail-box{'v-show': 'SubInfoBool'}
                %a{"v-for": "sub in SubInfo","v-bind:href": "'/categories/' + sub.id"}
                  %ul.detail
                    %li.detail_list
                      {{sub.name}}
          -# ここまでドロップダウンメニュー

        .header__bottom__leftside__right_box
          = link_to '', class: "header__bottom_box__leftside__right_box__content" do
            = fa_icon ("tag")
            %span ブランドから探す
      .header__bottom__rightside
        .header__bottom__rightside__bell
          = link_to '', class: "header__bottom__rightside__bell__content" do
            = fa_icon("bell lg")
            %span お知らせ
        .header__bottom__rightside__check
          = link_to '', class: "header__bottom__rightside__check__content" do
            %span
              = fa_icon("check lg")
              %span やることリスト
        .header__bottom__rightside__mypage
          = link_to '', class: "header__bottom__rightside__mypage__content" do
            %img.header__bottom__rightside__mypage__content__icon{alt: "", src: "//static.mercdn.net/images/member_photo_noimage_thumb.png", width: "32"}/
            %div マイページ
new Vue({
      el:"#app",
      data:{
       categoryInfo:[],
       SubInfo:[],
       menuBool: false,
       categoryInfoBool: false,
       SubInfoBool: false,
      },
      methods: {
        setCategoryInfo(id){
          axios.get(`/api/genres/${id}.json`)
          .then(res => {
            this.categoryInfo = res.data;
            this.categoryInfoBool = true;
          });
        },
        setSubInfo(id){
          axios.get(`/api/genres/${id}.json`)
          .then(res => {
            this.SubInfo = res.data;
            this.SubInfoBool = true;
          });
        },
        showMenu:function(){
          return this.menuBool = true
        },
        hiddenMenu:function(){
          return this.menuBool = false;
        },
        hiddenCategory:function(){
          return this.categoryInfoBool = false;
        },
        hiddenSubInfo:function(){
          return this.SubInfoBool = false
        },
      }
    });

かんせい!
Vue楽しいので何か違うこともやってみたいです!コンポーネント作るとかも楽しそう:unicorn:

参考記事

インストール〜作成までの流れ
https://qiita.com/cohki0305/items/a678b0b17c5b496c1de9
https://qiita.com/saongtx7/items/fdb77901e7fcf291e2ad
axiosの使い方
https://www.webprofessional.jp/fetching-data-third-party-api-vue-axios/
https://qiita.com/ryouzi/items/06cb0d4aa7b6527b3645
vue.jsでmouseoverした時だけ表示する方法
https://qiita.com/sukechansan/items/07a415e4e7e5ce358afc
vue.jsでのリンクの貼り方
https://qiita.com/asaokamei/items/6afa7e2f33207d041588

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue CLIに関するすること(メモ)

Vue-cliをするまで

https://qiita.com/567000/items/dde495d6a8ad1c25fa43

こちらのページがわかりやすかった。

https://techacademy.jp/magazine/16105

npmについても調べたページをメモ。

手順画像

1.VS Codeを開く
VS Code.jpg

2.ターミナルを開く(※コマンドプロンプトでもいいけどね)
Terminal.jpg

3.「cd (ファイルを保存するディレクトリ)」
cd.jpg

4.「vue create (プロジェクト名)」
vue create.jpg

※versionが古いと怒られる・・・

vue create is a Vue CLI 3 only command and you are using Vue CLI 2.9.6.
You may want to run the following to upgrade to Vue CLI 3:
npm uninstall -g vue-cli
npm install -g @vue/cli

(意訳)
「『vue create』は3からのコマンドなのにお前の古いよ」
「下のコマンド打って上げろ」

ver.jpg

5.保存したディレクトリを開く
created.jpg

6.できてる!
directory.jpg

つづく

つづく

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Macのターミナルで【Vue.js(Vue CLI 3系)】のvue コマンドを使えるようにするまでの話

そもそもなんでVue.js?

現在、WEBサイトのフロントエンド開発を主に仕事でしているのですが、フロントエンドエンジニアとしてJavaScriptのフレームワークのひとつぐらいまともにプログラミングできないとまずいよな〜と思ったのがきっかけです。
日本語のドキュメントもあり、フレームワークを調べてるときに比較的学習コストが少ないということで今回Vue.jsを選択しました。
現在は様々な機能が使えるVueCLIを使って開発するのがトレンドらしく、とりあえず触ってみよう的な試みです。

Vue.jsについてはこちらの記事に詳しく書いてありました。

Vue.js概要?

環境セットアップのための予備知識

OS環境 Mac OS 10.13.6(High Sierra)

VueCLI 3系をインストールするにあたり、以下のようなサイトを参考にしました。

10分で始めるVue.js(基本編)

Vue CLI 3 をインストールしプロジェクトを作成する方法

Homebrewからnodebrewをインストールして、Node.jsをインストールするまで

、、、。

いきなり、なじみのない言葉が、、、npm、homebrew、nodebrew、Node.js????

ということで調べて自分なりの解釈でざっくりと以下のように理解しました。

Node.js

WEBブラウザ以外でjsを動かすための環境のひとつ。
サーバサイドで動くJavaScriptのことでサーバサイドJavaScriptと呼ばれることもある。
従来のjavascriptととの違いは実行環境(内部的に構築されてるみたい)とライブラリからなっている(最初の時点である程度のモジュールが組み込まれているらしい)

Homebrew

Macのターミナル用の※パッケージ管理システム

※パッケージ管理システムとは
ターミナル用のAppleストアやgoogleストア的なもの。それらが目的に応じていろいろ用意されている。
Ruby→gem/PHP→compser/など

npm

タスクランナー/モジュール(webpackやbrowserifyなど)などの開発環境系のパッケージ管理するためのもの

nodebrew

node.jsのversion管理するためのパッケージ管理システム

パッケージはそれぞれ、依存関係にあり、Vue.jsをインストールするための流れはざっくりこんな感じ

HomebrewをインストールするためにX codeをインストール

nodebrewをインストールするため、Homebrewをインストール

Node.jsとnpmをインストールするため、nodebrewをインストール

VueCLIをインストールするため、Node.jsとnpmをインストール
(node.jsをインストールする際にnpmをインストールされる)

ようやく、VueCLIをインストール

セットアップ

それではいきましょうw

X codeをインストール

App storeからXcodeをインストール

コマンドライン・デベロッパーツールをインストール

ターミナルから$ xcode-select --installコマンドを発行すると
以下のようにインストールを要求されて

$ xcode-select --install
xcode-select: note: install requested for command line developer tools

同時にポップアップが出てくるので"インストール"を選択し利用規約に同意します。

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3137343238332f61356335323066322d383938372d353435322d653039382d6164343934363935613039652e706e67.png

Homebrewをインストール

Homebrewの公式サイトからコードをコピペしてEnterを押します。
途中でEnterの入力/インストールユーザのOSパスワードの入力を求められるので入力して進んでください。

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

インストールが確認するには以下のコマンドを実行できればインストールされたことになります。

$ brew doctor

nodebrewのインストール

$ brew install nodebrew

インストールを確認するには

$ nodebrew -v

情報が表示されたらインストールされてます!!

Node.jsとnpmのインストール

Node.jsの最新版をインストール

$ nodebrew install latest

Node.jsを有効にします。

nodebrew use 先程インストールしたNode.jsのver

nodeコマンドを使用するため、パスを通します。

#zshの場合は
echo "export PATH=$HOME/.nodebrew/current/bin:$PATH" >> ~/.zshrc

#bashの場合は
echo "export PATH=$HOME/.nodebrew/current/bin:$PATH" >> ~/.bashrc

先程追加したパスが動くようになり、nodeコマンドが使えるようになります。

#zshの場合は
$ source ~/.zshrc

#bashの場合は
$ source ~/.bashrc

npmはNode.jsとともにインストールされます。

npmがインストールしたか確認

$ npm -v

Node.jsがインストールしたか確認

$ node -v

バージョンが表示されればOKです!!

Vue CLI(Vue.js)のインストール

npm install -g @vue/cli

vueコマンドを使うためのパスを確認

npm bin -g
#/Users/xxxxxxx/npm/bin

vue コマンドを使用するためにパスを通す

$ export PATH=/Users/xxxxxxx/npm/bin:$PATH

vueのインストールを確認

$ vue --version

バージョンが表示されればOKです!!

おしまい

とりあえず、Vueを動かす環境づくりはこんな感じでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

el・data・データバインディング・リスト(Laravel使用Vue.js入門)#1

◆Vue.js利用準備

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

CDNを利用することで、ファイルをvue.jsのファイルをダウンロードすることなく利用できます。

◆データバインディング(表示しているものとデータの同期)

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>Vue.js</title>
    <link href="{{ asset('css/app.css') }}" rel="stylesheet" type="text/css">
</head>
<body>
    <div id="app">
        <h1>@{{ message }}</h1>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    const vue = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue.js!!'
        }
    });
</script>

僕は、laravelのbladeを利用させていただいているため、\@{{message}}と指定しておりますが、bladeを使用されていない方は{{message}}で動作すると思います。

new Vue({})でVueインスタンスを作成して、その中のオプションを指定することでVueアプリケーションを作成することができます。

elは、DOM要素のセレクタを指定します。ちなみにクラスでも指定できます。マウントと言ったりします。

dataは、上のel内で指定した範囲内で利用するデータをもつ変数です。

今回の場合ですと、画面にHello Vue.js!!がデータバインディングにより表示されます。
また、messageの内容を変えると画面に表示される文字も変更されます。これをリアクティブといいます。

バインディングは属性やクラスやスタイルも指定可能です。

◆リスト

<body>
    <div class="app">
        <ol>
            <li v-for="fruit in fruitList">
                @{{fruit}}
            </li>
        </ol>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    const vue = new Vue({
        el: '.app',
        data: {
            fruitList:[
                'ミカン', 'メロン', 'ブドウ'
            ]
        }
    });
</script>

v-forディレクティブを使うと、簡単にリストを配列内のデータを利用し、使用することができます。

◆イベント・条件分岐

先ほどのリストを条件分岐で表示非表示を切り替えてみる

<body>
    <div class="app">
        <ol>
            <li v-for="fruit in fruitList" v-if="show">
                @{{fruit}}
            </li>
        </ol>
        <button v-on:click="handleClick">表示切替</button>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
    const vue = new Vue({
        el: '.app',
        data: {
            fruitList: [
                'ミカン', 'メロン', 'ブドウ'
            ],
            show: true
        },
        methods: {
            handleClick: function () {
                this.show =! this.show
            }
        }
    });
</script>

v-onディレクティブを使用することで、DOMのイベントを検知できます。
v-on:clickの中に処理を記述することも可能ですが、処理は分けて記述するほうが良いと思います。this.showはdata内のshowを指しております。
elで指定した範囲内で利用するメソッドを、methodsプロパティ内で記述します。今回なら、showの切り替えです。
v-ifディレクティブはtrueだと、DOMに読み込まれますが、falseだとDOMから削除されます。また、これと似たもので、v-showディレクティブがあるのですが、こちらは削除されず、非表示になります。

◆参考にさせていただいた記事

公式ページ
基礎から学ぶVue.js
データバインディングとは
vue.js入門

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue3.x系で作ったアプリをFirebaseにデプロイするまでの流れ【初心者向け】

vue.jsでWebアプリを作ってそれをFirebaseにデプロイするまでにやったことをまとめました。

途中躓いたところが何個かあったので、備忘録として。

環境とかバージョンとか

  • Mac OS Mojave(10.14.3)
  • npm 6.9.0
  • vue-cli 3.5.1
  • firebase 6.5.0

1.vue.jsで何かしらアプリを作る

まずはvue-cliというツールをインストールします。
こちらの記事を参考にさせてもらいました。

Vue.js を vue-cli を使ってシンプルにはじめてみる

vueには2.x系と3.x系があり、firebaseにデプロイする際の手順で微妙に違いがあるそうです。
今回は最新の3.5.1を使いますので2.x系の話は割愛してます。

$ vue --versionとかで確認しておくと吉。

$ npm run buildを実行してdist配下にデプロイする用のファイルを置いておきます。

2.firebase側の準備

Firebaseに今回作成したアプリをデプロイする用のプロジェクトを作る必要があります。
GoogleアカウントでFirebase開き、コンソールから「プロジェクトを追加」をクリックします。

スクリーンショット 2019-03-24 19.13.19.png

そして、firebaseにログイン。

$ firebase login

ここでいろいろ英語で聞かれると思いますが、言われたとおりにハイハイ言っておきます。
(ブラウザに飛んで許可するか聞かれるはず)

ログインが終わったらプロジェクトIDをメモっておきます

$ firebase list
┌───────────┬───────────────────────┬─────────────┐
│ Name      │ Project ID / Instance │ Permissions │
├───────────┼───────────────────────┼─────────────┤
│ Cash Book │   プロジェクトID       │ Owner       │
└───────────┴───────────────────────┴─────────────┘

次にさっきのvueのアプリがあるディレクトリまで行き、initを実行。

$ firebase init --project=[FirebaseのプロジェクトID]

AAでFIREBASEと表示され、つらつらと色々聞かれます。

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

/workspace/vue/vue-cli-test/test-prj

? Which Firebase CLI features do you want to setup for this folder? Press Space to select features, then Enter to confirm your choices.
 Hosting: Configure and deploy Firebase Hosting sites

=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add, 
but for now we'll just set up a default project.

i  Using project hogehoge 

=== Hosting Setup

Your public directory is the folder (relative to your project directory) that
will contain Hosting assets to be uploaded with firebase deploy. If you
have a build process for your assets, use your build's output directory.

? What do you want to use as your public directory?

設定のあれこれを聞かれますが、基本的にはEnter連打でOK. (Y/n)で聞かれる質問はEnterを押すと大文字の方(Yes)を自動で選択してくれるようになっています。

ただし、? What do you want to use as your public directory?はvueのdistフォルダを指定しましょう。
(あとから変更可能です)

デフォルトではpublicフォルダが選択されますが、vueではdist配下にソースが格納されるのでdistに指定しなければいけません。

3.firebaseにアプリをデプロイする

$firebase deployでサーバーにアプリをデプロイします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js + Vuetify + Project-ShangriLa でアニメ情報を取得するアプリ作成

目的

面白いAPIを見つけたのでせっかくだからWebアプリ作ってみよう

環境

Windows Subsystem for Linux
OS: Ubuntu

使用技術

Vue.js(SPAフレームワーク)
Vuetify(UIコンポーネントライブラリ)
axios(Ajax通信ライブラリ)
materialdesignicons(マテリアルデザインアイコン)

サーバーレス+小規模なアプリなのでいかに低コストで導入できるかで選びました
(ちょっとVue使ってみたかった感もある)

やったこと

プロジェクト作成

$ vue create animecheck

Vuetify追加

オプション指定などできるけどデフォルトを指定する

$ vue add vuetify

マテリアルデザインアイコン用のパッケージインストール

$ npm install @mdi/font --save
$ npm install material-design-iconfont --save

選択用コンボボックス作成

2014年~現在までの西暦とクールを選択する用のコンボボックスを作成する

search.vue
<template src="../view/search.html">
</template>

<script src="../js/search.js">
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped src="../assets/css/search.css">
</style>

html, js, css それぞれ別々に書いたほうがわかりやすそうだったので、vueファイルはテンプレートのみ記載する

search.html
    <v-layout row wrap>
        <v-flex xs12 sm6>
            <v-overflow-btn v-model="year" label='年' :items="selectYear" @change="getAnime"></v-overflow-btn>
        </v-flex>
        <v-flex xs12 sm6>
            <v-overflow-btn v-model="season" label='クール(季節)' :items="selectSeason" @change="getAnime"></v-overflow-btn>
        </v-flex>
    </v-layout>

:items でコンボボックスに入れる選択値を設定する
選択された値は v-model で指定している year, season にそれぞれ入る

search.js
import axios from 'axios';
import 'vuetify/dist/vuetify.min.css'
import '@mdi/font/css/materialdesignicons.css'
import 'material-design-icons-iconfont/dist/material-design-icons.css'

export default {
    data() {
        return {
            year: '',
            season: '',
            animes: [],
            selectYear: this.getYear(),
            selectSeason: [
                {value: '1', text: '冬 (1 ~ 3 月)'},
                {value: '2', text: '春 (4 ~ 6 月)'},
                {value: '3', text: '夏 (7 ~ 9 月)'},
                {value: '4', text: '秋 (10 ~ 12 月)'},
            ]
        }
    },
    methods: {
        /**
         * 2014 ~ 現在までの年数を取得する関数
         */
        getYear: function() {
            let currentYear = (new Date()).getFullYear();
            const baseYear = 2014;
            let years = []
            while(currentYear >= baseYear) {
                years.push({value: currentYear, text: currentYear + '年'})
                currentYear -= 1;
            }
            return years
        },
    }
...
}

クールだけは面倒なので固定で書いてしまった
(固定値として別の場所に書きたい)

アニメ取得APIとの連携

西暦とクールを選択したら、APIにリクエストを送りレスポンスをいい感じに加工して画面に表示する

search.html
    <v-layout v-if="animes.length" row wrap>
        <v-flex lg4 sm12 xs12 v-for="anime in animes">
            <v-card raised tile class="grey lighten-3 ma-2">
                <v-card-title primary-title>
                    <div>
                        <div class="headline">
                            {{ anime.title }}
                        </div>
                    </div>
                </v-card-title>
                <v-divider light></v-divider>
                <v-card-actions class="pa-3">
                    <v-btn flat>
                        <a v-bind:href="anime.public_url">
                            <v-icon>
                                mdi-laptop
                            </v-icon>
                        </a>
                    </v-btn>
                    <v-btn flat>
                        <a v-bind:href="'https://twitter.com/' + anime.twitter_account">
                            <v-icon>
                                mdi-twitter
                            </v-icon>
                        </a>
                    </v-btn>
                    <v-btn flat>
                        <a v-bind:href="'https://twitter.com/search?q=%23' + anime.twitter_hash_tag + '&src=typd'">
                            <v-icon>
                                mdi-pound-box
                            </v-icon>
                        </a>
                    </v-btn>
                </v-card-actions>
            </v-card>
        </v-flex>
    </v-layout>

双方向データバインディングでanimesという変数に値が入っていない場合は、結果部分は何も表示されないようにする

search.js
        /**
         * 年 + クールから対象のアニメリストを取得する関数
         */
        getAnime: function() {
            this.getYear();
            if (this.year && this.season) {
                // API: ShangriLa Anime API V1
                let api_url = "https://api.moemoe.tokyo/anime/v1/master/"
                this.animes = []
                axios.get(api_url + this.year + '/' + this.season, {
                }).then(response => {
                    for (let i = 0; i < Object.keys(response.data).length; i++) {
                        this.animes.push(response.data[i])
                    }
                })
            }
        }

コンボボックス側で指定されている関数
選択するたびに走るけど、年とクールが選択されていない限りは情報取得しないようにする

外部APIからjsonでデータ取得するためにはaxiosを使用する

アニメ情報取得には以下のAPIを使用しています

GET /anime/v1/master/:year/:n
:yearで指定されたYYYY年アニメの:nで指定されたクールの情報をすべて返します。

※参考
放映中のアニメ作品の情報を提供するAnime RESTful API サーバー作りました
axios を利用した API の使用

Vuetifyコンポーネントについて

Vuetify Component API Overview

公式のドキュメントに各コンポーネントの例とソースが記載されている
すごくわかりやすい

アイコンについて

Icon Component
Material Design Icons

Material Design Iconsのアイコンの利用をサポートしています。アイコン名にmdi-プレフィックスを付与するだけで使用できます。あなたのプロジェクトにMaterial Design Iconsを導入しておく必要があるので注意して下さい。

search.html
    <v-icon>
        mdi-twitter
    </v-icon>

これでTwitterアイコンなどが使用できる

デプロイ

サーバーレス+SPAなので、Github Pagesで簡単に公開したい
そのためビルド先のフォルダをdistからdocsに変更する

プロジェクトフォルダ直下に以下のファイルを作成する

vue.config.js
module.exports = {
    publicPath: "./",
    assetsDir: "",
    outputDir: "docs"
}

ビルドする

$ npm run build

ビルド完了後にgitに上げてGithub Pagesの設定をしてあげれば出来上がり

完成品

アニメ検索くん

ソースはこちら

とりあえずお手軽に作れました

最後に

こちらのAPIを使用させていただきました!素晴らしいAPIに感謝です!
https://github.com/Project-ShangriLa/sora-playframework-scala

Vueについて少ししか勉強してないのでもっと最適化できる部分あるかもって感じです
よろしければコメントなどでご指摘いただければと思います

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js で子コンポーネントにプロパティを一括で渡す方法

沢山のプロパティを持つコンポーネントに値を渡すとき、
以下のように全プロパティについてズラズラ書いていくのはちょっと面倒です。

よくない例
<Profile 
    :id="id" 
    :name="name" 
    :nickname="nickename"
    :address="address"
/>

こういうときは、プロパティ名なしv-bindにオブジェクトを渡すことでプロパティを一括で渡すことができます。

良い例
<Profile v-bind="{id, name, nickname, address}" />

あるいはdata等にオブジェクトを定義しておくとテンプレートがより簡潔に書けます。

良い例
<Profile v-bind="profile" />

(中略)

  data() {
    return {
      profile: {
        id: id,
        name: name,
        nickname: nickname,
        address: address,
      }
    }
  },

蛇足

上述の方法は公式ドキュメントにも一応ちょこっと記載はあるのですが、意外と知らない人も多そうだったので記事にしました。

実際、公式の別の箇所では「プロパティが増えてきたらプロパティ定義を一つにまとめよう」的な別の解決策が書かれています。
しかしながらそれだとプロパティのバリデーションがやり辛くなるため、現在ではバッド・プラクティスではないかと思われます。

現行のバージョンではプロパティ定義を変えずともオブジェクトで渡せるので是非そうしていきましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む