# Vue Components
# SmileForm
# Use SmileForm component
PRE-REQUISITE
Make sure the SmileForm component is added to view-module
- Add a data in
.kviewfile, for example Employee data
data Employee (SmileForm) (
name: string ({"hint": "Please provide your full name."}),
password: string(password),
age: number? (),
description: string(textarea),
dob: string(datepicker),
bornTime: string(time),
deliveryTime: string(time_with_seconds),
married: boolean (),
single: boolean (checkbox),
currentDesignation: Designation (),
designationList: Designation*(),
tag: string*(),
country: string({ autocomplete, 'callback': 'populateStates'}),
states: string*({ autocomplete, 'item-text': 'text', 'item-value' : 'id'})
)
- Add to the html where SmileForm needs to be used.
<SmileForm v-model='employee' :schema='employeeSchema'></SmileForm>
- Add data as below for formSchema and formData
public employee: Data.Employee = new Data.Employee();
public employeeSchema: any = Data.Employee.toSmileFormDecorator();
# Add SmileForm component to view-module (onetime)
- Add to
component SmileForm under "smile" in the
<view_module>.kview
component SmileForm under "smile"
compile
Replace the content with the below one in components/smile/
SmileForm.vue
<template>
<div class="smileform">
<v-container :class="[containerClasses]">
<v-form :id="id" :ref="id" @submit.prevent="submitForm">
<v-layout :class="[layoutClasses]">
<template v-for="(obj, key) in schema.properties">
<template v-if="(obj['utype'] === 'textfield') && !obj['multiple'] ">
<v-text-field
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
type="text"
v-model="formData[key]"
:mask="obj['mask']"
:error-messages="errors.collect(key)"
:counter="obj['counter']"
:label="obj['title']"
:data-vv-name="key"
:required="obj['required']"
:hint="obj['hint'] ? obj['hint'] : ''"
:append-icon="obj['append-icon']"
:prepend-icon="obj['prepend-icon']"
></v-text-field>
</template>
<template v-if="(obj['utype'] === 'password') ">
<v-text-field
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
:append-icon="showPassword ? 'visibility_off' : 'visibility'"
:type="showPassword ? 'text' : 'password'"
v-model="formData[key]"
:mask="obj['mask']"
:error-messages="errors.collect(key)"
:counter="obj['counter']"
:label="obj['title']"
:data-vv-name="key"
:required="obj['required']"
:hint="obj['hint'] ? obj['hint'] : ''"
@click:append="showPassword = !showPassword"
></v-text-field>
</template>
<template v-if="(obj['utype'] === 'textarea') ">
<v-textarea
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
type="text"
v-model="formData[key]"
:mask="obj['mask']"
:error-messages="errors.collect(key)"
:counter="obj['counter']"
:label="obj['title']"
:data-vv-name="key"
:required="obj['required']"
:hint="obj['hint'] ? obj['hint'] : ''"
:auto-grow="obj['auto-grow']"
></v-textarea>
</template>
<template v-if="(obj['utype'] === 'datepicker') ">
<v-menu
:ref="key"
:close-on-content-click="false"
v-model="tempObj[key]"
:nudge-right="40"
:return-value.sync="formData[key]"
lazy
transition="scale-transition"
offset-y
full-width
min-width="290px"
>
<v-text-field
:ref="key+'textfield'"
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
:type="obj['type'] ? obj['type'] : 'text'"
v-model="formData[key]"
:mask="obj.mask"
:error-messages="errors.collect(key)"
:counter="obj.counter"
:label="obj['title']"
:data-vv-name="key"
:required="obj['required']"
:hint="obj['hint']"
slot="activator"
:append-icon="obj['append-icon']"
:prepend-icon="obj['prepend-icon']"
readonly
></v-text-field>
<v-date-picker v-model="formData[key]" >
<v-spacer></v-spacer>
<v-btn flat color="primary" @click="tempObj[key] = false">Cancel</v-btn>
<v-btn flat color="primary" @click="$refs[key][0].save(formData[key]); errors.remove(key); ">OK</v-btn>
<!-- <v-btn flat color="primary" @click="removeErrorMessages(key)">OK</v-btn> -->
</v-date-picker>
</v-menu>
</template>
<template v-if="(obj['utype'] === 'timepicker')">
<v-text-field
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
type="text"
v-model="formData[key]"
:mask="obj['mask']"
:error-messages="errors.collect(key)"
:counter="obj['counter']"
:label="obj['title']"
:data-vv-name="key"
:required="obj['required']"
:hint="obj['hint']"
:placeholder="obj['placeholder']"
:append-icon="obj['append-icon']"
:prepend-icon="obj['prepend-icon']"
></v-text-field>
</template>
<template v-if="obj['utype'] === 'number'">
<v-text-field
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
type="number"
v-model="formData[key]"
:mask="obj['mask']"
:error-messages="errors.collect(key)"
:counter="obj['counter']"
:label="obj['title']"
:data-vv-name="key"
:required="obj['required']"
:hint="obj['hint'] ? obj['hint'] : ''"
:append-icon="obj['append-icon']"
:prepend-icon="obj['prepend-icon']"
></v-text-field>
</template>
<template v-if="obj['utype'] === 'switch'">
<v-switch
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
v-model="formData[key]"
:mask="obj['mask']"
:error-messages="errors.collect(key)"
:value="formData[key]"
:label="obj['title']"
:data-vv-name="key"
type="swtich"
:required="obj['required']"
:persistent-hint="obj['persistent-hint']"
:hint="obj['hint']"
></v-switch>
</template>
<template v-if="obj['utype'] === 'checkbox'">
<v-checkbox
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
v-model="formData[key]"
:mask="obj['mask']"
:error-messages="errors.collect(key)"
:label="obj['title']"
:data-vv-name="key"
:multiple="obj['multiple']"
type="checkbox"
:required="obj['required']"
:persistent-hint="obj['persistent-hint']"
:hint="obj['hint']"
></v-checkbox>
</template>
<template v-if="obj['utype'] === 'select-box'">
<v-select
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
v-model="formData[key]"
:mask="obj['mask']"
@change="obj['callback'] ? onChangeOfSelect(obj['callback']) : ''"
:error-messages="errors.collect(key)"
:items="obj.options ? obj.options : obj.options"
:item-text="obj['item-text'] ? obj['item-text'] : 'value'"
:item-value="obj['item-value'] ? obj['item-value'] : 'id'"
:label="obj['title']"
:data-vv-name="key"
:multiple="obj['multiple']"
:chips="obj['chips'] ? obj['chips'] : true"
:small-chips="obj['small-chips'] ? obj['small-chips'] : false"
:deletable-chips="obj['deletable-chips'] ? obj['deletable-chips'] : true"
:clearable="obj['clearable'] ? obj['clearable'] : true"
:menu-props="obj['menu-props'] ? obj['menu-props'] :{maxHeight:300}"
:return-object="obj['return-object'] ? obj['return-object'] : false"
:required="obj['required']"
:persistent-hint="obj['persistent-hint']"
:hint="obj['hint']"
:dense="obj['dense'] ? obj['dense'] : true"
></v-select>
</template>
<template v-if="(obj['utype'] === 'combo-box' || (obj['utype'] === 'textfield') && obj['multiple'])">
<v-combobox
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
v-model="formData[key]"
:mask="obj['mask']"
@change="obj['callback'] ? onChangeOfSelect(obj['callback']) : ''"
:error-messages="errors.collect(key)"
:items="obj.options ? obj.options : obj.options"
:item-text="obj['item-text'] ? obj['item-text'] : 'value'"
:item-value="obj['item-value'] ? obj['item-value'] : 'id'"
:label="obj['title']"
:data-vv-name="key"
:multiple="obj['multiple']"
:chips="obj['chips'] ? obj['chips'] : true"
:small-chips="obj['small-chips'] ? obj['small-chips'] : false"
:deletable-chips="obj['deletable-chips'] ? obj['deletable-chips'] : true"
:clearable="obj['clearable'] ? obj['clearable'] : true"
:menu-props="obj['menu-props'] ? obj['menu-props'] :{maxHeight:300}"
:return-object="obj['return-object'] ? obj['return-object'] : false"
:required="obj['required']"
:persistent-hint="obj['persistent-hint']"
:hint="obj['hint']"
:dense="obj['dense'] ? obj['dense'] : true"
></v-combobox>
</template>
<template v-if="(obj['utype'] === 'auto-complete') ">
<v-autocomplete
v-validate="validations(key)"
:validate-on-blur="obj['validate-on-blur'] ? obj['validate-on-blur'] : true"
v-model="formData[key]"
:mask="obj['mask']"
@change="obj['callback'] ? onChangeOfSelect(obj['callback'], formData[key]) : ''"
:error-messages="errors.collect(key)"
:items="obj.options ? obj.options : obj.options"
:item-text="obj['item-text'] ? obj['item-text'] : 'value'"
:item-value="obj['item-value'] ? obj['item-value'] : 'id'"
:label="obj['title']"
:data-vv-name="key"
:multiple="obj['multiple']"
:chips="obj['chips'] ? obj['chips'] : true"
:small-chips="obj['small-chips'] ? obj['small-chips'] : false"
:deletable-chips="obj['deletable-chips'] ? obj['deletable-chips'] : true"
:clearable="obj['clearable'] ? obj['clearable'] : true"
:menu-props="obj['menu-props'] ? obj['menu-props'] :{maxHeight:300}"
:return-object="obj['return-object'] ? obj['return-object'] : false"
:required="obj['required']"
:persistent-hint="obj['persistent-hint']"
:hint="obj['hint']"
:dense="obj['dense'] ? obj['dense'] : true"
></v-autocomplete>
</template>
</template>
</v-layout>
<v-btn type="submit" color="primary" v-if="submit">Submit</v-btn>
<v-btn type="button" color="default" @click="clearForm" v-if="clear">Clear</v-btn>
</v-form>
</v-container>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop, Emit, Watch } from 'vue-property-decorator';
// import store, * as Store from '@/../src-gen/store';
// import * as Data from '@/../src-gen/data';
// import * as Enum from '@/../src-gen/enums';
// import * as ReaderActionSet from '@/../src-gen/reader-action-set';
// import * as WriterActionSet from '@/../src-gen/writer-action-set';
// import * as ReaderService from '@/../src-gen/reader-service';
// import * as WriterService from '@/../src-gen/writer-service';
@Component
export default class SmileForm extends Vue {
@Prop() private value!: any;
@Prop() private schema!: any;
@Prop({
default: (Math.floor(Math.random() * (1000 - 10 + 1)) + 10).toString()
}) private id!: string;
@Prop({ default: true }) private clear!: boolean;
@Prop({ default: true }) private submit!: boolean;
@Prop({ default: 'wrap column' })
private layoutClasses!: string;
@Prop({ default: 'fluid grid-list-md' })
private containerClasses!: string;
private showPassword: boolean = false;
private tempObj: object = {}; // For Datpicker
get formData() {
return this.value;
}
set formData(value) {
this.formData = value;
}
@Watch('formData')
public onFormDataChanged(value: any, oldValue: any) {
this.$emit('input', value);
}
public mounted() {
console.log(this.$validator);
// this.$validator.localize('en', this.dictionary);
}
public validations(field: any) {
let validations = '';
if (this.schema.properties) {
const props = this.schema.properties[field];
if (props.required === true) {
validations = 'required';
}
if (props.validate) {
validations = validations + '|' + props.validate;
}
}
return validations;
}
public submitForm() {
console.log('I am in submit form');
const vm: any = this;
this.$validator
.validateAll()
.then(() => {
if (vm.errors.items.length > 0) {
console.log('Error occured.');
// vm.$toastr.e("ERRROR MESSAGE");
} else {
vm.$emit('submit', vm.value);
}
})
.catch(() => {});
}
public clearForm() {
this.$emit('input', {});
this.$validator.reset();
}
public onChangeOfSelect(callback: any, value: any) {
const vm: any = this;
console.log('I am in onChangeOfSelect ...' + callback);
vm.$parent[callback](value);
console.log(this.schema);
}
}
</script>
<style>
</style>
- Copy the decorator of SmileForm
decorator SmileForm {
default {
"utype" : "textfield"
}
string default {
"utype" : "textfield"
}
string password {
"utype" : "password",
"validate" : "min:8|max:25|verify_password"
}
string textarea {
"utype" : "textarea",
"counter" : "50",
"auto-grow" : true
}
string datepicker {
"utype" : "datepicker",
"append-icon" : "event"
}
string time {
"utype" : "timepicker",
"mask" : "time",
"append-icon" : "access_time",
"placeholder" : "##:##"
}
string time_with_seconds {
"utype" : "timepicker",
"mask" : "time-with-seconds",
"append-icon" : "access_time",
"placeholder" : "##:##:##"
}
string combobox {
"utype" : "combo-box"
}
number default {
"utype" : "number"
}
boolean default {
"utype" : "switch"
}
checkbox {
"utype" : "checkbox"
}
enum default {
"utype" : "select-box"
}
string selectbox {
"utype" : "select-box"
}
string autocomplete {
"utype" : "auto-complete"
}
}
# SmileGrid
# Use SmileGrid component
PRE-REQUISITE
Make sure the SmileGrid component is added to view-module
- Add a data in
.kviewfile, for example EmployeeGrid data
data EmployeeGrid (SmileGrid) (
empId: string,
name: string ({'link': 'emp/:empId'}),
age: number? (align_right),
emailId: string({disable_sortable, "formatter" : "formatEmail"}),
dob: string({"title": "Date of Birth"}),
actionDelete: string(action_icon_delete_dialog),
expand: string(action_expand)
)
- Add to the html where SmileForm needs to be used.
<SmileGrid
:schema="personSchema"
:items="personItems"
item-key="name"
@delete="deleteAction"
>
</SmileGrid>
- Add data as below for formSchema and formData
public personItems: Data.PersonGrid[] = [];
public personSchema: any = Data.PersonGrid.toSmileGridDecorator();
# Add SmileGrid component to view-module (onetime)
- Add to
component SmileGrid under "smile" in the
<view_module>.kview
component SmileGrid under "smile"
compile
Replace the content with the below one in components/smile/
SmileGrid.vue
<template>
<div >
<v-toolbar flat color="white">
<v-toolbar-title v-if="gridTitle">{{gridTitle}} </v-toolbar-title>
<v-divider class="mx-2" inset vertical v-if="gridTitle" ></v-divider>
<div class="text-xs-center" v-if="showCount">
<v-chip>{{items.length}}</v-chip>
</div>
<v-spacer></v-spacer>
<v-spacer></v-spacer>
<v-spacer></v-spacer>
<v-text-field v-model="searchQuery" append-icon="search" label="Search" v-if="search" ></v-text-field>
<slot name="toolbar"></slot>
</v-toolbar>
<v-data-table
:dark="dark"
:headers="gridHeaders"
:hide-headers="hideHeaders"
:hide-actions="hideActions"
:no-data-text="noDataText"
select-all
:item-key="itemKey"
v-model="selected"
:items="items"
class="elevation-1"
:search="searchQuery"
>
<template slot="headers" slot-scope="props">
<tr>
<th v-if="value" width="20px">
<v-checkbox
:input-value="props.all"
:indeterminate="props.indeterminate"
primary
hide-details
@click.native="toggleAll"
></v-checkbox>
</th>
<template v-for="header in props.headers">
<th
v-if="header.sortable"
:key="header.text"
:class="[header.align, 'sortable column', pagination.descending ? 'desc' : 'asc', header.value === pagination.sortBy ? 'active' : '', !header.align && schema.properties[header.value].type === 'number' ? 'text-xs-right' : '']"
v-show="!schema.properties[header.value].hidden"
@click="changeSort(header.value)"
>
<v-icon small>arrow_upward</v-icon>
{{ header.text }}
</th>
<th
v-if="!header.sortable"
:key="header.text"
:class="[header.align, ' column', pagination.descending ? 'desc' : 'asc', header.value === pagination.sortBy ? 'active' : '']"
v-show="!schema.properties[header.value].hidden"
>
{{ header.text }}
</th>
</template>
</tr>
</template>
<template slot="items" slot-scope="props">
<tr :active="props.selected" @click="props.selected = !props.selected">
<td v-if="value">
<v-checkbox
:input-value="props.selected"
primary
hide-details
></v-checkbox>
</td>
<template v-for="(value, key) in schema.properties">
<td v-if="value && !value.actionExpand"
:class="[value.classes ? value.classes : '', !value.classes && value.align ? 'text-xs-'+value.align : '', , !value.classes && !value.align && value.type == 'number' ? 'text-xs-right' : '']"
v-show="!value.hidden"
>
<template v-if="value.link">
<a :href="buildLink(value.link, props.item)" >{{ props.item[key] }}</a>
</template>
<template v-if="!value.link && !value.formatter">
{{ props.item[key] }}
</template>
<template v-if="value.formatter">
<div v-html="$parent[value.formatter](props.item[key], props.item)"></div>
</template>
<v-btn :id="value.actionId" small v-if="value.actionId && !value.actionButton && !value.actionIcon && !value.actionExpand" class="mr-2" @click.native="$emit( value.actionId, props.item)" > {{getTitleCase(value.actionId)}} </v-btn>
<v-btn v-if="value.actionId && value.actionButton " small class="mr-2" @click.native="$emit( value.actionId, props.item)" > {{value.btnText}} </v-btn>
<v-icon v-if="value.actionId && value.actionIcon && !value.dialog " small @click="action(value.actionId, props.item)" > {{value.icon}} </v-icon>
<v-dialog v-model="dialog[props.item[itemKey]]" max-width="290" v-if="value.actionId && value.actionIcon && value.dialog ">
<v-icon slot="activator" small > {{value.icon}} </v-icon>
<v-card>
<v-card-title class="headline">{{ props.item[itemKey] }}</v-card-title>
<v-card-text> Do you want to {{getTitleCase(value.actionId)}} ? </v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="red darken-1" flat="flat" @click="dialog[props.item[itemKey]] = false" > Cancel </v-btn>
<v-btn color="blue darken-1" flat="flat" @click="action(value.actionId, props.item); dialog[props.item[itemKey]] = false;" > Agree </v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</td>
<td v-if="!value && !value.actionExpand">
{{ props.item[key] }}
</td>
<td @click="props.expanded = !props.expanded" v-if="value.actionExpand">
<v-icon v-show="!props.expanded">add</v-icon><v-icon v-show="props.expanded">remove</v-icon>
</td>
</template>
</tr>
</template>
<template slot="expand" slot-scope="props">
<v-card flat>
<v-card-text>
<div v-html="$parent['expandRow'](props.item)"></div>
</v-card-text>
</v-card>
</template>
</v-data-table>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
@Component
export default class SmileGrid extends Vue {
@Prop() private value: any[];
@Prop() private schema!: any ;
@Prop() private items!: any [];
@Prop() private itemKey!: string;
@Prop() private gridTitle: string;
@Prop() private gridSubTitle: string;
@Prop({ default: true}) private search!: boolean;
@Prop({ default: false }) private hideHeaders!: boolean;
@Prop({ default: false }) private hideActions!: boolean;
@Prop({ default: 'No data to be displayed' }) private noDataText!: string;
@Prop({ default: false}) private dark!: boolean;
@Prop({ default: false}) private showCount!: boolean;
private selected: any [] = [];
private pagination: any = { sortBy: 'name' };
private dialog: any = {};
private searchQuery: string = '';
private get gridHeaders() {
const vm = this;
const headers = [];
for (const key of Object.keys(vm.schema.properties)) {
const title = (vm.schema.properties[key].title) ?
vm.schema.properties[key].title : vm.schema.properties[key].title;
const header: any = {
text : title,
value : key,
sortable: true
};
// console.log(vm.schema.properties[key].title);
// console.log(vm.schema.properties[key].sortable);
if (vm.schema.properties[key].sortable === false) {
// console.log("Sortable should be false");
header.sortable = false;
}
if (vm.schema.properties[key].align) {
header['align'] = 'text-xs-' + vm.schema.properties[key].align;
}
if (vm.schema.properties[key].visible === false) {
header['visibility'] = 'hidden';
}
headers.push(header);
}
return headers;
}
private action(event: any, item: any) {
console.log('I am in action - SmileGrid');
this.$emit(event, item);
}
private changeSort(column: string) {
if (this.pagination.sortBy === column) {
this.pagination.descending = !this.pagination.descending;
} else {
this.pagination.sortBy = column;
this.pagination.descending = false;
}
}
private buildLink(link: string, item: any): string {
let contructedLink = '';
const pathList = link.split('/');
// console.log(pathList.array);
pathList.forEach(element => {
if (element.includes(':')) {
const queryParam = element.split(':')[1];
const queryValue = item[queryParam];
contructedLink = contructedLink + '/' + queryValue;
} else {
contructedLink = contructedLink + '/' + element;
}
});
return contructedLink;
}
private toggleAll() {
if (this.selected.length) {
this.selected = [];
} else {
this.selected = this.items.slice();
}
}
private getTitleCase(value: string) {
const tile = value.replace(/([a-z])([A-Z])/g, '$1 $2');
return tile[0].toUpperCase() + tile.slice(1);
}
@Watch('selected') private onSelectedChanged(value: any, oldValue: any) {
this.$emit('input', value);
}
@Watch ('value') private onValueChanged(value: any, oldValue: any) {
this.selected = value;
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.hidden {
display: none
}
.visible {
display: block
}
</style>
- Copy the decorator of SmileGrid
decorator SmileGrid {
disable_sortable {
"sortable" : false
}
align_right {
"align" : "right"
}
align_left {
"align" : "left"
}
action_icon {
"actionIcon": true // Provide icon and actionId
}
action_btn {
"actionBtn": true // Provide btnText and actionId
}
action_icon_delete {
"actionIcon": true,
"icon" : "delete",
"actionId": "delete" // in <SmileGrid add @delete="deleteAction" implement deleteAction as a method
}
action_icon_delete_dialog {
"actionIcon": true,
"icon" : "delete",
"actionId": "delete", // in <SmileGrid add @delete="deleteAction" implement deleteAction as a method
"dialog" : true
}
action_btn_delete {
"actionButton": true,
"btnText" : "delete",
"actionId": "delete" // in <SmileGrid add @delete="deleteAction" implement deleteAction as a method
}
action_icon_edit {
"actionIcon": true,
"icon" : "edit",
"actionId": "edit" // in <SmileGrid add @edit="editAction" implement editAction as a method
}
action_btn_edit {
"actionButton": true,
"btnText" : "edit",
"actionId": "edit" // in <SmileGrid add @edit="editAction" implement editAction as a method
}
action_expand {
"actionExpand": true // implement expandRow(row) as a method
}
}