最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Is there a vue.js equivalent of ngTemplateOutlet? - Stack Overflow

programmeradmin0浏览0评论

Does vue.js have an equivalent of Angular's *ngTemplateOutlet directive? Let's say I have some components defined like this:

    <template>
        <div id="independentComponent">
            Hello, {{firstName}}!
        </div>
    </template>
    <script>
        export default {
            name: "independentComponent",
            props: ['firstName']
        }
    </script>

    ...

    <template>
        <div id="someChildComponent">
            <slot></slot>
            <span>Let's get started.</span>
        </div>
    </template>
    <script>
        export default {
            name: "someChildComponent"
        }
    </script>

I want to be able to do something like this:

<template>
    <div id="parentComponent">
        <template #indepdentInstance>
            <independentComponent :firstName="firstName" />
        </template>
        <someChildComponent>
            <template #indepdentInstance></template>
        </someChildComponent>
    </div>
</template>
<script>
    export default {
        name: "parentComponent",
        components: {
            someChildComponent,
            independentComponent
        },
        data() {
            return {
                firstName: "Bob"
            }
        }
    }
</script>

In Angular, I could accomplish this with

<div id="parentComponent">
    <someChildComponent>
        <ng-container *ngTemplateOutlet="independentInstance"></ng-container>
    </someChildComponent>

    <ng-template #independentInstance>
        <independentComponent [firstName]="firstName"></independentComponent>
    </ng-template>
</div>

But it looks like Vue requires the element to be written to the DOM exactly where it is in the template. Is there any way to reference an element inline and use that to pass to another component as a slot?

Does vue.js have an equivalent of Angular's *ngTemplateOutlet directive? Let's say I have some components defined like this:

    <template>
        <div id="independentComponent">
            Hello, {{firstName}}!
        </div>
    </template>
    <script>
        export default {
            name: "independentComponent",
            props: ['firstName']
        }
    </script>

    ...

    <template>
        <div id="someChildComponent">
            <slot></slot>
            <span>Let's get started.</span>
        </div>
    </template>
    <script>
        export default {
            name: "someChildComponent"
        }
    </script>

I want to be able to do something like this:

<template>
    <div id="parentComponent">
        <template #indepdentInstance>
            <independentComponent :firstName="firstName" />
        </template>
        <someChildComponent>
            <template #indepdentInstance></template>
        </someChildComponent>
    </div>
</template>
<script>
    export default {
        name: "parentComponent",
        components: {
            someChildComponent,
            independentComponent
        },
        data() {
            return {
                firstName: "Bob"
            }
        }
    }
</script>

In Angular, I could accomplish this with

<div id="parentComponent">
    <someChildComponent>
        <ng-container *ngTemplateOutlet="independentInstance"></ng-container>
    </someChildComponent>

    <ng-template #independentInstance>
        <independentComponent [firstName]="firstName"></independentComponent>
    </ng-template>
</div>

But it looks like Vue requires the element to be written to the DOM exactly where it is in the template. Is there any way to reference an element inline and use that to pass to another component as a slot?

Share Improve this question edited Sep 2, 2019 at 4:01 Hasta Dhana 4,7197 gold badges20 silver badges27 bronze badges asked Oct 31, 2018 at 19:46 asker11660asker11660 2312 silver badges3 bronze badges 1
  • A lot of times it's not possible to write a solution in a framework following the exact principles from another framework. What exactly are you trying to accomplish here? I'm sure there'll be a vue way of doing it. – dsfx3d Commented Sep 10, 2020 at 11:28
Add a comment  | 

5 Answers 5

Reset to default 2

You cannot reuse templates like ngTemplateOutlet, but can combine idea of $refs, v-pre and runtime template compiling with v-runtime-template to achieve this.

First, create reusable template (<ng-template #independentInstance>):

<div ref="independentInstance" v-show="false">
    <template v-pre> <!-- v-pre disable compiling content of template -->
        <div> <!-- We need this div, because only one root element allowed in templates -->
            <h2>Reusable template</h2>
            <input type="text" v-model="testContext.readWriteVar">
            <input type="text" v-model="readOnlyVar">
            <progress-bar></progress-bar>
        </div>
    </template>
</div>

Now, you can reuse independentInstance template:

<v-runtime-template
    :template="$refs.independentInstance.innerHTML"
    v-if="$refs.independentInstance">
</v-runtime-template>

But keep in mind that you cannot modify readOnlyVar from inside independentInstancetemplate - vue will warn you with:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "readOnlyVar"

But you can wrap it in object and it will work:

@Component({
    components: {
        VRuntimeTemplate
    }
})
export default class ObjectList extends Vue {
    reusableContext = {
        readWriteVar: '...'
    };

    readOnlyVar = '...';
}

You could try Portal vue written by LinusBorg a core Vue team member.

PortalVue is a set of two components that allow you to render a component's template (or a part of it) anywhere in the document - even outside the part controlled by your Vue App!

Sample code:

<template>
  <div id="parentComponent">
    <portal to="independentInstance">
      <!-- This slot content will be rendered wherever the <portal-target>
           with name 'independentInstance' is located. -->
      <independent-component :first-name="firstName" />
    </portal>
    
    <some-child-component>
        <portal-target name="independentInstance">
          <!--
          This component can be located anywhere in your App.
          The slot content of the above portal component will be rendered here.
          -->
        </portal-target>
    </some-child-component>
  </div>
</template>

There is also a vue-simple-portal written by the same author that is smaller but that mounts the component to end of body element.

My answer from @NekitoSP gave me an idea for a solution. I have implemented the sample below. It worked for me. Perhaps you want to use it as a custom component with props.

keywords: #named #template #vue

<template>
  <div class="container">
    <div ref="templateRef" v-if="false">write here your template content and add v-if for hide in current place</div>
    ....some other contents goes here
    <p v-html="getTemplate('templateRef')"></p>
  </div>
</template>


<script lang="ts">
  import Vue from 'vue';
  Vue.extend({
    methods:{
      getTemplate(tempRef){
        return this.$refs[tempRef].innerHTML
      }
    }
  })
</script>

X-Templates

Use an x-template. Define a script tag inside the index.html file.

The x-template then can be referenced in multiple components within the template definition as #my-template.

Run the snippet for an example.

See the Vue.js doc more information about x-templates.

Vue.component('my-firstname', {
  template: '#my-template',
  data() {
    return {
      label: 'First name'
    }
  }
});

Vue.component('my-lastname', {
  template: '#my-template',
  data() {
    return {
      label: 'Last name'
    }
  }
});

new Vue({
  el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <my-firstname></my-firstname>
  <my-lastname></my-lastname>
</div>

<script type="text/x-template" id="my-template">
  <div>
    <label>{{ label }}</label>
    <input />
  </div>
</script>

Not really sure i understand your problem here, but i'll try to give you something that i will opt to do if i want to add two components in one template.

HeaderSection.vue

    <template>
        <div id="header_id" :style="'background:'+my_color">
            welcome to my blog
        </div>
    </template>
    <script>
        export default {

            props: ['my_color']
        }
    </script>

BodySection.vue

    <template>
        <div id="body_id">
            body section here
        </div>
    </template>
    <script>
        export default {

        }
    </script>

home.vue

<template>

    <div id="parentComponent">

        <header-section :color="my_color" />

        <body-section />
    </div>
</template>
<script>
    import HeaderSection from "./components/HeaderSection.vue"
    import BodySection from "./components/BodySection.vue"
    export default {

        name: "home",

        components: {
            HeaderSection,
            BodySection
        },

        data() {
            return {
                my_color: "red"
            }
        }
    }
</script>
发布评论

评论列表(0)

  1. 暂无评论