props 변경이 반영되지 않음

4619 단어 Vue.js
props 를 직접 v-model

환경


  • vue 2.6.11
  • JavaScript

  • 무엇을 하고 싶었는가



    하위 구성 요소의 클릭 이벤트에서 선택한 값을 상위 구성 요소를 통해
    다른 자식 구성 요소에 전달하고 싶었습니다.

    그림으로 하면 다음과 같은 형태.



    보다 상세하게 설명하면, 캘린더 앱을 만들고 있어, 이하와 같은 구성으로 하고 있었다.


    구성 요소 이름
    개요


    componentA
    일정 등록을 위한 모달 대화상자

    componentB
    캘린더의 실체를 가지는 컴퍼넌트(월 표시용 뷰) componentC에 직접 기술하고 있지 않은 것은, 탭으로 월·주·일 표시를 전환하고 싶었기 때문에

    componentC
    캘린더 페이지


    componentA에서 선택한 날짜를 componentC를 통해 componentB의 초기 값으로 설정하고 싶었습니다.

    여기서 문제가 된 것은 componentC -> componentB에 데이터를 전달하는 방법입니다.

    안 좋은 예



    componentA 의 소스는 생략하고 있습니다.

    props를 data의 초기 값으로 지정


    var componentB = {
        props: {
            'initDateString': String
        },
        data() {
            return {
                // ① propsで受け取った値をdataに代入
                startDate: this.initDateString
            }
        },
        template: `
            <div>
                <input type="date" v-model="startDate">
            </div>
        `
    }
    
    var componentC = {
        components: {
            componentA,
            componentB
        },
        data() {
            return {
                selectedDate: null
            }
        },
        methods: {
            selected(value) {
                this.selectedDate = value
            }
        },
        template: `
            <div>
                <component-a @click="selected"></component-a>
                <component-b :init-date-string="selectedDate"></component-b>
            </div>
        `
    }
    

    이것이 잘 가지 않았던 것은, 이하가 원인.

    소스중①dataprops 를 대입해도 data와 props가 관련지을 수 없기 때문에,
    두 번째 이후의 props 변경은 data에 반영되지 않습니다.

    componentC -> B에 건네주고 있는 값의 초기치가 null이므로,
    componentB의 초기값으로서는 아무것도 설정되어 있지 않은 상태로부터 변경되는 일이 없다.

    props를 computed를 통해 data의 초기 값으로 지정



    componentC는 변경이 없으므로 생략
    var componentB = {
        props: {
            'initDateString': String
        },
        data() {
            return {
                // ① computed経由で初期値を設定
                startDate: this.dateString
            }
        },
        template: `
            <div>
                <input type="date" v-model="startDate">
            </div>
        `,
        computed: {
            dateString() {
                return this.initDateString
            }
        }
    }
    

    이것이 잘되지 않은 이유는 다음과 같습니다.

    소스중①
    원래 data 에의 초기치 대입의 처리가 통과하는 것은 인스턴스 생성시의 1회만
    그래서 computed 로부터 반환되는 값에는 props 의 변경이 반영되지만,
    그 computed를 호출하는 처리가 최초 밖에 통과하지 않기 때문에, 조금 전과 같은 상태.

    해결 방법



    computed에 getter,setter를 준비하고 v-model로 설정



    componentC는 변경이 없으므로 생략
    var componentB = {
        props: {
            'initDateString': String
        },
        data() {
            return {
                // ① dataでは初期値設定しない
                startDate: null
            }
        },
        template: `
            <div>
                <!-- ② computedをバインドする -->
                <input type="date" v-model="bindStartDate">
            </div>
        `,
        computed: {
            bindStartDate: {
                get() {
                                     // ③ 初期表示にのみ利用
                    // setterで別プロパティを指定しているので、その後DOMの値と連動しない
                    // ここはちょっと特殊なことしているかも
                    return this.initDateString
                },
                set(value) {
                    // ④ 実際に保存に利用するプロパティにセットする
                    // そもそもpropsを子コンポーネントで書き換えるとエラーになる
                    // もし書き換えたければ、this.$emit('change', value)で
                    // 親コンポーネントで書き換えて再度propsで渡し直す
                    this.startDate = value
                }
            }
        }
    }
    

    ↑는 watch 사용해도 할 수 있는 것 같다.
    생략하고 있지만, 실제 소스의 data 에는 또 하나 endDate 가 있어,
    정확히 같은 것 (세터로 설정하는 것이 endDate 그냥)하고 있기 때문에,
    이 경우는 watch 사용하는 편이 심플했을지도?

    라고 할까, 이 만들기 자체 그다지 좋지 않은 것일까,,,? 조금 복잡해져 버렸고.

    ↓ 참고로 했습니다.
    htps // // cs. 메아아/엔트리/200716/
    htps : // 이 m / 시메지 9 / ms / c27d69f17d2d08722b3

    좋은 웹페이지 즐겨찾기