3838 </ion-item >
3939
4040 <ion-item >
41- <ion-input v-model =" input" label =" Input" ></ion-input >
41+ <ion-input v-model =" input" label =" Input" required @ionBlur = " handleValidation " @ionInput = " handleValidation " ></ion-input >
4242 </ion-item >
4343
4444 <ion-item >
45- <ion-input-otp v-model =" inputOtp" ></ion-input-otp >
45+ <ion-input-otp v-model =" inputOtp" required @ionBlur = " handleValidation " @ionInput = " handleValidation " ></ion-input-otp >
4646 </ion-item >
4747
4848 <ion-item >
4949 <ion-range label =" Range" :dual-knobs =" true" :min =" 0" :max =" 100" slot =" end" v-model =" range" ></ion-range >
5050 </ion-item >
5151
5252 <ion-item >
53- <ion-textarea label =" Textarea" v-model =" textarea" ></ion-textarea >
53+ <ion-textarea label =" Textarea" v-model =" textarea" required @ionBlur = " handleValidation " @ionInput = " handleValidation " ></ion-textarea >
5454 </ion-item >
5555
5656 <ion-item >
9999 </ion-page >
100100</template >
101101
102- <script lang="ts">
102+ <script setup lang="ts">
103103import {
104104 IonBackButton ,
105105 IonButton ,
@@ -126,101 +126,105 @@ import {
126126 IonToggle ,
127127 IonToolbar
128128} from ' @ionic/vue' ;
129- import { defineComponent , ref } from ' vue' ;
130-
131- export default defineComponent ({
132- components: {
133- IonBackButton ,
134- IonButton ,
135- IonButtons ,
136- IonCheckbox ,
137- IonContent ,
138- IonDatetime ,
139- IonHeader ,
140- IonInput ,
141- IonInputOtp ,
142- IonItem ,
143- IonLabel ,
144- IonPage ,
145- IonRadio ,
146- IonRadioGroup ,
147- IonRange ,
148- IonSearchbar ,
149- IonSegment ,
150- IonSegmentButton ,
151- IonSelect ,
152- IonSelectOption ,
153- IonTextarea ,
154- IonTitle ,
155- IonToggle ,
156- IonToolbar
157- },
158- setup() {
159- const checkbox = ref (false );
160- const toggle = ref (false );
161- const input = ref (' ' );
162- const inputOtp = ref (' ' );
163- const range = ref ({
164- lower: 30 ,
165- upper: 70
166- });
167- const textarea = ref (' ' );
168- const searchbar = ref (' ' );
169- const datetime = ref (' ' );
170- const radio = ref (' red' );
171- const segment = ref (' dogs' );
172- const select = ref (' apples' );
173-
174- const reset = () => {
175- checkbox .value = false ;
176- toggle .value = false ;
177- input .value = ' ' ;
178- inputOtp .value = ' ' ;
179- range .value = {
180- lower: 30 ,
181- upper: 70
182- };
183- textarea .value = ' ' ;
184- searchbar .value = ' ' ;
185- datetime .value = ' ' ;
186- radio .value = ' red' ;
187- segment .value = ' dogs' ;
188- select .value = ' apples' ;
129+ import { ref } from ' vue' ;
130+
131+ const checkbox = ref (false );
132+ const toggle = ref (false );
133+ const input = ref (' ' );
134+ const inputOtp = ref (' ' );
135+ const range = ref ({
136+ lower: 30 ,
137+ upper: 70
138+ });
139+ const textarea = ref (' ' );
140+ const searchbar = ref (' ' );
141+ const datetime = ref (' ' );
142+ const radio = ref (' red' );
143+ const segment = ref (' dogs' );
144+ const select = ref (' apples' );
145+
146+ const reset = () => {
147+ checkbox .value = false ;
148+ toggle .value = false ;
149+ input .value = ' ' ;
150+ inputOtp .value = ' ' ;
151+ range .value = {
152+ lower: 30 ,
153+ upper: 70
154+ };
155+ textarea .value = ' ' ;
156+ searchbar .value = ' ' ;
157+ datetime .value = ' ' ;
158+ radio .value = ' red' ;
159+ segment .value = ' dogs' ;
160+ select .value = ' apples' ;
161+ }
162+
163+ const set = () => {
164+ checkbox .value = true ;
165+ toggle .value = true ;
166+ input .value = ' Hello World' ;
167+ inputOtp .value = ' 1234' ;
168+ range .value = {
169+ lower: 10 ,
170+ upper: 90
171+ }
172+ textarea .value = ' Lorem Ipsum' ;
173+ searchbar .value = ' Search Query' ;
174+ datetime .value = ' 2019-01-31' ;
175+ radio .value = ' blue' ;
176+ segment .value = ' cats' ;
177+ select .value = ' bananas' ;
178+ }
179+
180+ const setIonicClasses = (element : HTMLElement , isBlurEvent : boolean ) => {
181+ requestAnimationFrame (() => {
182+ let isValid = false ;
183+
184+ // Handle ion-input-otp which has multiple inputs
185+ if (element .tagName === ' ION-INPUT-OTP' ) {
186+ const ionInputOtp = element as any ;
187+ const value = ionInputOtp .value || ' ' ;
188+ const length = ionInputOtp .length || 4 ;
189+ // input-otp needs to check if all inputs are filled
190+ // (value length equals component length)
191+ isValid = value .length === length ;
192+ // Handle ion-textarea which uses shadow DOM
193+ } else if (element .tagName === ' ION-TEXTAREA' ) {
194+ const nativeTextarea = element .shadowRoot ?.querySelector (' textarea' ) as HTMLTextAreaElement | null ;
195+ if (nativeTextarea ) {
196+ isValid = nativeTextarea .checkValidity ();
197+ }
198+ // Handle ion-input which uses scoped encapsulation
199+ } else if (element .tagName === ' ION-INPUT' ) {
200+ const nativeInput = element .querySelector (' input' ) as HTMLInputElement | null ;
201+ if (nativeInput ) {
202+ isValid = nativeInput .checkValidity ();
203+ }
189204 }
190205
191- const set = () => {
192- checkbox .value = true ;
193- toggle .value = true ;
194- input .value = ' Hello World' ;
195- inputOtp .value = ' 1234' ;
196- range .value = {
197- lower: 10 ,
198- upper: 90
199- }
200- textarea .value = ' Lorem Ipsum' ;
201- searchbar .value = ' Search Query' ;
202- datetime .value = ' 2019-01-31' ;
203- radio .value = ' blue' ;
204- segment .value = ' cats' ;
205- select .value = ' bananas' ;
206+ // Remove validation classes
207+ element .classList .remove (' ion-valid' , ' ion-invalid' , ' ion-untouched' );
208+
209+ // Mark as touched only on blur
210+ if (isBlurEvent ) {
211+ element .classList .add (' ion-touched' );
206212 }
207213
208- return {
209- checkbox ,
210- toggle ,
211- input ,
212- inputOtp ,
213- range ,
214- textarea ,
215- searchbar ,
216- datetime ,
217- radio ,
218- segment ,
219- select ,
220-
221- reset ,
222- set
214+ // Add validation classes based on validity state
215+ if (isValid ) {
216+ element .classList .add (' ion-valid' );
217+ } else {
218+ element .classList .add (' ion-invalid' );
223219 }
224- }
225- });
220+ });
221+ };
222+
223+ const handleValidation = (event : CustomEvent ) => {
224+ const element = event .target as HTMLElement ;
225+ if (! element ) return ;
226+
227+ const isBlurEvent = event .type === ' ionBlur' ;
228+ setIonicClasses (element , isBlurEvent );
229+ };
226230 </script >
0 commit comments