Ссылка на компоненты VueJS не определена на всех этапах - PullRequest
0 голосов
/ 17 декабря 2018

Я изучаю VueJS, используя его с приложением rails, и у меня возникают проблемы при попытке доступа к компоненту из родительского компонента.Возможно, я делаю это неправильно, но я не могу понять, в чем проблема.

Я пытался свести мою проблему к простому HTML-примеру:

<html>
  <head>
    <meta charset='utf-8' />
    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.21/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <outer>
        <div>
          <form>
            <inner ref="testref"><input/></inner>
          </form>
        </div>
      </outer>
    </div>
    <script>
      let inner = Vue.component('inner', {
        template: `
          <div>
            <span hidden="true"><slot></slot></span>
            <input v-model="searchText" />
          </div>
        `,
        props: ['placeholder'],
        data: function() {
          return {
            searchText: this.placeholder
          }
        }
      })
      let outer = Vue.component('outer', {
        template: `
          <div>
            <h3 v-on:click="testos">Hello: {{ client_id }}</h3>
            <slot></slot>
          </div>
        `,
        data: function() {
          console.log('data', this.$refs.testref)
          return {
            client_id: ''
          }
        },
        mounted: function() {
          console.log('mounted', this.$refs.testref)
        },
        methods: {
          testos: function() {
            console.log('method', this.$refs.testref)
          }
        }
      })
      new Vue({
        el: '#app',
        components: {
          'inner': inner,
          'outer': outer
        }
      })
    </script>
  </body>
</html>

Я получаю следующие журналы консоли:

data undefined
mounted undefined
method undefined

Я пытался следовать синтаксису в https://vuejs.org/v2/api/#ref,, но я также пытался использовать :ref вместо ref, и это не 'лучше не работает.

Я понимаю, что есть осложнение «сроков регистрации», даже если я не до конца понимаю.Однако не должны ли быть правильные сроки при нажатии на H3?Я надеялся, что это будет решено даже mounted, как в https://stackoverflow.com/a/40884455/2730032.

Бонусный вопрос: Мой вопрос действительно о том, как работает ref.Тем не менее, у меня есть более общая проблема, которая может быть полезна для советов, не использующих ref.

У меня есть компонент inner, который меня порадовал тем, что оборачивает поле ввода для client_id с полем поиска, которое дает некоторыеПодобные выбору опции из API и устанавливают поле ввода client_id для выбранной опции.Я хочу повторно использовать этот компонент inner в компоненте outer, который будет использовать client_id (всякий раз, когда он изменяется inner) для вызова API и заполнения множества других полей формы в компоненте outer(эти поля задаются с inner до outer через слоты).Если это имеет какой-то смысл.

Я решил, что лучший способ сделать это - указать client_id в качестве поля данных inner и outer получить к нему доступ через ref.Поэтому я пытаюсь заставить ref работать.

EDIT1: Произошла ошибка копирования, извините за это.Тег test1 был из предыдущей версии.Но я все равно получаю эту проблему.

РЕДАКТИРОВАТЬ 2: Я принял второй ответ Роя Дж, потому что я чувствую, что он лучше всего отвечает на вопрос, и хотя это не очень хороший дизайн, это, вероятно, правильное решение для некоторыхлюди.Однако в моей реальной реализации я использовал первый ответ Роя Дж, и, возможно, кому-то, кто читает этот вопрос, тоже следует (мне также удалось добавить inner в шаблон outer, чтобы избежать использования app).

Ответы [ 3 ]

0 голосов
/ 17 декабря 2018

Ваш ref определен как часть вашего корневого шаблона.Вы пытаетесь получить к нему доступ из outer, но он там не определен, поэтому он никогда не появляется.

В общем случае ref s являются исключительной ситуацией.Вы должны относиться к ним как к последнему средству, когда вам действительно нужно что-то знать о состоянии DOM, а не когда вам нужно обмениваться данными.

Правильный способ обмена данными - это владение ими.общий предок компонентов, которые должны его использовать.Это может быть, например, хранилище типа Vuex или корневой экземпляр.

Я написал, как может работать ваш пример кода, если корневой экземпляр принадлежит client_id и поделился им с outer и inner.Переходя к inner, я включаю модификатор .sync для чистой обработки обновлений.

Я определяю searchText как вычисляемую переменную, которая генерирует событие обновления, которое вызывает sync, так что вы все еще можете v-model="searchText".

const inner = Vue.component('inner', {
  template: `
          <div>
            <span hidden="true"><slot></slot></span>
            <input v-model="searchText" />
          </div>
        `,
  props: ['placeholder'],
  computed: {
    searchText: {
      get() { return this.placeholder; },
      set(newValue) { this.$emit('update:placeholder', newValue); }
    }
  }
});
const outer = Vue.component('outer', {
  template: `
          <div>
            <h3 v-on:click="testos">Hello: {{ client_id }}</h3>
            <slot></slot>
          </div>
        `,
  props: ['client_id'],
  methods: {
    testos() {
      console.log("Yes, client_id is", this.client_id);
    }
  }
})
new Vue({
  el: '#app',
  data: {
    client_id: 'Initial Client ID'
  },
  components: {
    'inner': inner,
    'outer': outer
  }
})
<script src="https://unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
  <outer :client_id="client_id">
    <div>
      <form>
        <inner :placeholder.sync="client_id"></inner>
      </form>
    </div>
  </outer>
</div>
0 голосов
/ 18 декабря 2018

Поскольку единственная причина, по которой вы не смогли использовать ваш ref, заключалась в том, что он был определен в родительском элементе, вы можете перейти на $parent и использовать его refs.Обратите внимание, что это делает outer зависимым (запутанным) от структуры inner и наличия ref, чтобы добраться до него.Это плохой дизайн, но это наименьшее изменение в коде, которое вам нужно сделать, чтобы сделать то, что вы пытаетесь сделать.

const inner = Vue.component('inner', {
  template: `
          <div>
            <span hidden="true"><slot></slot></span>
            <input v-model="searchText" />
          </div>
        `,
  props: ['placeholder'],
  data: function() {
    return {
      searchText: this.placeholder
    }
  }
});
const outer = Vue.component('outer', {
  template: `
          <div>
            <h3 v-on:click="testos">Hello: {{ client_id }}</h3>
            <slot></slot>
          </div>
        `,
  data: function() {
    return {
      client_id: ''
    }
  },
  methods: {
    testos: function() {
      this.client_id = this.$parent.$refs.testref.searchText;    
      console.log('method', this.client_id);
    }
  }

})
new Vue({
  el: '#app',
  components: {
    'inner': inner,
    'outer': outer
  }
})
<script src="https://unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
  <outer>
    <div>
      <form>
        <inner ref="testref"></inner>
      </form>
    </div>
  </outer>
</div>
0 голосов
/ 17 декабря 2018

Объясняя комментарий Роя, используйте модификатор синхронизации , если вам требуется двусторонняя привязка данных свойства.В вашем случае, вы делаете.Ребенок должен иметь возможность обновить свойство и сообщить родителю об изменении.Под капотом на самом деле есть магия, где значением является функция обратного вызова, которая возвращает значение дочернего элемента, а родительский объект обновляет переменную своей собственной.

Насколько ваш ref не работает - дочерний элементКомпонент не был создан, хотя родительский элемент был смонтирован.Это связано с тем, как метод ._render() работает в VueJS.Поэтому, если вам нужно использовать ссылку на пользовательский компонент, который является дочерним, вам нужно поймать изменение в next tick:

mounted: function() {
  this.$nextTick(() => {
      console.log(this.$refs.testref)
  })
}

Наконец, и, возможно, просто плохое задание на копирование, у вас естьсинтаксическая ошибка в вашем коде:

<inner ref="testref"><inner/></test1>

Этот закрывающий тег </test1> ничего не соответствует и выдаст ошибку синтаксического анализатора.

Все вышеперечисленное должно решить вашу проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...