データ駆動UIとVue .JSとクエーサー
65863 ワード
データ駆動UIとVue .JSとクエーサー
説明
2020年3月中旬に、フレームワークVueを使用してJSON(Data Drive UI)のスキーマ定義に基づいて動的UI生成の最初の試みを開始しました.クェーサー.
データ駆動UIの概念は、以下のような興味深い解決を可能にします.
動的に生成されたフォームは、フィールドのためのエディットコントロールコンポーネント(および他の関連プロパティ)の型を持つデータベース内の各対応するエンティティフィールドのフィールド定義スキーマを提示します.これらのコントロールは、他のグループの下で、またはグループ(タブ、カード、展開など)の下に表示されます.このスキームは、お互いの依存関係(EG諸国、州、都市)に関連するルックアップフィールドも提供しました.エディットコントロールは、イベントコミュニケーション用のイベントバスの使用やフォーム、エディットコントロール、ラッパーコンポーネント間のプロパティ通信のスコープスロットのようないくつかの調整を使用してクエーサーフレームワークのフォームコントロールに基づいています.JSONスキーマのスロットを使用するいくつかの複雑なコンポーネントの構成も実装されました.レンダララッパーコンポーネントは、RESTful/GraphSQL APIとの相互作用のために提供され、データベース内の対応するエンティティ/ルックアップのデータと対話します.
単純さの理由のために、大部分の機能は主要なコンポーネント(すなわちフォーム、グループと編集規制(この記事の焦点である))のダイナミックなレンダリングだけに集中するためにオリジナルのコードから除外されました.私たちは、タブでグループ化された分野でフォームの実装を維持しました.
事前の要件
私たちは、Git CLI、JavaScript、Vueの良い知識を持っていると仮定します.JSとクエーサーフレームワーク.Vue CLIとQuasar CLIをシステムにインストールする必要があります.このチュートリアルはLinux環境で実行されていましたが、あなたの好みのオペレーティングシステムについて簡単に調整できます.
JSONスキーマ構造
JSON構造はかなり簡単です.各グループ項目のフィールドのグループとリストを定義します.
しかし、フィールドプロパティを定義するのは、サポートされているクエーサーUIコントロールを許可するように複雑です.
スキーマのフィールドプロパティを使用すると、フィールドに入力された値、マスクの編集、多くの視覚的側面、および大いに多くの検証規則を定義できます.
JSON構造は以下の通りです.
GroupModel : string =>(タブのみがサポートされています);
array::arrayグループの配列
export default {
/*
* Group type: Only 'tab' is currently supported
*/
groupModel: "tab",
/*
* List of group itens
*/
groups: [
{
/*
* Main properties (name, label, icon)
*/
name: "Group 1",
label: "Group 1",
icon: "mail",
/*
* Control type specific properties
*/
flat: true,
"expand-separator": true,
/*
* Field list: name, id and fieldType
are the main properties, the others are
UI control specific properties.
*/
fields: [
{
/*
* Main field properties
*/
name: "id",
id: "g1_id",
fieldType: "inputtext",
/*
* Control type specific properties
*/
label: "id",
dense: false,
readonly: true,
hidden: true,
},
/*
* Other fields definitions...
*/
{
name: "name",
id: "g1_name",
fieldType: "inputtext",
label: "Name",
placeholder: "Name...",
hint: "Inform the name...",
dense: true,
clearable: true,
"clear-icon": "close",
/*
* Validation rules can be defined as in the example below
*/
rules: [
{
params: ["val"],
exp: '!!val || "Name is required!"',
},
],
},
{
name: "on",
id: "g1_on",
fieldType: "btntoggle",
label: "On?",
hint: "Report if ON or OFF...",
dense: false,
clearable: true,
"stack-label": true,
filled: false,
options: [
{ label: "On", value: "on" },
{ label: "Off", value: "off" },
],
},
{
name: "onoff",
id: "g1_onoff",
fieldType: "checkbox",
"outer-label": "On or Off?",
label: "On/Off",
hint: "Report if ON or OFF...",
"indeterminate-value": null,
"true-value": "on",
"false-value": "off",
dense: false,
clearable: true,
"stack-label": true,
filled: false,
},
{
name: "alive",
id: "g1_alive",
fieldType: "radio",
"outer-label": "Is alive?",
label: "Alive",
hint: "let me know if you're alive...",
val: "alive",
dense: false,
clearable: true,
"stack-label": true,
filled: false,
},
{
name: "birthday",
id: "g1_birthday",
fieldType: "datepicker",
label: "Birthday",
hint: "enter your birthday...",
mask: "YYYY-MM-DD",
titleFormat: "ddd., DD [de] MMM.",
dense: false,
clearable: true,
"stack-label": true,
filled: false,
},
{
name: "time",
id: "g1_time",
fieldType: "timepicker",
label: "Time",
hint: "Inform the time...",
format24h: true,
dense: false,
clearable: true,
"stack-label": true,
filled: false,
},
{
name: "date",
id: "g1_date",
fieldType: "inputdate",
label: "Date",
placeholder: "Date...",
dateMask: "DD/MM/YYYY",
mask: "##/##/####",
hint: "Inform the date...",
titleFormat: "ddd., DD [de] MMM.",
dense: true,
clearable: true,
},
{
name: "time2",
id: "g1_time2",
fieldType: "inputtime",
label: "Time",
placeholder: "Time...",
timeMask: "HH:mm:ss",
mask: "##:##:##",
hint: "Inform the time...",
format24h: true,
withSeconds: true,
dense: true,
clearable: true,
},
{
name: "date_time",
id: "g1_date_time",
fieldType: "inputdatetime",
label: "Date/Time",
placeholder: "Date/Time...",
dateMask: "DD/MM/YYYY HH:mm:ss",
mask: "##/##/#### ##:##:##",
hint: "Inform the date and time...",
dateTitleFormat: "ddd., DD [de] MMM.",
format24h: true,
withSeconds: true,
dense: true,
clearable: true,
},
{
name: "options",
id: "g1_options",
fieldType: "select",
label: "Options",
hint: "Inform the option...",
dense: true,
clearable: true,
transitionShow: "flip-up",
transitionHide: "flip-down",
options: ["Google", "Facebook", "Twitter", "Apple", "Oracle"],
},
{
name: "word",
id: "g1_word",
fieldType: "editor",
label: "Editor",
hint: "Spills the beans...",
clearable: true,
"stack-label": true,
"min-height": "5rem",
},
{
name: "range",
id: "g1_range",
fieldType: "range",
outerLabel: "Range",
hint: "Inform the range...",
clearable: true,
"stack-label": true,
min: 0,
max: 50,
label: true,
},
{
name: "track",
id: "g1_track",
fieldType: "slider",
outerLabel: "Track",
hint: "Drag...",
clearable: true,
"stack-label": true,
min: 0,
max: 50,
step: 5,
label: true,
},
{
name: "evaluate",
id: "g1_evaluate",
fieldType: "rating",
label: "Rating",
hint: "Do the evaluation...",
clearable: true,
"stack-label": true,
max: 5,
size: "2em",
color: "primary",
},
{
name: "open_close",
id: "g1_open_close",
fieldType: "toggle",
"outer-label": "Open?",
label: "Open",
hint: "Open or closed report...",
dense: false,
clearable: true,
"stack-label": true,
filled: false,
color: "primary",
"true-value": "on",
"false-value": "off",
},
{
name: "files",
id: "g1_files",
fieldType: "uploader",
"outer-label": "Send files",
label: "Select the files",
hint: "Select the files...",
dense: false,
clearable: true,
multiple: true,
"stack-label": true,
},
],
},
{
name: "Group 2",
label: "Group 2",
icon: "alarm",
flat: true,
"expand-separator": true,
},
{
name: "Group 3",
label: "Group 3",
icon: "movie",
flat: true,
"expand-separator": true,
},
],
};
魔法はどう
フレームワークに必要なリソース
フレームワークを動作させるためには、動的にコンポーネントを作成する可能性をサポートする必要があります.幸運にもVue.JSは、これらのことで非常に良いです!
VueJSスポートConditional Rendering - (v-if/v-else/v-else-if) , and List Rendering - (v-for) . これらの機能を使用すると、JSONスキーマを反復処理し、条件付きでUIコンポーネントをレンダリングできます.
条件付きRenenderingはいくつかの種類のコントロールについてはOKですが、あなたがそれらの多くを持っているときに最適なオプションではありません(この記事では、私たちはあなたのためにボーナスコントロールとしてフォームコントロールの約20種類を定義しました!)
この種の挑戦Vueのために.JSサポートdynamic component creation - (:is) . この機能を参照すると、動的にコンポーネントインスタンスをインポートできます.
また、各コントロールタイプが異なるプロパティセットを持っていることを述べました.仕事のために、Vue.JSはバッチ内のオブジェクトのすべてのプロパティをリンクできるようにする必要があります.そして再びVue.JSには以下の解決策があります.Passing all properties of an Object - (v-bind) .
以下のセクションでは、上記のすべての機能がどのように使われますか
template
成形機の断面Vue問題へのクリーンで簡潔な解決を作成します.
コンポーネントインフラストラクチャ
src/componentフォルダには一連のソースコードがあります.全体をどのように実装したかを理解するために分析しましょう.
<小野寺>js
このmixin object を注入します.Vueこの関数は、データ名(ComponentMap [])を提供することです.コンポーネント名を動的にインポートし、その名前のコンポーネントインスタンスを返すファクトリに解決します.
/**
* A mixin object that mantain a dictionary de components
*/
export default {
data() {
return {
componentMap: {},
};
},
methods: {
initComponentsMap() {
this.componentMap = {
// Group components
card: () => import("./Card01"),
tabs: () => import("./Tabs01"),
tab: () => import("./Tab01"),
tabpanel: () => import("./TabPanel01"),
expansion: () => import("./Expansion01"),
// Form component
form: () => import("./Form01"),
// From field components
inputtext: () => import("./Input01"),
inputdate: () => import("./DateInput01"),
inputtime: () => import("./TimeInput01"),
inputdatetime: () => import("./DateTimeInput01"),
select: () => import("./Select01"),
checkbox: () => import("./CheckBox01"),
radio: () => import("./Radio01"),
toggle: () => import("./Toggle01"),
btntoggle: () => import("./ButtonToggle01"),
optgroup: () => import("./OptionGroup01"),
range: () => import("./Range01"),
slider: () => import("./Slider01"),
datepicker: () => import("./DatePicker01"),
timepicker: () => import("./TimePicker01"),
rating: () => import("./Rating01"),
uploader: () => import("./Uploader01"),
editor: () => import("./Editor01"),
// Other
icon: () => import("./Icon01"),
};
},
},
};
その後、辞書はtemplate
名前は以下の通り:<!-- Create a dynamica TABS type component -->
<component :is="componentMap['tabs']"></component>
フォーミングVue
これは、JSONスキーマに基づいてUIを動的に組み立てる作業の大半を行います.
それは内部のサービスのための一連の機能を持っているので、本当に重要な部分に集中しましょう.
...
import componentMap from "./_componentMap01";
...
export default {
name: "FormGenerator",
mixins: [componentMap],
provide() {
return {
// The event bus to comunicate with components
eventBus: this.eventBus,
};
},
props: {
// The schema placeholder property
schema: {
type: Object,
},
},
data() {
return {
// The event bus instance
eventBus: new Vue(),
...
// Form data with input field contents
formData: {},
...
}
}
...
}
そして最後にtemplate
これは動的なコンポーネントを作成します-テンプレートのコメントは明らかにVueの方法を説明します.JSの機能は一緒に作品を作るために働く:<template>
<!--
Dynamic wrapper `form` component
`fixedSchema` is the ajusted version of property `schema`
-->
<component v-if="fixedSchema" :is="componentMap['form']" ref="form">
<!--
==================
Groups with fields
==================
-->
<div v-if="fixedSchema.groups && fixedSchema.groups.length > 0">
<!--
==========
TAB Model
==========
-->
<!--
Dynamic `tabs` component
-->
<component
v-if="fixedSchema.groupModel == 'tab'"
:is="componentMap['tabs']"
v-model="selectedGroup"
dense
>
<!--
Dynamic `tab itens` components
-->
<component
v-for="(group, index) in fixedSchema.groups"
:is="componentMap['tab']"
:key="index"
v-bind="group"
>
</component>
</component>
<q-separator />
<!--
Dynamic `tabpanel` component
-->
<component
v-for="(group, index) in fixedSchema.groups"
:is="componentMap['tabpanel']"
:key="index"
:selected="selectedGroup"
v-bind="group"
>
<div v-if="group.fields && group.fields.length > 0">
<!--
And finally all UI field controls:
- Component type specified by `componentMap[field.fieldType]`
- Data contents linked to `formData[field.name]` by `v-model`
- All `field` properties linked by `v-bind`
-->
<component
v-for="(field, index) in validFieldComps(group.fields)"
:key="index"
:is="componentMap[field.fieldType]"
v-model="formData[field.name]"
v-bind="field"
v-show="!field.hidden"
>
</component>
</div>
</component>
</div>
</component>
</template>
/ src /コンポーネントの他の“vue”ファイル
他のコンポーネントは、基本的に所望の機能性を提供するためにオリジナルのクエーサー構成要素のうちの1つ以上をカプセル化する.彼らはイベントをformgeneratorに渡します.ITSを通じたVue
event bus
を返します.v-on="$listners"
and v-bind="$attrs"
.例として、入力から次のソースコードがあります.Vue
<template>
<q-input
v-bind="$attrs"
v-on="$listeners"
@input="onInput"
@clear="onClear"
@focus="onFocus"
@blur="onBlur"
>
<template
v-for="(_, slot) of $scopedSlots"
v-slot:[slot]="scope"
>
<slot
:name="slot"
v-bind="scope"
/>
</template>
</q-input>
</template>
<script>
import compInfo from './_compInfo'
export default {
mixins: [compInfo],
inject: ['eventBus'],
methods: {
onInput (value) {
this.eventBus.$emit('input', this, value)
},
onClear (value) {
this.eventBus.$emit('clear', this, value)
},
onFocus (evt) {
this.eventBus.$emit('focus', this, evt)
},
onBlur (evt) {
this.eventBus.$emit('blur', this, evt)
}
},
inheritAttrs: false
}
</script>
フォーミングの使い方
今では簡単な部分は
src/pages/FormTest.vue
JSONスキーマをロードし、Formgeneratorコンポーネントに渡すページがあります.<template>
<form-generator :schema="schema" />
</template>
<script>
import FormGenerator from "../components/FormGenerator";
import jsonSchema from "../data/schema.js";
export default {
components: { FormGenerator },
data() {
return {
schema: {},
};
},
created() {
this.schema = jsonSchema;
},
};
</script>
以下のコマンドで例を実行します.# Run the Quasar/Vue application
$ yarn quasar dev
次に、好みのブラウザで次のURLを入力します.http://localhost:8080
あなたはこの印象的な結果を得る
このチュートリアルからの例の実行
インストール
# Clone tutorial repository
$ git clone https://github.com/maceto2016/VueDataDrivenUI
# access the project folder through the terminal
$ cd VueDataDrivenUI
# Install dependencies
$ npm install
アプリケーションを実行する
# Run the Quasar/Vue application
$ yarn quasar dev
アプリケーションのテスト
好みのブラウザで次のURLを入力します
http://localhost:8080
結論
本論文では、定義されたデータに存在する情報に基づいてUIの動的生成にすぎないデータ駆動UIの概念を提示する.この記事では、JSONスキーマを定義し、Vueを使用してインフラストラクチャを作成する方法を簡単に示しました.コンポーネントを動的に作成するJS +クエーサーフレームワーク.ボーナスとして、我々はQuasarフレームワークUIコンポーネントに基づいて約20のUIコンポーネントを提供します.
ここに提示されたソースコードと考えを自由に使ってください.Vueへの移行を含む改良のための巨大な余地があります.JS 3、クエーサー2とtypescript.今ではあなた次第です!
読書ありがとうございます.私はあなたのフィードバックを聞いて満足している!
Reference
この問題について(データ駆動UIとVue .JSとクエーサー), 我々は、より多くの情報をここで見つけました https://dev.to/maceto2016/data-driven-ui-with-vuejs-and-quasar-2ccbテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol