Есть ли эквивалент vue.js для ngTemplateOutlet?
Имеет ли vue.js эквивалент директивы Angular *ngTemplateOutlet
? Допустим, у меня есть некоторые компоненты, определенные следующим образом:
<template>
<div id="independentComponent">
Hello, {{firstName}}!
</div>
</template>
<script>
export default {
name: "independentComponent",
props: ['firstName']
}
</script>
...
<template>
<div id="someChildComponent">
<slot></slot>
<span>Let get started.</span>
</div>
</template>
<script>
export default {
name: "someChildComponent"
}
</script>
Я хочу иметь возможность сделать что-то вроде этого:
<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>
В Angular я мог бы сделать это с помощью
<div id="parentComponent">
<someChildComponent>
<ng-container *ngTemplateOutlet="independentInstance"></ng-container>
</someChildComponent>
<ng-template #independentInstance>
<independentComponent [firstName]="firstName"></independentComponent>
</ng-template>
</div>
Но похоже, что Vue требует, чтобы элемент был записан в DOM именно там, где он находится в шаблоне. Есть ли способ ссылаться на встроенный элемент и использовать его для передачи другому компоненту в качестве слота?
Ответы
Ответ 1
Вы не можете повторно использовать шаблоны, такие как ngTemplateOutlet
, но можете объединить идею $refs
, v-pre
и компиляции шаблона времени выполнения с v-runtime-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>
Теперь вы можете повторно использовать шаблон independentInstance
:
<v-runtime-template
:template="$refs.independentInstance.innerHTML"
v-if="$refs.independentInstance">
</v-runtime-template>
Но имейте в виду, что вы не можете изменить readOnlyVar
из шаблона independentInstance
- vue предупредит вас с помощью:
[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 value. Prop being mutated: "readOnlyVar"
Но вы можете обернуть его в object
, и он будет работать:
@Component({
components: {
VRuntimeTemplate
}
})
export default class ObjectList extends Vue {
reusableContext = {
readWriteVar: '...'
};
readOnlyVar = '...';
}
Ответ 2
Вы можете попробовать Portal vue, написанный LinusBorg основным участником команды Vue.
PortalVue представляет собой набор из двух компонентов, которые позволяют вам визуализировать шаблон компонента (или его часть) в любом месте документа - даже вне той части, которая контролируется вашим приложением Vue!
Пример кода:
<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>
Существует также vue-simple-portal, написанный тем же автором, который меньше по размеру, но монтирует компонент в конец элемента body.
Ответ 3
X-шаблоны
Используйте x-template
. Определите тег сценария внутри файла index.html
.
На x-template
можно ссылаться в нескольких компонентах в определении шаблона как #my-template
.
Запустите фрагмент для примера.
Смотрите документ Vue.js для получения дополнительной информации о x-шаблонах.
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>
Ответ 4
Не совсем уверен, что я понимаю вашу проблему здесь, но я постараюсь дать вам кое-что, что я решу сделать, если я хочу добавить два компонента в один шаблон.
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>