javascript - Skicka lösta asynkrona data via props från föräldrakomponent till barnkomponent - Vue


Föräldrakomponent:

<template>
  <div>
    <Nav :data="data" />
  </div>
</template>

<script lang="ts">
// ... removed obvious imports for readability
@Component({
  components: {
    Nav: () => import('@/components/Nav.vue')
  }
})
export default class Home extends Vue {
  public nav: NavInterface = {}

  private getData(): Promise<any> {
    // ... this.$http - is an axios instance
    return this.$http
      .getData()
      .then((resp: any) => {
        this.data = resp.data.nav
      })
  }
}
</script>

Barnkomponent:

<template>
  <div class="nav">
    <ul>
      <li v-for="(nav, index) in data">{{ nav.id }}</li>
    </ul>
  </div>
</template>

<script lang="ts">
// ... imports
export default class Nav extends Vue {
  @Prop({ default: null }) public readonly data!: NavInterface

  private mounted(): void {
    console.log(this.data) // is undefined, because promise is not resolved yet - this is a problem
  }
}
</script>

Det finns ett problem, när ett löfte löses i en föräldrakomponent, kommer det inte att lösas i ett barn ännu. Är det möjligt att ladda barnkomponenten först efter att mitt getData()-löfte har lösts framgångsrikt, eftersom min barnkomponent inte har någon mening utan data från föräldern.

Självklart kan jag använda en watcher i en barnkomponent, då får jag korrekt data från ett löfte, men det verkar hackigt för mig:

@Watch('data')
private onPropChange(val: any) {
  console.log(val) // i get the correct data
}

Jag föredrar att villkorligt rendera min barnkomponent, endast efter att löftet har uppfyllts.

Svar #1

Följande visar ett exempel på tillvägagångssätt. Du behöver variera detta baserat på dina faktiska krav:

<template>
  <div>
    <Nav v-if="isVisible" :items="navItems" />
  </div>
</template>
export default {
  data() {
    return { navItems: [] };
  },
  computed: {
    isVisible() {
      return this.navItems.length > 0;
    },
  },
  mounted() {
    return this.$http.getData().then(resp => {
      this.data.navItems = resp;
    });
  },
};

Observera: Jag använder längden på navItems-arrayen för att avgöra om den är synlig. Du kanske vill använda en separat variabel (t.ex. isLoaded) för att avgöra om Nav ska renderas när det inte finns några objekt eller om det uppstod ett fel.

Svar #2

Du kan lägga till ett if-uttryck i din föräldrakomponent:

<template>
  <div>
    <Nav v-if="loaded" :data="data" />
  </div>
</template>

och i funktionen:

return this.$http
      .getData()
      .then((resp: any) => {
        this.data = resp.data.nav
        this.loaded = true
      })

ELLER om data.nav är null eller inte finns efter att du har fyllt i datan

    <template>
      <div>
        <Nav v-if="data.nav" :data="data" />
      </div>
    </template>