Skip to content

Child Component

The difference from Page Component is that Child Component has three parts: Props, Emits and Slots. So how are the three parts defined and used in Zova?

Create Child Component

Let's first create a child component card using a cli command:

bash
$ zova :create:component card --module=a-demo

This command will create a directory src/component/card. In Zova, a child component will be splited to three files located in that directory:

src
└─ component
   └─ card
      ├─ index.vue
      ├─ controller.ts
      └─ render.tsx
NameDescription
index.vuedefine vue component
controller.tslocal bean for logic codes
render.tsxlocal bean for render codes

Props

Next, in the card child component, define three Props: header, content and footer

Define Props Interface

First, define the Props interface in controller.ts:

typescript
export interface Props {
  header?: string;
  content?: string;
  footer?: string;
}

You can also set default values for Props:

typescript
export class ControllerCard {
  static $propsDefault = {
    header: 'default header',
  };
}

Define Component Props

Then, define the component Props in index.vue:

typescript
<script setup lang="ts">
import { ControllerCard, Props} from './controller.js';
const props = withDefaults(defineProps<Props>(), ControllerCard.$propsDefault);
</script>

Access Props

Access Props in render.tsx:

typescript
export class RenderCard {
  render() {
    return (
      <div>
        <div>
          <div style={{ backgroundColor: 'teal' }}>
            <div>Prop: {this.$props.header}</div>
          </div>
          <div style={{ backgroundColor: 'orange' }}>
            <div>Prop: {this.$props.content}</div>
          </div>
          <div style={{ backgroundColor: 'green' }}>
            <div>Prop: {this.$props.footer}</div>
          </div>
        </div>
      </div>
    );
  }
}

Use Props

Next, use the child component inside the parent component:

typescript
import Card from '../../component/card/index.vue';

export class RenderComponent {
  render() {
    return (
      <div>
        <Card
          header="header"
          content="content"
          footer="footer"
        ></Card>
      </div>
    );
  }
}
  • Import the child component Card from index.vue, and then directly pass the value to the props of Card

Emits

Next, in the card child component, define an Emit: reset

Define Emits Interface

First, define the Emits interface in controller.ts:

typescript
export type Emits = {
  (e: 'reset', time: Date): void;
};

Define Component Emits

Then, define the component Emits in index.vue:

typescript
<script setup lang="ts">
import { Emits } from './controller.js';
const emit = defineEmits<Emits>();
</script>

Raise Emit

Raise Emit in render.tsx:

typescript
export class RenderCard {
  render() {
    return (
      <div>
        <button
          onClick={() => {
            this.$emit('reset', new Date());
          }}
        >
          Reset Time
        </button>
      </div>
    );
  }
}

Use Emits

Next, use the child component inside the parent component:

typescript
import Card from '../../component/card/index.vue';

export class RenderComponent {
  render() {
    return (
      <div>
        <Card
          onReset={time => {
            console.log(time);
          }}
        ></Card>
      </div>
    );
  }
}
  • Import the child component Card from index.vue, and then directly pass the event callback function to onReset

Slots

Next, in the card child component, define three Slots: header, default and footer

Define Slots Interface

First, define the Slots interface in controller.ts:

typescript
export interface Slots {
  header?(): JSX.Element;
  default?(): JSX.Element;
  footer?(): JSX.Element;
}

Render Slots

Render Slots in render.tsx:

typescript
export class RenderCard {
  render() {
    return (
      <div>
        <div>
          <div style={{ backgroundColor: 'teal' }}>
            <div>Slot: {this.$slots.header?.()}</div>
          </div>
          <div style={{ backgroundColor: 'orange' }}>
            <div>Slot: {this.$slots.default?.()}</div>
          </div>
          <div style={{ backgroundColor: 'green' }}>
            <div>Slot: {this.$slots.footer?.()}</div>
          </div>
        </div>
      </div>
    );
  }
}

Use Slots

Next, use the child component inside the parent component:

typescript
import Card from '../../component/card/index.vue';

export class RenderComponent {
  render() {
    return (
      <div>
        <Card
          slots={{
            header: () => {
              return <div>this is a header slot from parent</div>;
            },
            default: () => {
              return <div>this is a default slot from parent</div>;
            },
            footer: () => {
              return <div>this is a footer slot from parent</div>;
            },
          }}
        ></Card>
      </div>
    );
  }
}
  • Import the child component Card from index.vue, and then directly pass the value to the slots prop of Card

How to refer to child component instance?

In Zova, Template Ref is not used to refer to child component instances, but directly refers to the controller bean corresponding to the child component. See: Controller Ref

Using child components through module scope

In Zova, in addition to the general usage of importing child components, you can also use child components directly through the module scope object, see: Vue Child Component

Released under the MIT License.