Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One VueJS Component with multiple templates

How can I have a component with multiple templates or otherwise separate any methods and data away from any one specific template?

Components in VueJS are supposed to be a reusable piece, aren't they? If I have a user with its methods and data, surely it will behave the same throughout most (all?) of my UI. However, the way it will be displayed will vary.

like image 625
Cryptic Avatar asked Oct 30 '15 07:10

Cryptic


People also ask

Can you have multiple templates in Vue?

Instead of making two different components and two different templates, you could make one component and one template and display different elements using v-if/v-show.

Can a component have multiple templates?

Note Although it's possible for a component to render multiple templates, we recommend using an if:true|false directive to render nested templates conditionally instead. Create multiple HTML files in the component bundle.

Is Vue two way binding?

Vue is also perfectly capable of powering sophisticated Single-Page Applications in combination with modern tooling and supporting libraries. The v-model directive makes two-way binding between a form input and app state very easy to implement.

What is Renderless component Vue?

Renderless components in Vue are components that tuck away functionality without dictating how the UI for that functionality should look. Simply put, renderless components say "here's the functionality you need, it's up to you to use it and style it". The main benefit is reusability of the core functionality.


1 Answers

I often accomplish this by passing in a prop and using that prop for conditional rendering.

For example:

<template>
    <div>
        <div v-if="isActive">RADICAL ACTIVE STATE</div>
        <div v-if="!isActive">YOLO NOT ACTIVE</div>
    </div>
<template>

<script>
export default {
    props: {
        isActive: {
            type: Boolean,
            required: true,
        },
    },
};
</script>

That will probably get you there 80% of the time unless that "single root wrapper" thing is causing you a problem.

In that case, you definitely have options. One of which could be to use a slot and control the conditional rendering upstream. That might result in less markup.

Maybe you could instead just create two components that are each simpler than they would be otherwise. Reducing cyclomatic complexity is always good.

You can also throw v-if on the template itself <template v-if="">.

In my experience today, you cannot use two templates in one single file component (like the one in the code I showed above). Vue doesn't seem to parse the templates correctly. Vue was simply not executing my computed prop or method. It was always rendering the second template, so Vue may have internal logic whereby it loads the "last observed template".

A person will probably find it helpful to read http://vuejs.org/guide/components.html#Fragment_Instance because it illustrates the template instance prop.

It's the closest I've seen to the React equivalent to a stateless dumb component such as:

const ShowNumber = ({ num }) => (
    <div>{num}</div>
)

...

<ShowNumber num={1337} />

If my answer here is wetting your appetite but you feel like it still isn't quite what you need, Google "Vue render props" and take a look at how you can do crazy-complex logic inside the render instance prop. It's very similar to React's JSX, but I will say that if you are in this territory, there is probably a simpler way to do it.

I might recommend just creating two single-file components with one template in each, or find a way to use slots and control the behaviour from upstream.

You are also pretty safe to do it like I showed in my example above, because the data flow through the component is unidirectional and deterministic, and the UI will always be a reflection of the props and data. If the props or data change, your component will render the correct elements.

You could do this in the upstream parent component:

<large-widget v-if="someState === 'one'"></large-widget>

<small-widget v-else-if="someState === 'two'"></small-widget>

<div v-else>WIDGET IS BORKEN</div>
like image 176
agm1984 Avatar answered Nov 15 '22 19:11

agm1984