1
votes

I am trying to populate a NativeScript-Vue ListView with templates that contain components of which their types are not known ahead of time. For example, this code does not work as NativeScript does not have a 'component' element but this suggests what I am trying to accomplish:

<ListView for="component in components">
    <v-template>
        <component :is="component" />
    </v-template>
</ListView>

computed: {
    components () {
        return ['Label', 'Button'];
    }
}

Yes, I know you can use if="" in a v-template, but in this case I do not know ahead of time what components need to be loaded in the ListView. In my case I am loading global components in a plugin, and these components will be referenced in the ListView.

2

2 Answers

2
votes

Thanks @Manoj. Those wise words made me think, the template can't be dynamic but the contents of the v-template can be. Maybe not for everyone, but this code works for me:

// App.vue

<template>
    <Page>
        <ActionBar title="Welcome to NativeScript-Vue!"/>
        <GridLayout columns="*" rows="400,*">
            <ListView ref="lv" for="item in items">
              <v-template>
                <v-component :type="item.type" :options="item.options" />
              </v-template>
            </ListView>
        </GridLayout>
    </Page>
</template>

<script lang="ts">
  import Vue from 'nativescript-vue'
  import { Component } from 'vue-property-decorator'
  import VComponent from './VComponent.vue'

  @Component({
    components: {
      VComponent
    }
  })
  export default class App extends Vue {
      get items () {
      return [
        {type: 'Label', options: [{key: 'text', value: 'I am a Label'}, {key: 'color', value:'red'}] },
        {type: 'Button', options: [{key: 'text', value:'I am a Button!'}]}
      ]
    }
  }
</script>


// VComponent.vue

<template>
  <StackLayout ref="myLayout">
  </StackLayout>
</template>

<script lang="ts">
  import Vue from 'nativescript-vue'
  import { Component, Prop } from 'vue-property-decorator'
  import { StackLayout } from 'tns-core-modules/ui/layouts/stack-layout'
  import { Label } from 'tns-core-modules/ui/label'
  import { Button } from 'tns-core-modules/ui/button'

  const classByClassName = {
    'Label': Label,
    'Button': Button
  }

  @Component
  export default class CoolTemplate extends Vue {
    @Prop() type!: string;
    @Prop() options;

    mounted () {
      if (this.type) {
        const myLayout = <StackLayout>((<Vue>this.$refs.myLayout).nativeView)
        const component = new classByClassName[this.type]
        for (var i = 0; i< this.options.length; i++) {
          const option = this.options[i];
          component[option.key] = option.value 
        }
        myLayout.addChild(component)
      }
    }

  } 
</script>
1
votes

Your template can not be dynamic, that's the whole point of using ListView - keeping them static, so they can be reused as needed. If you like to see different components based on data then you must use multiple templates.

Read more on docs.