diff --git a/core/package-lock.json b/core/package-lock.json index 23f05fa73e..de95ebcd10 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -17,7 +17,7 @@ "@rollup/plugin-virtual": "^2.0.3", "@stencil/core": "2.1.2", "@stencil/sass": "1.3.2", - "@stencil/vue-output-target": "0.2.5", + "@stencil/vue-output-target": "0.3.0", "@types/jest": "^26.0.10", "@types/node": "^14.6.0", "@types/puppeteer": "3.0.1", @@ -1657,9 +1657,9 @@ "dev": true }, "node_modules/@stencil/vue-output-target": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.2.5.tgz", - "integrity": "sha512-EnCZbJDD7y2nTnaFXo4PJrEgP9CNvJARZQrnvz93rMDKCzm+19C9goU4D6A1ysblLVvZSTm3wELKSM1auRUm0Q==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.3.0.tgz", + "integrity": "sha512-uiBe+o7M+NU0gMRgJfrlepxLPBXK0lX4TL2jIPwhBfxYw++pbtg7BLRO2HxE69GR0nxw+7Uf3uJzOGbMsl+ZUQ==", "dev": true, "peerDependencies": { "@stencil/core": ">=1.8.0" @@ -16390,9 +16390,9 @@ "dev": true }, "@stencil/vue-output-target": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.2.5.tgz", - "integrity": "sha512-EnCZbJDD7y2nTnaFXo4PJrEgP9CNvJARZQrnvz93rMDKCzm+19C9goU4D6A1ysblLVvZSTm3wELKSM1auRUm0Q==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.3.0.tgz", + "integrity": "sha512-uiBe+o7M+NU0gMRgJfrlepxLPBXK0lX4TL2jIPwhBfxYw++pbtg7BLRO2HxE69GR0nxw+7Uf3uJzOGbMsl+ZUQ==", "dev": true }, "@stylelint/postcss-css-in-js": { diff --git a/core/package.json b/core/package.json index 3df1d70887..49c6b80888 100644 --- a/core/package.json +++ b/core/package.json @@ -38,7 +38,7 @@ "@rollup/plugin-virtual": "^2.0.3", "@stencil/core": "2.1.2", "@stencil/sass": "1.3.2", - "@stencil/vue-output-target": "0.2.5", + "@stencil/vue-output-target": "0.3.0", "@types/jest": "^26.0.10", "@types/node": "^14.6.0", "@types/puppeteer": "3.0.1", diff --git a/core/stencil.config.ts b/core/stencil.config.ts index bd1db3cddd..d331a5d8ca 100644 --- a/core/stencil.config.ts +++ b/core/stencil.config.ts @@ -91,12 +91,14 @@ export const config: Config = { { elements: ['ion-checkbox', 'ion-toggle'], targetAttr: 'checked', - event: 'ionChange' + event: 'v-ionChange', + externalEvent: 'ionChange' }, { elements: ['ion-datetime', 'ion-input', 'ion-radio-group', 'ion-radio', 'ion-range', 'ion-searchbar', 'ion-segment', 'ion-segment-button', 'ion-select', 'ion-textarea'], targetAttr: 'value', - event: 'ionChange' + event: 'v-ionChange', + externalEvent: 'ionChange' } ], }), diff --git a/packages/vue/src/ionic-vue.ts b/packages/vue/src/ionic-vue.ts index 9878dbd674..0e166d86fe 100644 --- a/packages/vue/src/ionic-vue.ts +++ b/packages/vue/src/ionic-vue.ts @@ -2,8 +2,17 @@ import { App, Plugin } from 'vue'; import { IonicConfig, setupConfig } from '@ionic/core'; import { applyPolyfills, defineCustomElements } from '@ionic/core/loader'; -const ael = (el: any, eventName: string, cb: any, opts: any) => el.addEventListener(eventName.toLowerCase(), cb, opts); -const rel = (el: any, eventName: string, cb: any, opts: any) => el.removeEventListener(eventName.toLowerCase(), cb, opts); +/** + * We need to make sure that the web component fires an event + * that will not conflict with the user's @ionChange binding, + * otherwise the binding's callback will fire before any + * v-model values have been updated. + */ +const transformEventName = (eventName: string) => { + return eventName === 'ionChange' ? 'v-ionchange' : eventName.toLowerCase(); +} +const ael = (el: any, eventName: string, cb: any, opts: any) => el.addEventListener(transformEventName(eventName), cb, opts); +const rel = (el: any, eventName: string, cb: any, opts: any) => el.removeEventListener(transformEventName(eventName), cb, opts); export const IonicVue: Plugin = { @@ -17,7 +26,7 @@ export const IonicVue: Plugin = { await applyPolyfills(); await defineCustomElements(window, { exclude: ['ion-tabs'], - ce: (eventName: string, opts: any) => new CustomEvent(eventName.toLowerCase(), opts), + ce: (eventName: string, opts: any) => new CustomEvent(transformEventName(eventName), opts), ael, rel } as any); diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index 2b1e03b932..e8bda1f50a 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -97,7 +97,8 @@ export const IonCheckbox = /*@__PURE__*/ defineContainer('ion-c ], { "modelProp": "checked", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -180,7 +181,8 @@ export const IonDatetime = /*@__PURE__*/ defineContainer('ion-d ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -294,7 +296,8 @@ export const IonInput = /*@__PURE__*/ defineContainer('ion-input', ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -445,7 +448,8 @@ export const IonRadio = /*@__PURE__*/ defineContainer('ion-radio', ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -457,7 +461,8 @@ export const IonRadioGroup = /*@__PURE__*/ defineContainer('i ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -481,7 +486,8 @@ export const IonRange = /*@__PURE__*/ defineContainer('ion-range', ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -551,7 +557,8 @@ export const IonSearchbar = /*@__PURE__*/ defineContainer('ion ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -567,7 +574,8 @@ export const IonSegment = /*@__PURE__*/ defineContainer('ion-seg ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -579,7 +587,8 @@ export const IonSegmentButton = /*@__PURE__*/ defineContainer('ion-selec ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -695,7 +705,8 @@ export const IonTextarea = /*@__PURE__*/ defineContainer('ion-t ], { "modelProp": "value", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); @@ -722,7 +733,8 @@ export const IonToggle = /*@__PURE__*/ defineContainer('ion-toggl ], { "modelProp": "checked", - "modelUpdateEvent": "ionChange" + "modelUpdateEvent": "v-ionChange", + "externalModelUpdateEvent": "ionChange" }); diff --git a/packages/vue/src/vue-component-lib/utils.ts b/packages/vue/src/vue-component-lib/utils.ts index 46a512d02f..e9a994c9a4 100644 --- a/packages/vue/src/vue-component-lib/utils.ts +++ b/packages/vue/src/vue-component-lib/utils.ts @@ -17,6 +17,7 @@ interface NavManager { interface ComponentOptions { modelProp?: string; modelUpdateEvent?: string; + externalModelUpdateEvent?: string; } const getComponentClasses = (classes: unknown) => { @@ -40,7 +41,7 @@ const getElementClasses = (ref: Ref, componentClasses: * integrations. */ export const defineContainer = (name: string, componentProps: string[] = [], componentOptions: ComponentOptions = {}) => { - const { modelProp, modelUpdateEvent } = componentOptions; + const { modelProp, modelUpdateEvent, externalModelUpdateEvent } = componentOptions; /** * Create a Vue component wrapper around a Web Component. @@ -66,8 +67,7 @@ export const defineContainer = (name: string, componentProps: string[] = * native web component, but the v-model will * not have been updated yet. */ - emit(modelUpdateEvent, e); - e.stopImmediatePropagation(); + emit(externalModelUpdateEvent, e); }); } }; @@ -112,7 +112,7 @@ export const defineContainer = (name: string, componentProps: string[] = ref: containerRef, class: getElementClasses(containerRef, classes), onClick: handleClick, - onVnodeBeforeMount: (modelUpdateEvent) ? onVnodeBeforeMount : undefined + onVnodeBeforeMount: (modelUpdateEvent && externalModelUpdateEvent) ? onVnodeBeforeMount : undefined }; if (modelProp) { @@ -130,7 +130,7 @@ export const defineContainer = (name: string, componentProps: string[] = Container.props = [...componentProps, ROUTER_LINK_VALUE]; if (modelProp) { Container.props.push(MODEL_VALUE); - Container.emits = [UPDATE_VALUE_EVENT, modelUpdateEvent]; + Container.emits = [UPDATE_VALUE_EVENT, externalModelUpdateEvent]; } return Container;