Как сделать выпадающее меню, которое действует на выбранный элемент? - PullRequest
1 голос
/ 21 июня 2019

TL; TR

Как мне:

getMyComponent(selectedMyComponentID).complexOperation()

Для меня это звучит как тривиальная и полезная вещь, например, из выпадающего меню.

Aнемного подробнее

Предположим, я делаю какой-то редактор (например, список задач или что-то в этом роде).GUI имеет концепцию «выбранного» элемента.(В нашем реальном случае это видимая загрузочная навигационная вкладка ), и я хочу иметь выпадающие меню с пунктами меню, которые выполняют различные операции с выбранным элементом.

Предполагая, что у меня есть идентификатор «выбранного» компонента, получить ссылку на MyComponent, который имеет метод complexOperation(), соответствующий этому идентификатору, на удивление сложно.

Возможно, это потому, что я этого не делаю "путь Vue ".

Я вижу эти способы достижения complexOperationOnSelectedMyComponent():

  • использование ref s - кажется грязным и безобразным
  • Реорганизовать complexOperation() из MyComponent в новый MyData, чтобы бизнес-логика данных использовалась приложениями App.vue и MyComponent.vue.Теперь родитель меняет данные и, следовательно, подпирает - звучит vue-ish.Но это приводит к большому количеству шаблонов, поскольку каждая операция в каждом компоненте теперь имеет две версии.Я не фанат шаблонов и дублирования ...
  • использовать vuex?Я думаю, что я еще не там ...
  • Использовать экземпляр Vue "шины событий" и события $ emit от родителя к потомку.Кажется излишним.И это грязно и имеет шаблон.

Я что-то упустил?Разве это не довольно стандартный материал?

Детали

Для простоты мы скажем, что есть шаблон:

<template>
  <div id="app">
    <div v-for="elem in mydata" :key="elem.id" @click="setSelected(elem)">
      <MyComponent :value="elem"/>
    </div>
    <button @click="complexOperationOnSelectedComponent">
        Complex operation on Selected Component
    </button>
  </div>
</template>

и структура данных, где первыйизначально выбран один:

data() {
  return {
    mydata: [
      { id: 0, foo: "bar", selected: true },
      { id: 1, foo: "baz", selected: false },
      { id: 2, foo: "world", selected: false }
    ]
  };
}

(Завершено codesandbox )

Итак, есть кнопка «Сложная операция с выбранным компонентом».Но что я должен добавить в метод complexOperationOnSelectedComponent?

Приведенные выше коды и поле также имеют эквивалентные кнопки внутри каждого компонента MyComponent.Они просто вызывают метод complexOperation() в определении MyComponent.

Я думаю, что кнопка находится внутри или снаружи компонента, является второстепенной деталью.Получите ссылку на MyComponent для выбранного идентификатора и вызовите selectedComponent.complexOperation() в обработчике @click пункта меню.

В нашем реальном сценарии пользователь выбирает «компонент», нажимая на навигационную панель,(не в экземпляре MyComponent), поэтому у нас есть идентификатор (mydata[n].id или 0, 1 или 2 выше).

Использование ref-s

То, что я мог сделать, это поставитьref="components" в определении <MyComponents>.Поскольку он находится в цикле v-for, this.$refs.components будет массивом MyComponents.Найдите тот с правильным идентификатором и используйте его.

Поскольку нет никакой гарантии относительно порядка в this.$refs.components Мне бы пришлось каждый раз искать в массиве selectedMyComponentID, но эй ...

Это действительно лучшее решение?

1 Ответ

0 голосов
/ 21 июня 2019

Обычно вы хотите использовать $ refs, если вам нужен прямой доступ к DOM, чтобы получить элементы, которые не связаны или не контролируются ни с какими свойствами экземпляра Vue.Вы можете сохранить, какой элемент выбран в данных, и нацелить его на конкретный элемент.

Поместить свой метод в родительский или дочерний?Это зависит от того, нужна ли вам логика в других местах?В этом случае, я думаю, что это имеет смысл для ребенка, потому что вы также хотите иметь возможность выполнять действия там.

Use an "event bus" Vue instance and $emit events from parent to child. Seems overkill.

Опоры переходят от родителя к ребенку.События передаются от ребенка к родителю.Шины Vuex и Event действительно полезны в больших приложениях, но в этом случае они не нужны.Однако вы должны генерировать изменения, которые вы хотите внести в ваши реквизиты, а не редактировать их напрямую, как вы делаете это сейчас в MyComponent.

Я немного реорганизовал ваш код, хочу повторить, что прямое изменение значений реквизита - плохая практика: https://codesandbox.io/s/button-onclick-on-selected-child-lf37c?fontsize=14

<template>
  <div id="app">
    <div v-for="elem in mydata" :key="elem.id" @click="selectedElem = mydata[elem.id]">
      <MyComponent :value="elem" :reverse="elem.reverse"/>
    </div>
    <button @click="reverseSelectedFoo">Reverse Selected Foo</button>
  </div>
</template>

<script>
import MyComponent from "./components/MyComponent";

export default {
  name: "App",
  data() {
    // Ideally this data is read from an API somewhere
    return {
      mydata: [
        { id: 0, foo: "bar", reverse: false },
        { id: 1, foo: "baz", reverse: false },
        { id: 2, foo: "world", reverse: false }
      ],
      selectedElem: null
    };
  },
  methods: {
    reverseSelectedFoo() {
      this.selectedElem.reverse = !this.selectedElem.reverse;
    }
  },
  components: {
    MyComponent
  }
};
</script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...