suchen

Heim  >  Fragen und Antworten  >  Hauptteil

Der richtige Weg zur Komponentenentkopplung

Ich beschäftige mich mit der Kommunikation zwischen zwei Komponenten (unter Verwendung von Vue 2), von denen eine eine Schaltfläche ist, die Zustände wie Anfang, Laden und Abschluss (Erfolg oder Misserfolg) haben kann. Für jeden Zustand der Schaltfläche kann ich einen anderen anzeigen Text, verschiedene Symbole (Laden: Drehsymbol, erfolgreicher Abschluss: Häkchen, Fehlerabschluss: x), ich habe auch ein Formular, das eine Schaltflächenkomponente verwendet. Ich bin mir nicht sicher, wie ich den Status der Schaltfläche basierend auf dem aktuellen Status der Formularübermittlung ändern kann. Bitte sehen Sie sich den Code unten an.

Meine Button-Komponente:

<template>
 <button
    class="ui-button"
    @click="clicked"
    :data-status-type="status_type"
    :disabled="is_disabled"
    :type="type"
  >
    <i :class="icon" v-if="is_disabled || concluded"></i>
    {{ title }}
  </button>         
</template>

<script>
export default {
  props: {
    title: {
      type: String,
    },
    type: {
      default: "button",
      type: String,
    },
  },
  data() {
    return {
      concluded: false,
      icon: "fa fa-spin ",
      is_disabled: false,
      status_type: "success",
    };
  },
  methods: {
    clicked() {
      if (!this.is_disabled) {
        this.$emit(
          "clicked",
          () => {
            this.is_disabled = true;
            this.icon = "fa fa-spin fas fa-spinner";
          },
          (succeeded) => {
            this.is_disabled = false;
            this.concluded = true;
            this.icon = succeeded ? "fas fa-check" : "fas fa-xmark";
            this.status_type = succeeded ? "success" : "error";
            setTimeout(() => {
              this.concluded = false;
              this.icon = "";
              this.status_type = "";
            }, 1500);
          }
        );
      }
    },
  },
};
</script>

Meine Formularkomponente:

<template>
  <div>
    <ThePages :parents="accompaniments">
       <!--  ... some reactive stuff  -->
      <template #extra_button>
        <TheButton @clicked="sendItemToCart" :title="button_text" :disabled="button_disabled" />
      </template>
    </ThePages>
  </div>
</template>

<script>
import axios from 'axios'
import FormatHelper from '../helpers/FormatHelper'
import SwalHelper from '../helpers/SwalHelper'
import TheButton from './TheButton.vue'
import ThePages from './ThePages.vue'
import TheQuantityPicker from './TheQuantityPicker.vue'

export default {
  props: ['product'],
  components: {
    TheButton,
    ThePages,
    TheQuantityPicker,
  },
  data() {
    return {
      accompaniments: this.product.accompaniment_categories,
      button_text: '',
      button_disabled: false,
      format_helper: FormatHelper.toBRCurrency,
      observation: '',
      quantity: 1,
      success: false,
    }
  },
  created() {
    this.addQuantityPropToAccompaniments()
    this.availability()
  },
  methods: {
    // ... some other methods
    async sendItemToCart(startLoading, concludedSuccessfully) {
      startLoading()  // This will change the button state
      this.button_text = 'Adicionando...'
      await axios
        .post(route('cart.add'), {
          accompaniments: this.buildAccompanimentsArray(),
          id: this.product.id,
          quantity: this.quantity,
          observation: this.observation,
        })
        .then(() => {
          concludedSuccessfully(true)  // This will change the button state
          this.button_text = 'Adicionado'
          SwalHelper.productAddedSuccessfully()
        })
        .catch((error) => {
          concludedSuccessfully(false)  // This will change the button state
          if (
            error?.response?.data?.message ==
            'Este produto atingiu a quantidade máxima para este pedido.'
          ) {
            SwalHelper.genericError(error?.response?.data?.message)
          } else {
            SwalHelper.genericError()
          }
          this.button_text = 'Adicionar ao carrinho'
        })
    },
  },
}
</script>

Im Code oben können Sie sehen, wie ich den Status der Schaltfläche basierend auf dem Status des Formulars ändere: Meine Schaltfläche gibt beim Klicken zwei Funktionen aus (startLoading, IncludedSuccessfully), und ich verwende diese beiden Funktionen dann in sendItemToCart.

Dies scheint die beiden Komponenten etwas zu sehr zu koppeln, da ich diese Funktionen als Parameter an die Methoden der übergeordneten Komponente übergeben muss. Außerdem habe ich eine andere Idee, wie das geht: Jeder Schaltfläche eine Referenz zu geben und dann ihre Methode mithilfe der Referenz in der übergeordneten Komponente aufzurufen. Die Idee klingt ein bisschen wie „Komposition statt Vererbung“ in der objektorientierten Programmierung, wo ich das Objekt/die Komponente einfach auffordere, etwas zu tun, in diesem Fall jedoch ohne die Funktion als Parameter zu nehmen.

Nun, die beiden oben genannten Fälle scheinen besser zu sein, als für jede Schaltfläche, die ich möglicherweise habe, eine Variable zu erstellen, aber sie scheinen verbesserungswürdig zu sein. Ich suche also: Wie kann ich meine Komponenten besser entkoppeln?

P粉476475551P粉476475551466 Tage vor584

Antworte allen(1)Ich werde antworten

  • P粉023650014

    P粉0236500142023-09-15 09:27:59

    如果我们讨论的是Vue 3(你没有指定Vue的版本,所以不确定是Vue 2),你可能正在寻找provide/inject

    https://vuejs.org/guide/components/provide-inject.html

    所以,如果你有一个父组件和一堆子组件,这些子组件只能作为父组件的后代出现(例如表单和输入框),你可以provide表单的状态:

    OP评论说按钮也可能出现在其他地方,所以我们应该同时使用props和provide。在没有表单的情况下,我们可以使用props作为默认的注入值。

    在表单组件中:

    <script setup>
    
    import {reactive, provide} from 'vue';
    
    const form = reactive({button_disabled: false, button_text: '', sendItemToCart});
    provide('form', form);
    
    function sendItemToCart(){
      // your logic here
    }
    
    </script>
    
    <template>
      <div>
        <ThePages :parents="accompaniments">
           <!--  ... some reactive stuff  -->
          <template #extra_button>
            <TheButton />
          </template>
        </ThePages>
      </div>
    </template>

    在按钮组件中:

    <script setup>
    
    import {inject} from 'vue';
    
    const props = defineProps('button_disabled button_text'.split(' '));
    const form = inject('form', props); // use either provide of props
    
    </setup>
    
    <template>
     <button
        class="ui-button"
        @click="() => form.sendItemToCart ? form.sendItemToCart() : $emit('clicked')"
        :data-status-type="status_type"
        :disabled="form.button_disabled"
        :type="type"
      >
        <i :class="icon" v-if="form.button_disabled || concluded"></i>
        {{ form.button_text }}
      </button>         
    </template>

    将代码调整为Options API。

    使用Vue 2进行更新

    OP更正了答案,使用的是Vue 2。所以...

    幸运的是,Vue 2也支持provide/inject!唯一的问题是如何使provide具有响应性,我猜在这里得到了解决:

    How do I make Vue 2 Provide / Inject API reactive?

    Antwort
    0
  • StornierenAntwort