diff --git a/core/src/components/input/usage/vue.md b/core/src/components/input/usage/vue.md index 9be8fc34976..87766566954 100644 --- a/core/src/components/input/usage/vue.md +++ b/core/src/components/input/usage/vue.md @@ -1,45 +1,72 @@ ```html + + ``` \ No newline at end of file diff --git a/vue/src/components/inputs.ts b/vue/src/components/inputs.ts index 2dcdb7ac25c..eec71ea5969 100644 --- a/vue/src/components/inputs.ts +++ b/vue/src/components/inputs.ts @@ -1,57 +1,81 @@ -import Vue from 'vue'; +import Vue, { CreateElement, RenderContext } from 'vue'; + +interface EventHandler { + [key: string]: (e: Event) => void; +} + +// Events to register handlers for +const events: string[] = ['ionChange', 'ionInput', 'ionBlur', 'ionFocus', 'ionCancel', 'ionSelect']; + +// Arguments to be passed to the factory function +const inputComponentsToBeCreated = [ + ['IonCheckboxVue', 'ion-checkbox', 'ionChange', 'checked'], + ['IonDatetimeVue', 'ion-datetime'], + ['IonInputVue', 'ion-input', 'ionInput'], + ['IonRadioVue', 'ion-radio', 'ionSelect'], + ['IonRangeVue', 'ion-range'], + ['IonSearchbarVue', 'ion-searchbar', 'ionInput'], + ['IonSelectVue', 'ion-select'], + ['IonTextareaVue', 'ion-textarea'], + ['IonToggleVue', 'ion-toggle', 'ionChange', 'checked'], +]; + +// Factory function for creation of input components +export function createInputComponents() { + inputComponentsToBeCreated.map(args => (createInputComponent as any)(...args)); +} /** * Create a wrapped input component that captures typical ionic input events * and emits core ones so v-model works. - * @param {} name the vue name of the component - * @param {*} coreTag the actual tag to render (such as ion-datetime) + * @param name the vue name of the component + * @param coreTag the actual tag to render (such as ion-datetime) + * @param modelEvent to be used for v-model + * @param valueProperty to be used for v-model */ -export function createInputComponent(name: string, coreTag: string, modelEvent = 'ionChange', valueProperty = 'value') { - return Vue.component(name, { +function createInputComponent(name: string, coreTag: string, modelEvent = 'ionChange', valueProperty = 'value') { + Vue.component(name, { + name, + functional: true, model: { event: modelEvent, - prop: valueProperty + prop: valueProperty, }, - render(createElement: any) { - // Vue types have a bug accessing member properties: - // https://github.com/vuejs/vue/issues/8721 - const cmp: any = this; - - return createElement(coreTag, { - 'attrs': cmp.attrs, - 'on': { - 'ionChange': cmp.handleChange, - 'ionInput': cmp.handleInput, - 'ionBlur': cmp.handleBlur, - 'ionFocus': cmp.handleFocus - } - }, this.$slots.default); + render(h: CreateElement, { data, listeners, slots }: RenderContext) { + return h(coreTag, { + ...data, + on: buildEventHandlers(listeners, modelEvent, valueProperty), + }, slots().default); }, - methods: { - handleChange($event: any) { - if (modelEvent === 'ionChange') { - // Vue expects the value to be sent as the argument for v-model, not the - // actual event object - this.$emit('ionChange', $event.target[valueProperty]); - } else { - this.$emit('ionChange', $event); - } - }, - handleInput($event: any) { - if (modelEvent === 'ionInput') { - // Vue expects the value to be sent as the argument for v-model, not the - // actual event object - this.$emit('ionInput', $event.target[valueProperty]); - } else { - this.$emit('ionInput', $event); - } - }, - handleBlur($event: any) { - this.$emit('ionBlur', $event); - }, - handleFocus($event: any) { - this.$emit('ionFocus', $event); - } + }); +} + +function buildEventHandlers(listeners: RenderContext['listeners'], modelEvent: string, valueProperty: string) { + const handlers: EventHandler = {}; + + // Loop through all the events + events.map((eventName: string) => { + if (!listeners[eventName]) { + return; } + + // Normalize listeners coming from context as Function | Function[] + const callbacks: Function[] = Array.isArray(listeners[eventName]) + ? listeners[eventName] as Function[] + : [listeners[eventName] as Function]; + + // Assign handlers + handlers[eventName] = (e: Event) => { + callbacks.map((f: Function) => { + if (e) { + f(modelEvent === eventName + ? (e.target as any)[valueProperty] + : e + ); + } + }); + }; }); + + return handlers; } diff --git a/vue/src/ionic.ts b/vue/src/ionic.ts index d83929d3aba..24a2dce0ea7 100644 --- a/vue/src/ionic.ts +++ b/vue/src/ionic.ts @@ -13,7 +13,7 @@ import { appInitialize } from './app-initialize'; import { VueDelegate } from './controllers/vue-delegate'; import IonTabs from './components/navigation/ion-tabs'; import IonPage from './components/navigation/ion-page'; -import { createInputComponent } from './components/inputs'; +import { createInputComponents } from './components/inputs'; export interface Controllers { actionSheetController: ActionSheetController; @@ -98,15 +98,7 @@ export const install: PluginFunction = (_Vue, config) => { Vue.component('IonTabs', IonTabs); Vue.component('IonPage', IonPage); - createInputComponent('IonCheckboxVue', 'ion-checkbox', 'ionChange', 'checked'); - createInputComponent('IonDatetimeVue', 'ion-datetime'); - createInputComponent('IonInputVue', 'ion-input', 'ionInput'); - createInputComponent('IonRadioVue', 'ion-radio'); - createInputComponent('IonRangeVue', 'ion-range'); - createInputComponent('IonSearchbarVue', 'ion-searchbar', 'ionInput'); - createInputComponent('IonSelectVue', 'ion-select'); - createInputComponent('IonTextareaVue', 'ion-textarea'); - createInputComponent('IonToggleVue', 'ion-toggle', 'ionChange', 'checked'); + createInputComponents(); appInitialize(config);