Generic компоненты

Generic компоненты в Quasar Framework позволяют создавать переиспользуемые, параметризованные элементы интерфейса, которые могут работать с разными типами данных и конфигурациями. Такой подход повышает гибкость приложений и уменьшает дублирование кода.


Создание Generic компонентов

Generic компонент в Quasar создаётся как стандартный Vue-компонент с использованием TypeScript для определения типов входных данных. Это позволяет использовать сильную типизацию и получать автодополнение при разработке.

Пример базового Generic компонента:

<script lang="ts" setup>
import { defineProps } from 'vue'

interface Item {
  id: number
  name: string
}

const props = defineProps<{ items: Item[] }>()
</script>

<template>
  <q-list bordered padding>
    <q-item v-for="item in props.items" :key="item.id">
      <q-item-section>{{ item.name }}</q-item-section>
    </q-item>
  </q-list>
</template>

В этом примере компонент принимает массив элементов типа Item. Можно расширить этот компонент, сделав его Generic, чтобы он принимал любой тип данных:

<script lang="ts" setup>
import { defineProps } from 'vue'

type GenericProps<T> = {
  items: T[]
  displayField: keyof T
}

const props = defineProps<GenericProps<any>>()
</script>

<template>
  <q-list bordered padding>
    <q-item v-for="item in props.items" :key="item[props.displayField]">
      <q-item-section>{{ item[props.displayField] }}</q-item-section>
    </q-item>
  </q-list>
</template>

Теперь компонент можно использовать с любым типом объектов, указывая поле, которое нужно отображать.


Использование Generic Props и Slots

Generic компоненты особенно полезны при работе со слотами, где необходимо передавать пользовательские шаблоны отображения. Например:

<script lang="ts" setup>
import { defineProps, type PropType } from 'vue'

type GenericProps<T> = {
  items: T[]
}

const props = defineProps<GenericProps<any>>()
</script>

<template>
  <q-list bordered padding>
    <slot name="item" v-for="item in props.items" :item="item" :key="item.id">
      <q-item>
        <q-item-section>{{ item.name }}</q-item-section>
      </q-item>
    </slot>
  </q-list>
</template>

Использование компонента с кастомным шаблоном:

<GenericList :items="users">
  <template #item="{ item }">
    <q-item>
      <q-item-section>{{ item.fullName }}</q-item-section>
      <q-item-section side>{{ item.age }}</q-item-section>
    </q-item>
  </template>
</GenericList>

Это позволяет полностью контролировать отображение данных без изменения внутренней логики компонента.


Типизация Generic компонентов

TypeScript обеспечивает строгую типизацию Generic компонентов, предотвращая ошибки при передаче данных. Определение типа через PropType<T> делает компонент максимально гибким и безопасным:

import { PropType } from 'vue'

interface User {
  id: number
  name: string
  email: string
}

const props = defineProps({
  items: {
    type: Array as PropType<User[]>,
    required: true
  }
})

Для Generic компонента можно использовать параметр типа T:

const props = defineProps<{ items: T[] }>()

Вместе с emit и событиями это позволяет создавать компоненты, которые работают с любым типом данных и возвращают строго типизированные события.


Примеры интеграции с Quasar-компонентами

Generic форма с q-input и q-select:

<script lang="ts" setup>
import { defineProps } from 'vue'

type Field<T> = {
  label: string
  model: keyof T
  type: 'text' | 'number' | 'select'
  options?: T[]
}

interface GenericFormProps<T> {
  data: T
  fields: Field<T>[]
}

const props = defineProps<GenericFormProps<any>>()
</script>

<template>
  <q-form>
    <q-input v-for="field in props.fields" 
             v-if="field.type === 'text'" 
             :key="field.model" 
             v-model="props.data[field.model]" 
             :label="field.label" />
    <q-select v-for="field in props.fields" 
              v-if="field.type === 'select'" 
              :key="field.model" 
              v-model="props.data[field.model]" 
              :options="field.options" 
              :label="field.label" />
  </q-form>
</template>

Такой подход позволяет строить динамические формы, где структура и типы данных полностью управляются внешними параметрами.


Паттерны использования

  1. Reusable Lists – Generic списки с кастомными слотами и полями отображения.
  2. Dynamic Forms – Формы с любым числом полей и типами данных.
  3. Configurable Tables – Таблицы с динамическими колонками и кастомными ячейками.
  4. Typed Events – Generic компоненты с строго типизированными событиями для взаимодействия с родительскими компонентами.

Важные моменты

  • Использование TypeScript усиливает контроль типов и предотвращает ошибки при передаче данных.
  • Generic компоненты с поддержкой слотов дают возможность гибкой кастомизации интерфейса без изменения внутренней логики.
  • Параметризация через Generic позволяет использовать один компонент для множества различных сценариев, снижая дублирование кода.
  • При работе с коллекциями рекомендуется использовать уникальные ключи (key) для оптимизации рендеринга Vue и корректной работы виртуального DOM.

Generic компоненты являются основой масштабируемого и типобезопасного фронтенда на Quasar Framework, позволяя строить сложные интерфейсы с минимальными усилиями и высокой переиспользуемостью кода.