LoginSignup
7
9

More than 3 years have passed since last update.

【Vue】メソッドを外部のjsファイルに移動する方法。外部ファイルの処理結果をvueに戻す。

Last updated at Posted at 2020-11-13

メソッドを外部のjsファイルに移動する方法について。

変数を外部ファイル(.js)に切り出してexport defaultでインポートする方法はこちら

今回は応用編として、.vue内のメソッドを外部ファイルに切り出す方法。

流れと考え方

操作の流れは、vueのデータをjsに渡して、処理後に戻す作業になる。

(1).vue -> (2).js -> (3).vue



(1) .vueの処理
・vueのメソッドを実行
・メソッド内で変数を定義し、jsファイルにプロパティ名でデータを渡す。

(2) .jsの処理
・vueのデータを引数としてプロパティで受け取り処理を実行後、戻り値で返す。

(3) .vueの処理
.jsから戻ってきた値を、vueの値(this.xxx)に代入する。



▼考え方
Inputする(渡す)データとOutputする(戻す)データの受け渡しを意識することが大切。

.vueの処理(概念)
methods:{
    vueのメソッド名(){
      const {受け取るデータ} = jsファイルのコマンド名({
        渡すデータ
      })

      vueのデータ = 受け取るデータ
    }
  } 
.jsの処理(概念)
const メソッド名 = ({受け取るデータ}) => {
    処理
  return {
    渡すデータ
  }
}

export default 渡すメソッド

jsファイルに引数としてデータを渡し、処理後にreturnでvue側の変数名と一致するデータを返す。

最後に、受け取ったデータをvueのデータに入れて受け取り完了となる。


実例

実例で見るとよりわかりやすい。

.vue(scriptの中身)
//外部ファイル(transformationJs.js)からメソッドをjsMultiとしてインポート
import jsMulti from "./transformationJs"

export default {
  data(){
    return{
      A: 1,
      B: 2,
      x: 100,
      y: 200,
    }
  },

  //ここが重要!jsファイルのメソッドとデータをやりとりする記述
  methods:{
    btnClicked(){
      //処理結果をいれるプロパティを用意
      const {A, B} = jsMulti({
        //引数内でプロパティ名を指定してデータを渡す
        A: this.A,   
        B: this.B,
        x: this.x,
        y: this.y
      })

      //vueのデータにjsファイルの処理結果を代入
      this.A = A
      this.B = B
    }
  }
}

読み込んだのはテンプレートではなくメソッドなので、components:{}は不要

.js
//メソッドを定義
const jsMulti = ({A, B, x, y}) => {
  //処理結果を代入する変数を定義
  const _A = A * x
  const _B = B * y

  return {  //ここが重要!
    //vue側で設定したプロパティにデータを入れる
    A: _A,
    B: _B
  }
}

//外部ファイルに渡すメソッド
export default jsMulti

・.js側で処理済みの値はわかりやすいように_を付けたが、これは別に何でもいい。(Aのままでもいいし、他の新しい変数名でも可)

・.jsのメソッドは最後にreturnをプロパティとして返す。

A = _Aはエラーになる。(※プロパティの形になっていない)

Aのみで値を入れない場合は、空のデータが返る。 (※AはA:Aの省略表記となるため、変数Aを定義していない場合は空のプロパティを返したことになる。)

・A:Aの省略表記Aをshort propertyと呼ぶ


処理結果事例とフルコード

上記の実例は、ボタンをクリックする毎に、A × 100、B × 200をする処理となる。

image.png

↓ クリック

image.png

↓ クリック

image.png

▼フルコード

.vue
<template>
  <div>
   <!-- クリックイベントでbtnClickedを発火 -->
    <button
      @click="btnClicked"
    >【Click Me】</button>

    <p>・A: {{A}}</p>
    <p>・B: {{B}}</p>

  </div>
</template>

<script>
import jsMulti from "./transformationJs"

export default {
  data(){
    return{
      A: 1,
      B: 2,
      x: 100,
      y: 200,
    }
  },
  methods:{
    btnClicked(){
      const {A, B} = jsMulti({
        A: this.A,
        B: this.B,
        x: this.x,
        y: this.y
      })

      this.A = A
      this.B = B
    }
  }
}
</script>
transformationJs.js
const jsMulti = ({A, B, x, y}) => {
  const _A = A * x
  const _B = B * y

  return {
    A: _A,
    B: _B
  }
}

export default jsMulti


配列の中のコマンドを実行する例

v-forで表示した配列として格納されているボタンに対し、外部ファイルの処理を実行する場合の例。(*流れのみ)

(1)ボタンタグをクリックするとメソッドが発火する。
このとき、引数でクリックされたボタンの要素を渡す。

@click="onClickButton(command)

(2)ボタンクリックで発火させる処理
command.onClickでJSファイル内のプロパティonClickを実行している。

.vue
<script>
import cellCommands from "./commands"

export default {
  methods:{
    onClickButton(command){

      const {
        //js内で処理後の値を格納する変数を用意(output用)
        rows,
        selectedCells,
      } = command.onClick({  //jsファイルに渡すデータ(input用)。プロパティ名: 値 。returnで返す。
        selectedCells: this.selectedCells,
        rows: this.innerValue,
        maxColNum: this.maxColNum,
        thPosition: this.thPosition,
      })

      //jsの出力をvueに渡す(output)
      this.innerValue = rows
      this.selectedCells = selectedCells
    },
  }
}
</script>

各要素のプロパティonClickの中に記述された処理が実行される。

commands.js
const commands = [
  {
    cmd: 'clearSelects',
    text: "選択解除",
    icon: 'mdi-cancel',
    isActive: anySelectedCells,
    onClick: ({
      rows,
    }) => {
      return {
        selectedCells:[],
        rows,
      }
    }
  },
  {
    cmd: 'addColumnBefore',
    text:"左に列追加",
    icon: 'mdi-table-column-plus-before',
    isActive: (params) => notThColandSelectedFirstCol(params) && singleSelectedCell(params),
    onClick: ({
      selectedCells,
      rows,
      thPosition
    }) => {

      if(selectedCells.length != 1) {
        return {
          selectedCells,
          rows,
        }
      }

      //変換後の出力の値は別の変数に代入。一時的として、_変数名とする。
      const _rows = rows.map((tr, index) => {
        let newCell
        if(thPosition == 'row' && index === 0){
          newCell = initTh()
        }else{
          newCell = initTd()
        }
        tr.article_items = [
          ...tr.article_items.slice(0, selectedCells[0].cellIndex),
          ...[newCell],
          ...tr.article_items.slice(selectedCells[0].cellIndex),
        ]
        return tr
      })

      const _selectedCells = selectedCells.map((cell)=>{
        const {cellIndex: cCellIndex} = cell
        if(cCellIndex >= selectedCells[0].cellIndex){
          return {
            ...cell,
            cellIndex: cCellIndex + 1
          }
        } else {
          return cell
        }
      })

      //vueに戻すoutput用
      return {
        selectedCells: _selectedCells,
        rows: _rows,
      }
    }
  },
  {
    cmd: 'addColumnAfter',
    text:"右に列追加",
    icon: 'mdi-table-column-plus-after',
    isActive: singleSelectedCell,
    onClick: ({
      selectedCells,
      rows,
      thPosition,
    }) => {
      if(selectedCells.length != 1) {
        return {
          selectedCells,
          rows,
        }
      }

      const _rows = rows.map((tr, index) => {
        let newCell
        if (thPosition == 'row' && index === 0) {
          newCell = initTh()
        } else {
          newCell = initTd()
        }

        return {
          ...tr,
          article_items: [
            ...tr.article_items.slice(0, selectedCells[0].cellIndex + 1),
            ...[newCell],
            ...tr.article_items.slice(selectedCells[0].cellIndex + 1),
          ]
        }
      })

      const _selectedCells = selectedCells.map((cell)=>{
        const {cellIndex: cCellIndex} = cell
        if(cCellIndex > selectedCells[0].cellIndex){
          return {
            ...cell,
            cellIndex: cCellIndex + 1
          }
        }else {
          return cell
        }
      })

      return {
        rows: _rows,
        selectedCells: _selectedCells
      }
    }
  },
  {
    cmd: 'removeColumn',
    text:"列削除",
    icon: 'mdi-table-column-remove',
    color: 'error',
    isActive: (params) => anySelectedCells(params) && notAllColumnsSelected(params),
    onClick: ({
      selectedCells,
      rows,
      maxColNum,
    }) => {
      const cellIndicesToRemove = new Set(
        selectedCells.map((cell) => cell.cellIndex)
      )

      if(cellIndicesToRemove.size == maxColNum) {
        return
      }

      const _rows = rows.map((row) => {
        return {
          ...row,
          article_items: row.article_items.filter((cells, cellIndex)=> 
            !cellIndicesToRemove.has(cellIndex)
          )
        }
      })

      return {
        rows: _rows,
        selectedCells: []
      }
    },
  },
  {
    cmd: 'addRowAfter',
    text:"下に行追加",
    icon: 'mdi-table-row-plus-after',
    isActive: singleSelectedCell,
    onClick: ({
      selectedCells,
      rows,
      maxColNum,
      thPosition,
    }) => {
      if(selectedCells.length != 1) {
        return {
          selectedCells,
          rows,
        }
      }

      const _rows = [
        ...rows.slice(0, selectedCells[0].rowIndex + 1),
        ...[getTr( {maxColNum, thPosition} )],
        ...rows.slice(selectedCells[0].rowIndex + 1),
      ]

      const _selectedCells = selectedCells.map((cell) => {
        const { rowIndex: cRowIndex } = cell
        if (cRowIndex > selectedCells[0].rowIndex){
          return {
            ...cell,
            rowIndex:  cRowIndex + 1,
          }
        } else {
          return cell
        }
      })

      return {
        rows: _rows,
        selectedCells: _selectedCells
      }
    },
  },
  {
    cmd: 'addRowBefore',
    text:"上に行追加",
    icon: 'mdi-table-row-plus-before',
    isActive: (params) => singleSelectedCell(params) && notThColandSelectedFirstRow(params),
    onClick: ({
      selectedCells,
      rows,
      maxColNum,
      thPosition,
    }) => {

      if(selectedCells.length != 1) {
        return {
          selectedCells,
          rows,
        }
      }

      const _rows = [
        ...rows.slice(0, selectedCells[0].rowIndex),
        ...[getTr( {maxColNum, thPosition} )],
        ...rows.slice(selectedCells[0].rowIndex),
      ]

      const _selectedCells = selectedCells.map((cell) => {
        const { rowIndex: cRowIndex } = cell
        if (cRowIndex >= selectedCells[0].rowIndex){
          return {
            ...cell,
            rowIndex:  cRowIndex + 1,
          }
        } else {
          return cell
        }
      })

      return {
        rows: _rows,
        selectedCells: _selectedCells
      }
    }
  },
  {
    cmd: 'removeRow',
    text:"行削除",
    icon: 'mdi-table-row-remove',
    color: 'error',
    isActive: (params) => anySelectedCells(params) && notAllRowsSelected(params),
    onClick: ({
      selectedCells,
      rows,
    }) => {
      const rowIndicesToRemove = new Set(
        selectedCells.map((cell)=> cell.rowIndex)
      )

      if(rowIndicesToRemove.size == rows.length) return

      const _rows = rows.filter((row, rowIndex)=> 
        !rowIndicesToRemove.has(rowIndex)
      )

      return {
        rows: _rows,
        selectedCells: []
      }
    },
  }
]

export default commands

commands.jsの中身はかなり長いので、vueファイルから切り出すことでコードを見やすくすることができる。


参考リンク

export defaultとimport fromの使い方
複数の変数やメソッドを渡す方法
MDN公式 export default

7
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
9