shin_moto
@shin_moto

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

【Vue.js】親子コンポーネント間のデータにラグがある

解決したいこと

子コンポーネントのinput要素に入力される文字数を監視し、
文字数オーバーを検知したら、呼び出し元コンポーネントにあるbuttonをdisableにする。

前提

・フロントエンドをVue.js v2.6.10
・バックエンドをlaravelで構築しているアプリケーションを運用
・バックエンドは改修しない、vue.js側で解決する
・コンポーネントの構成も変えない(子の中にボタンを作るのは無し)

発生している問題・エラー

どうやらprops(子)の値が更新されるのが、emitで値を親が受け取ってからなので、
emitに渡した時点では値が最新にならずに1テンポ遅れて反映される。

<具体例>
親コンポーネント

<template>
  <div>
    <child
      :name="userName"
      @changeName="changeName($event)"
    />
  </div>

  <div>
    <button :disabled="isDisabled">変更を保存</button>
  </div>
</template>

(略)

<script>
export default {
  data(){
    return {
      userName: "",
    }
  },

  methods: {
    changeName(e) {
      this.isDisabled = e.error;
      this.userName = e.value;
    },

子コンポーネント

Child.vue
(略)
<template>
  <input v-model="inputName" />
  <span>{{inputName.length}}/10</span>
</template>
(略)

<script>

props: {
    name: {
      type: String,
    },

computed: {
    // ボタンの判定
    isSubmitDisabled() {
      return this.isNameOver
    },
    // 文字数オーバーの監視
    isNameOver() {
      return this.inputName.length > 10
    },
    inputName: {
      get() {
        if(!this.$props.name)return "";
        return this.$props.name;
      },
      set(value) {
        this.$emit('changeName', {value: value, error: this.isSubmitDisabled});
      }
    },

この場合、11文字目を入力した段階では何も起こらなず、12文字目を入力するとisDisabledが発動する。

自分で試したこと

・子コンポーネントでnameをwatchして{error: this.isSubmitDisabled}を$emitしてみたが、これも上手く動かなかった。

0

2Answer

自分ならこんな感じで実装しますが、如何でしょうか。

methods:{
	 // ボタンの判定
    isSubmitDisabled(val) {
      return this.isNameOver(val)
    },
    // 文字数オーバーの監視
    isNameOver(val) {
      return val.length > 10
    },
},
computed:{
   inputName: {
      get() {
        if(!this.$props.name)return "";
        return this.$props.name;
      },
      set(value) {
      	const error = this.isSubmitDisabled(val);
        return this.$emit('changeName', {value: value, error: error });
      }
   }
},

computedはそもそも算出プロパティです。
意識的に実行して返り値を受け取ったり、プロパティを変更するときはmethodsを使うべきかなぁという見解です。

自分ならボタンの判定/文字数オーバーについては親コンポーネントで実装します。
コンポーネントは本来単体の部品であって、親や子以外でほかのコンポーネントの存在を意識して実装してしまうと、複雑化してしまう恐れがあり、アンチパターンかと存じます。

2Like

Comments

  1. @shin_moto

    Questioner

    @FAL_neighbor
    お返事遅くなり申し訳ありません。ご回答ありがとう御座いました。
    ご提案頂きました内容で問題点【11文字目を入力した段階では何も起こらなず、12文字目を入力するとisDisabledが発動する】が解消する事を確認しました。

    また、ボタンの判定/文字数オーバーについては親コンポーネントで実装する事としました。こちらもご指摘ありがとう御座いました。

親コンポーネントに emit しているのは、データ送信用なんですよね?

編集中は子コンポーネントだけで閉じておいて、 onblur で親コンポーネントに emit するのではダメですか?

1Like

Comments

  1. @shin_moto

    Questioner

    〉親コンポーネントに emit しているのは、データ送信用なんですよね?
    valueについてはそうです。割愛していますが、postする為の値です。

    仕様として、入力している文字数カウンターとボタンのdisabledが連動しないといけないので、編集が終わった段階では違う動きになってしまう気がします。
  2. なるほど。では、今は `v-model` していますが

    https://qiita.com/simezi9/items/c27d69f17d2d08722b3a

    上記の記事にもある通り、`v-model` は `v-bind` と `v-on:change` のロジックまとめているだけなので、`v-bind` と `v-on:change` の定義に分けて、`v-on:change` の延長で、親コンポーネントに emit するのはどうでしょうか。

    -- 追記:編集は子コンポーネントだけでクローズさせる、一方向で親コンポーネントに通知する形はどうか、という意味です。
  3. @shin_moto

    Questioner

    @yoshi389111
    お返事遅くなり申し訳ありません。
    〉`v-bind` と `v-on:change` の定義に分けて、`v-on:change` の延長で、親コンポーネントに emit する
    なるほど、確かにその方法であれば想定した動きを再現出来そうです。

    〉編集は子コンポーネントだけでクローズさせる、一方向で親コンポーネントに通知する形はどうか、という意味です。
    新規登録でも編集でも使用する想定なので、初期値は親からバインドしますが、変更後の値は一通でも問題はないです。

Your answer might help someone💌