Ionic 5/Angular: Laggy <ion-input> with "Added non-passive event listener to a scroll-blocking 'touchstart' event" warning on iOS only - angular8

I've started getting non-passive event listener warning on a form used in an ionic project. I've reproduced it using a bare-bones ionic tabs project and have the following versions of ionic, angular and capacitor installed:
Ionic:
Ionic CLI : 6.4.0 (/usr/local/lib/node_modules/#ionic/cli).
Ionic Framework : #ionic/angular 5.0.7.
#angular-devkit/build-angular : 0.803.26.
#angular-devkit/schematics : 8.3.26.
#angular/cli : 8.3.26.
#ionic/angular-toolkit : 2.2.0.
Capacitor:
Capacitor CLI : 2.0.1.
#capacitor/core : 2.0.1.
In Google Chrome, this only appears when I set the device to 'iOS'. There is a noticeable lag between tapping on an ion-input field and the field being available to enter data. This persists when I compile the code and package it as a iOS app. Setting the device as an android device in Google Chrome and compiling as an Android app is unaffected.
To reproduce this issue, create a new ionic tabs app and replace the tab1 files with the following:
tab1.module.ts
import { RouterModule } from '#angular/router';
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { ReactiveFormsModule } from '#angular/forms';
import { Tab1Page } from './tab1.page';
import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
#NgModule({
imports: [
IonicModule,
CommonModule,
ReactiveFormsModule,
ExploreContainerComponentModule,
RouterModule.forChild([{ path: '', component: Tab1Page }])
],
declarations: [Tab1Page]
})
export class Tab1PageModule {}
tab1.page.html
<ion-toolbar color="primary">
<ion-title>
Calculate Score
</ion-title>
<ion-buttons slot="start">
<ion-back-button defaultHref="/tabs/tab2"></ion-back-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<form [formGroup]="batsForm" (ngSubmit)="submitForm()" novalidate>
<!-- Inputs with labels -->
<ion-item>
<ion-label position="fixed">Age </ion-label>
<ion-input formControlName="age" type="number" pattern="[0-9]*" placeholder="Enter age"></ion-input>
<ion-select formControlName="period" value="years" okText="Select" cancelText="Cancel">
<ion-select-option value="years">Years</ion-select-option>
<ion-select-option value="months">Months</ion-select-option>
</ion-select>
<!-- <ion-input (click)="showPicker()" value="{{ age }} {{ period }}"></ion-input> -->
</ion-item>
<ion-item *ngIf="batsForm.get('age').dirty && errorControl.age.invalid">
<ion-text color="danger" *ngIf="batsForm.get('age').dirty && errorControl.age.errors?.min">
Age must be greater than 0
</ion-text>
<ion-text color="danger" *ngIf="batsForm.get('age').dirty && errorControl.age.errors?.max">
Age must be less than 116
</ion-text>
<ion-text color="danger"*ngIf="batsForm.get('age').dirty && errorControl.age.errors?.required">
An age value is required
</ion-text>
</ion-item>
<ion-radio-group formControlName="penetrating" >
<ion-list-header>
<ion-label>Penetrating injury</ion-label>
</ion-list-header>
<ion-item>
<ion-label>Yes</ion-label>
<ion-radio slot="start" value="yes"></ion-radio>
</ion-item>
<ion-item>
<ion-label>No</ion-label>
<ion-radio slot="start" value="no" checked></ion-radio>
</ion-item>
</ion-radio-group>
<ion-radio-group formControlName="velocity" >
<ion-list-header>
<ion-label>High velocity trauma</ion-label>
</ion-list-header>
<ion-item>
<ion-label>Yes</ion-label>
<ion-radio slot="start" value="yes"></ion-radio>
</ion-item>
<ion-item>
<ion-label>No</ion-label>
<ion-radio slot="start" value="no" checked></ion-radio>
</ion-item>
</ion-radio-group>
<ion-item>
<ion-label position="fixed">Systolic BP</ion-label>
<ion-input formControlName="bp" type="number" pattern="[0-9]*" min="0" max="240" required></ion-input>
</ion-item>
<ion-item *ngIf="batsForm.get('bp').dirty && errorControl.bp.invalid">
<ion-text color="danger" *ngIf="batsForm.get('bp').dirty && errorControl.bp.errors?.min">
BP must be greater than 0mmHg
</ion-text>
<ion-text color="danger" *ngIf="batsForm.get('bp').dirty && errorControl.bp.errors?.max">
BP must be less than 240mmHg
</ion-text>
<ion-text color="danger"*ngIf="batsForm.get('bp').dirty && errorControl.bp.errors?.required">
Blood pressure value is required
</ion-text>
</ion-item>
<ion-item>
<ion-label position="fixed">GCS</ion-label>
<ion-input formControlName="gcs" type="number" pattern="[0-9]*" min="3" max="15" required></ion-input>
</ion-item>
<ion-item *ngIf="batsForm.get('gcs').dirty && errorControl.gcs.invalid">
<ion-text color="danger" *ngIf="batsForm.get('gcs').dirty && errorControl.gcs.errors?.min">
GCS must be 3 or more
</ion-text>
<ion-text color="danger" *ngIf="batsForm.get('gcs').dirty && errorControl.gcs.errors?.max">
GCS must be 15 or less
</ion-text>
<ion-text color="danger"*ngIf="batsForm.get('gcs').dirty && errorControl.gcs.errors?.required">
A GCS is required
</ion-text>
</ion-item>
<ion-item>
<ion-label position="fixed">Resp Rate</ion-label>
<ion-input formControlName="rr" type="number" pattern="[0-9]*" min="0" max="60" required></ion-input>
</ion-item>
<ion-item *ngIf="batsForm.get('rr').dirty && errorControl.rr.invalid">
<ion-text color="danger" *ngIf="batsForm.get('rr').dirty && errorControl.rr.errors?.min">
Resp rate must be greater than 0
</ion-text>
<ion-text color="danger" *ngIf="batsForm.get('rr').dirty && errorControl.rr.errors?.max">
Resp rate must be less than 60
</ion-text>
<ion-text color="danger"*ngIf="batsForm.get('rr').dirty && errorControl.rr.errors?.required">
A respiratory rate is required
</ion-text>
</ion-item>
<ion-item>
<ion-label position="fixed">SpO2 on air</ion-label>
<ion-input formControlName="spo2" type="number" pattern="[0-9]*" min="50" max="100" required></ion-input>
</ion-item>
<ion-item *ngIf="batsForm.get('spo2').dirty && errorControl.spo2.invalid">
<ion-text color="danger" *ngIf="batsForm.get('spo2').dirty && errorControl.spo2.errors?.min">
SpO2 be greater than 50%
</ion-text>
<ion-text color="danger" *ngIf="batsForm.get('spo2').dirty && errorControl.spo2.errors?.max">
SpO2 must be 100% or less
</ion-text>
<ion-text color="danger"*ngIf="batsForm.get('spo2').dirty && errorControl.spo2.errors?.required">
SpO2 is required when a respiratory rate is not provided
</ion-text>
</ion-item>
<ion-item>
<ion-label position="fixed">Heart Rate </ion-label>
<ion-input formControlName="hr" type="number" pattern="[0-9]*" min="0" max="180" id="hr" required></ion-input>
</ion-item>
<ion-item *ngIf="batsForm.get('hr').dirty && errorControl.hr.invalid">
<ion-text color="danger" *ngIf="batsForm.get('hr').dirty && errorControl.hr.errors?.min">
Heart rate must be greater than 0
</ion-text>
<ion-text color="danger" *ngIf="batsForm.get('hr').dirty && errorControl.hr.errors?.max">
Heart rate must be less than 180
</ion-text>
<ion-text color="danger"*ngIf="batsForm.get('hr').dirty && errorControl.hr.errors?.required">
Heart rate value is required
</ion-text>
</ion-item>
<ion-item>
<ion-label position="fixed">Callsign</ion-label>
<ion-input formControlName="callsign" type="number" pattern="[0-9]*" required placeholder="Enter vehicle callsign"></ion-input>
</ion-item>
<ion-item *ngIf="batsForm.get('callsign').dirty && errorControl.callsign.invalid">
<ion-text color="danger"*ngIf="batsForm.get('callsign').dirty && errorControl.callsign.errors?.required">
Your vehicle callsign is required
</ion-text>
</ion-item>
<div class="ion-padding">
<ion-button expand="full" type="submit" class="ion-no-margin" [disabled]="!batsForm.valid" >Calculate Score</ion-button>
</div>
</form>
</ion-content>
tab1.page.ts
import { LoadingController } from '#ionic/angular';
import { NetworkStatus } from '#capacitor/core';
import { Plugins } from '#capacitor/core';
import { FormGroup, FormBuilder, Validators } from '#angular/forms';
import { distinctUntilChanged } from 'rxjs/operators';
import { Router } from '#angular/router';
const { Network } = Plugins;
#Component({
selector: 'app-tab1',
templateUrl: 'tab1.page.html',
styleUrls: ['tab1.page.scss']
})
export class Tab1Page implements OnInit {
status: NetworkStatus;
batsForm: FormGroup;
thenumbers = new Array(100);
public show = true;
public spinner = false;
public loading = null;
public isSubmitted = false;
public startGCS = 15;
public nextID = 1;
public age: any;
public period: any;
constructor(
private loadingCtrl: LoadingController,
public formBuilder: FormBuilder,
private router: Router
) { }
ngOnInit() {
this.batsForm = this.formBuilder.group({
bp : ['', [Validators.required, Validators.min(0), Validators.max(240)]],
gcs : ['', [Validators.required, Validators.min(3), Validators.max(15)]],
rr : ['', [Validators.required, Validators.min(0), Validators.max(60)]],
spo2 : ['', [Validators.required, Validators.min(50), Validators.max(100)]],
hr : ['', [Validators.required, Validators.min(0), Validators.max(180)]],
age : ['', [Validators.required, Validators.min(0), Validators.max(115)]],
period : ['', [Validators.required]],
penetrating : ['', [Validators.required]],
velocity : ['', [Validators.required]],
callsign : ['', [Validators.required]]
});
this.batsForm.controls.period.setValue('years');
this.formControlValueChanged();
}
ionViewDidEnter() {
this.getStatus();
this.nextID = 1;
}
async getStatus() {
try {
this.status = await Network.getStatus();
console.log(this.status);
} catch (e) {
console.log('Error', e);
}
}
get errorControl() {
return this.batsForm.controls;
}
// tslint:disable-next-line: max-line-length
// https://www.infragistics.com/community/blogs/b/infragistics/posts/how-to-do-conditional-validation-on-valuechanges-method-in-angular-reactive-forms-
// https://stackoverflow.com/questions/47821809/rangeerror-maximum-call-stack-size-exceeded-when-using-valuechanges-subscribe
formControlValueChanged() {
const spo2Control = this.batsForm.get('spo2');
const rrControl = this.batsForm.get('rr');
this.batsForm.get('rr').valueChanges.pipe(distinctUntilChanged()).subscribe(
(rr: number) => {
console.log(rr);
if ( rr !== null && ( rr > 0 || rr < 50 )) {
spo2Control.setValidators([Validators.min(50), Validators.max(100)]);
} else {
spo2Control.setValidators([Validators.required, Validators.min(50), Validators.max(100)]);
}
spo2Control.updateValueAndValidity();
});
this.batsForm.get('spo2').valueChanges.pipe(distinctUntilChanged()).subscribe(
(spo2: number) => {
console.log(spo2);
if (spo2 !== null && ( spo2 > 50 || spo2 < 101 )) {
rrControl.setValidators([Validators.min(0), Validators.max(60)]);
} else {
rrControl.setValidators([Validators.required, Validators.min(0), Validators.max(60)]);
}
rrControl.updateValueAndValidity();
});
}
submitForm() {
console.log('Form submitted');
console.log(this.batsForm.value);
}
}

Just in case anyone else has this issue, reverting to #ionic/angular 5.0.5 fixes the lag issue, although not the passive listener version warning.

Related

GoogleMap Set longitude and latitude after request

I Developer this Template and put its code below. When I type a phrase, Google offers me an address, and when I click on it, the map zooms automatically and displays the marker, as in the picture below:
In the image below, the fields (longitude and latitude) are automatically set only when the map is clicked, but I need the values ​​of the fields (longitude) when I search for the address at the same time (as I explained above) and latitude) are updated and the user does not need to necessarily click on the map.
I just started working with vuejs. Please guide me by editing my code. Thanks
<template>
<div no-body class="mb-1 w-100">
<div class="body-bg shadow-none" id="test2">
<div
:class="{
'is-invalid':
$parent.form.errors.has('auto_complete_map') ||
$parent.form.errors.has('location'),
}"
class="d-flex flex-row justify-content-center border border-1 rounded border-dark"
>
<h4 block v-b-toggle.accordion-2 variant="info">Location</h4>
</div>
</div>
<b-collapse
class="navbar-fixed-top"
id="accordion-2"
accordion="my-accordion"
>
<div class="d-flex flex-row gap-2 justify-content-between mt-2">
<div class="col-sm-6">
<label for="latitude" class="form-label"
>{{ $t("latitude")
}}<span class="small text-danger m-1">*</span></label
>
<input
id="latitude"
:value="latitude"
class="form-control bg-transparent"
name="latitude"
placeholder=""
spellcheck="false"
data-ms-editor="true"
:disabled="true"
/>
<has-error :form="form" field="latitude" />
</div>
<div class="col-sm-6">
<label for="longitude" class="form-label">{{
$t("longitude")
}}</label>
<input
id="longitude"
class="form-control bg-transparent"
:value="longitude"
name="longitude"
placeholder=""
spellcheck="false"
data-ms-editor="true"
:disabled="true"
/>
<has-error :form="form" field="longitude" />
</div>
</div>
<label class="form-label mt-2"
>{{ $t("location") }}<span class="small text-danger">*</span></label
>
<GmapAutocomplete
id="auto_complete_map"
ref="location"
v-validate="'required|min:5'"
class="form-control"
name="location"
:value="location"
:placeholder="$t('enter a location')"
aria-required="true"
:disabled="$parent.canEditProperty()"
#place_changed="setPlace"
#keydown.enter.prevent
/>
<has-error :form="form" field="auto_complete_map" />
<GmapMap
ref="map"
class="my-3"
:center="center"
:zoom="zoom"
style="width: 100%; height: 300px"
#click="clickMap"
>
<GmapMarker
v-for="(m, index) in markers"
:key="index"
:position="m.position"
#click="center = m.position"
/>
</GmapMap>
<!-- <div class="d-flex flex-row justify-content-end m-2">
<b-button class="rounded-lg px-4 text-lg" block v-b-toggle.accordion-3 variant="info">next</b-button>
</div> -->
<div class="d-flex flex-row justify-content-end gap-3 m-2 mt-3">
<b-button
class="rounded-lg px-4 col-sm-2 text-white text-lg border border-dark"
block
v-b-toggle.accordion-1
variant="dark"
>previous</b-button
>
<b-button
class="rounded-lg px-6 col-sm-2 text-lg border border-dark"
block
v-b-toggle.accordion-3
variant="light"
>Next Step</b-button
>
</div>
</b-collapse>
</div>
</template>
<script>
import Autocomplete from "vue2-google-maps/dist/components/autocomplete";
export default {
name: "GoogleMap",
props: {
location: { type: String, default: "" },
latitude: { type: String, default: "" },
longitude: { type: String, default: "" },
},
data() {
return {
center: {
lat: 45.508,
lng: -73.587,
},
currentPlace: null,
markers: [],
places: [],
zoom: 12,
};
},
mounted() {
this.geolocate();
},
methods: {
setPlace(place) {
this.markers.splice(0, this.markers.length);
this.places.splice(0, this.places.length);
this.currentPlace = place;
this.updateLocation(place.formatted_address);
this.addMarker();
},
addMarker() {
if (this.currentPlace) {
const marker = {
lat: this.currentPlace.geometry.location.lat(),
lng: this.currentPlace.geometry.location.lng(),
};
this.markers.push({ position: marker });
this.places.push(this.currentPlace);
this.center = marker;
this.zoom = 17;
this.currentPlace = null;
}
},
clickMap(location) {
const marker = location.latLng;
// this.markers.clear()
this.markers.splice(0, this.markers.length);
this.places.splice(0, this.places.length);
this.markers.push({ position: marker });
// this.places.push(this.currentPlace)
// this.center = marker
// this.zoom = 17
this.currentPlace = null;
const geocoder = new google.maps.Geocoder();
geocoder
.geocode({ location: location.latLng })
.then((response) => {
if (response.results[0]) {
this.updateLocation(response.results[0].formatted_address);
this.getLoc(location.latLng);
} else {
window.alert("No results found");
}
})
.catch((e) => window.alert("Geocoder failed due to: " + e));
},
geolocate: function () {
const oldLocation = this.location;
if (oldLocation.length > 0) {
const _this = this;
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: oldLocation }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
// const lat = results[0].geometry.location.lat()
// const lng = results[0].geometry.location.lng()
// const placeName = results[0].address_components[0].long_name
if (results.length > 0) {
_this.setPlace(results[0]);
}
}
});
} else {
navigator.geolocation.getCurrentPosition((position) => {
this.center = {
lat: position.coords.latitude,
lng: position.coords.longitude,
};
});
}
},
updateLocation: function (newLocation) {
this.location = newLocation;
this.$emit("eventname", newLocation);
},
getLoc: function (location) {
// this.location = this.markers
this.longitude = location.lng();
this.latitude = location.lat();
this.$emit("getlog", location);
},
},
};
</script>
the passed in variable place in setPlace(place) has properties .geometry.location.lag() and .geometry.location.lng(). These are your latitude and longitude values for the entered address. Use these to set the latitude and longitude variables used by your <input> elements
setPlace(place) {
this.markers.splice(0, this.markers.length);
this.places.splice(0, this.places.length);
this.currentPlace = place;
this.updateLocation(place.formatted_address);
this.addMarker();
this.latitude = place.geometry.location.lat();
this.longitude = place.geometry.location.lng();
},
I do also want to point out that you've created latitude and longitude as props, which aren't meant to be mutated. If you don't have a reason to have them as props put them as data variables instead

VueJs - Conditionaly showing elements in table

I have pretty simple table of users here with only 4 cols, and i want to show a button for each user depending on his status 'isActive'. If user is active i want to show button with text 'disable' and vice versa. I am little bit stuck with this because i dont have an idea how can i show these buttons, because i am using vuexy template for this project(admin panel). Is there a way to do this with JSX?
Please take a look at code, i am getting data from mysql with nodejs. Ask me if you need more info. Thanks.
<template>
<div>
<div class="container">
<b-card-text class="mb-2">
<div
v-if="showLoginError"
class="text-center bg-danger colors-container rounded text-white width-360 height-50 d-flex align-items-center justify-content-center mr-1 ml-50 my-1 shadow"
>
<span>{{ loginError }}</span>
</div>
</b-card-text>
<b-card-text class="mb-2">
<div
v-if="showSuccessMessage"
class="text-center bg-success colors-container rounded text-white width-360 height-50 d-flex align-items-center justify-content-center mr-1 ml-50 my-1 shadow"
>
<span>{{ successMessage }}</span>
</div>
</b-card-text>
<section id="card-actions" class="input-section">
<b-row>
<b-col cols="8">
<b-card-actions ref="cardAction">
<validation-observer ref="simpleRules">
<b-form>
<b-row>
<b-col md="6">
<b-form-group>
<validation-provider
#default="{ errors }"
name="First Name"
rules="required"
>
<b-form-input
v-model="name"
:state="errors.length > 0 ? false:null"
placeholder="Twitter username"
/>
</validation-provider>
</b-form-group>
</b-col>
<b-col cols="12">
<b-button
variant="primary"
type="submit"
#click.prevent="validationForm"
>
Submit
</b-button>
</b-col>
</b-row>
</b-form>
</validation-observer>
</b-card-actions>
</b-col>
</b-row>
</section>
// This is table
<b-table responsive="sm" :items="items"/>
</div>
</div>
</template>
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate'
import {
BFormInput, BFormGroup, BForm, BRow, BCol, BButton, BTable,
} from 'bootstrap-vue'
import { required } from '#validations'
import axios from 'axios'
import { getUserToken } from '#/auth/auth'
export default {
components: {
ValidationProvider,
ValidationObserver,
BFormInput,
BFormGroup,
BForm,
BRow,
BCol,
BButton,
BTable,
},
data() {
return {
name: '',
successMessage: '',
showSuccessMessage: false,
loginError: '',
showLoginError: false,
required,
items: [],
}
},
beforeMount() {
this.getAllUsers()
},
methods: {
getAllUsers() {
const API_URL = `${this.$server}/api/twitter/allusers`
const params = {
token: getUserToken(),
}
axios.post(API_URL, null, { params }).then(res => {
if (res.data) {
res.data.forEach(element => {
let isActive = 'active'
if (element.isActive === 0) {
isActive = 'disabled'
}
const arr = {
twitter_name: element.twitter_name,
twitter_username: element.twitter_username,
twitter_id: element.twitter_id,
userActive: isActive,
}
this.items.push(arr)
})
}
})
},
validationForm() {
const API_URL = `${this.$server}/api/twitter/adduser`
const params = {
twitter_username: this.name,
token: getUserToken(),
}
axios.post(API_URL, null, { params }).then(res => {
if (res.data.success) {
this.successMessage = res.data.message
this.showSuccessMessage = true
// Hide message after 5sec
setTimeout(() => {
this.successMessage = ''
this.showSuccessMessage = false
}, 5000)
} else {
this.loginError = res.data.message
this.showLoginError = true
// Hide message after 5sec
setTimeout(() => {
this.loginError = ''
this.showLoginError = false
}, 5000)
}
})
},
},
}
</script>
I'm a little bit confused, where do you want to show your button ?
If it's in the table, you can use the custom templation of Bootstrap-Vue, you'll find the doc here with an example : https://bootstrap-vue.org/docs/components/table#custom-data-rendering
EDIT: here an example for your case
<b-table responsive="sm" :items="items">
<template #cell(userActive)="data">
<b-button v-if="data.userActive">Disabled</b-button>
<b-button v-else>Enabled</b-button>
</template>
</b-table>

Vue 3 Vuelidate not working with component

I am using vue 3 / typescript . I have 2 forms one is for patient registration and other is for patient visits both forms required validation on some fields. I am using vuelidate to validate both forms. I am have applied validation on both forms patient and visit . patient form is a component and i injected it inside the visit page .The problem is when i add patient component inside visit page vailidation on visit page get fails. but then i comment out the patient compoment on visit page it works fine. both forms are independent form each other.
Here is the patient registration component
<template>
<Dialog
id="previewReceiptDailog"
v-model:visible="productDialog"
:style="{ width: '60vw' }"
position="top"
class="p-fluid"
:modal="true"
:closable="true"
#hide="closeDialog"
>
<template #header>
<h6 class="p-dialog-titlebar p-dialog-titlebar-icon">
<i class="pi pi-plus-circle"></i> {{ dialogTitle }}
</h6>
</template>
<div class="p-grid">
<div class="p-col-6">
<div class="p-field">
<label
for="firstName"
:class="{
'p-error': v$.firstName.$invalid && submitted
}"
>First Name</label
>
<InputText
id="firstName"
v-model="v$.firstName.$model"
:class="{
'p-invalid': v$.firstName.$invalid && submitted
}"
/>
<small
v-if="
(v$.firstName.$invalid && submitted) ||
v$.firstName.$pending.$response
"
class="p-error"
>{{
v$.firstName.required.$message.replace(
"Value",
"First Name"
)
}}</small
>
</div>
</div>
</div>
<template #footer>
<Button
type="button"
label="Save"
icon="pi pi-check-circle"
class="p-button-primary pull-left"
#click.prevent="saveItem(!v$.$invalid)"
/>
</template>
</Dialog>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { reactive } from "vue";
import { required } from "#vuelidate/validators";
import useVuelidate from "#vuelidate/core";
#Options({
props: {
PatientRegistration: Object
},
watch: {
PatientRegistration(obj) {
this.openDialog();
this.dialogTitle = obj.dialogTitle;
this.productDialog = obj.status;
if (obj.patientID != 0) {
this.loadPatient(obj.patientID);
}
}
},
emits: ["updatePatientStatus"]
})
export default class PatientDialog extends Vue {
private submitted = false;
private state = reactive({
firstName: "",
});
private validationRules = {
firstName: {
required
},
};
private v$ = useVuelidate(this.validationRules, this.state);
//DEFAULT METHOD OF TYPE SCRIPT
//CALLING WHENEVER COMPONENT LOADS
created() {
this.toast = new Toaster();
this.patientService = new PatientService();
}
saveItem(isFormValid) {
this.submitted = true;
if (isFormValid) {
//DO SOMETHING
}
}
}
</script>
Here is the visit page where i injeceted the component of patient
<template>
<section>
<div class="app-container">
<Dialog
v-model:visible="productDialog"
:style="{ width: '50vw' }"
:maximizable="true"
position="top"
class="p-fluid"
>
<template #header>
<h5 class="p-dialog-titlebar p-dialog-titlebar-icon">
{{ dialogTitle }}
</h5>
</template>
<form #submit.prevent="saveVisitItem(!v1$.$invalid)">
<div class="p-field">
<label for="notes">Notes</label>
<InputText
id="notes"
v-model="v1$.notes.$model"
:class="{
'p-invalid': v1$.notes.$invalid && submitted
}"
/>
<small
v-if="
(v1$.notes.$invalid && submitted) ||
v1$.notes.$pending.$response
"
class="p-error"
>{{
v1$.notes.required.$message.replace(
"Value",
"Notes"
)
}}</small
>
</div>
<Button
type="submit"
label="Save"
icon="pi pi-check"
class="p-button-primary"
/>
</form>
</Dialog>
</div>
</section>
<PatientDialog
:PatientRegistration="{
patientID: this.patientID,
status: this.patientDialogStatus,
dialogTitle: this.dialogTitle
}"
v-on:updatePatientStatus="updatePatientStatus"
/>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import VisitService from "../../service/VisitService.js";
import { reactive } from "vue";
import useVuelidate from "#vuelidate/core";
import { required } from "#vuelidate/validators";
import PatientDialog from "../../components/PatientDialog.vue";
import AutoComplete from "primevue/autocomplete";
#Options({
components: {
PatientDialog,
AutoComplete
}
})
export default class EncounterVisit extends Vue {
private validationRules = {
notes: {
required
}
};
private visitFormState = reactive({
notes: "",
});
private v1$ = useVuelidate(this.validationRules, this.visitFormState);
//ADD OR UPDATE THE ITEM VIA HTTP
saveVisitItem(isFormValid) {
this.submitted = true;
console.log(this.v1$);
console.log(isFormValid);
//this.submitted = false;
}
}
</script>
The response i get from the console log is
$anyDirty: true
$clearExternalResults: ƒ $clearExternalResults()
$dirty: false
$error: false
$errors: []
$getResultsForChild: ƒ $getResultsForChild(key)
$invalid: true
$model: null
$path: "__root"
$pending: false
$reset: () => {…}
$silentErrors: (5) [Proxy, Proxy, Proxy, Proxy, Proxy]
$touch: () => {…}
$validate: ƒ ()
notes: Proxy {$dirty: ComputedRefImpl, $path: 'notes', required: {…}, $touch: ƒ, $reset: ƒ, …}
_vuelidate_72: Proxy {$dirty: ComputedRefImpl, $path: '__root', $model: null, $touch: ƒ, $reset: ƒ, …}
[[Prototype]]: Object
false

Ionic + Vuew: strange error when compiling

I'm experimenting with vuejs+ionic but when I try to compile on the mac I get this error
TS2339: Property 'selectedDate' does not exist on type '{ handler(date: string): Promise<false | undefined>; deep: boolean; }'.
If I compile in window everything is working.
This is located in the "watch" section.
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Controlli Preoperativi</ion-title>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Controlli preoperativi</ion-title>
</ion-toolbar>
</ion-header>
<ion-grid>
<ion-row>
<ion-col>
<div class="ion-text-center">
Da qui potrai impostare la presenza o assenza di non conformità nel tuo locale.
<br />
Attenzione, le modifiche nel passato sono possibili solo fino al giorno precedente.
</div>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-item>
<ion-label>imposta la data di lavoro</ion-label>
<ion-datetime display-format="DD/MM/YYYY" v-model="selectedDate"></ion-datetime>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col v-for="item in controlli" :key="item.id">
<ion-card>
<img :src="item.immagine" :alt="item.nome" :title="item.nome" />
<ion-card-header>
<ion-card-title>{{ item.nome }}</ion-card-title>
</ion-card-header>
<ion-card-content>
<div>
{{ item.descrizione }}
</div>
<div>
<ion-item>
<ion-label>presente</ion-label>
<ion-checkbox v-model="item.nonConformitaPresente" :data-id="item.id" data-type="ncp" #click="handleCheckChange(item.id, 'ncp')" />
</ion-item>
</div>
<div>
<ion-item>
<ion-label>non presente</ion-label>
<ion-checkbox v-model="item.nonConformitaNonPresente" :data-id="item.id" data-type="ncnp" #click="handleCheckChange(item.id, 'ncnp')" />
</ion-item>
</div>
<div v-show="item.nonConformitaPresente">
<ion-item>
<ion-label position="floating">problema riscontrato</ion-label>
<ion-textarea rows="5" v-model="item.problemaRiscontrato"></ion-textarea>
</ion-item>
<ion-item>
<ion-label position="floating">risoluzione</ion-label>
<ion-textarea rows="5" v-model="item.risoluzione"></ion-textarea>
</ion-item>
</div>
<div class="ion-text-center" style="margin-top: 15px">
<ion-button type="submit" shape="round" #click="handleSaveButton(item.id)" :data-id="item.id">
Salva
<ion-icon :icon="save"></ion-icon>
</ion-button>
</div>
</ion-card-content>
</ion-card>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
</ion-page>
</template>
<script lang="ts">
import { IonHeader, IonPage, IonDatetime, IonItem, IonIcon, IonButton, alertController, IonTextarea, IonContent, IonCheckbox, IonLabel, IonToolbar, IonTitle, IonCard, IonCardContent, IonCardHeader, IonCardTitle, IonGrid, IonRow, IonCol } from '#ionic/vue';
import { save } from 'ionicons/icons';
import ApiService from '#/services/api.service';
import {ControlloModel} from '#/services/app.service';
export default {
name: 'controlli',
components: { IonHeader, IonPage, IonDatetime, IonItem, IonIcon, IonButton, IonLabel, IonTextarea, IonContent, IonCheckbox, IonToolbar, IonTitle, IonCard, IonCardContent, IonCardHeader, IonCardTitle, IonGrid, IonRow, IonCol },
setup() {
return {
save
}
},
data() {
return {
selectedDate: new Date().toISOString().split('T')[0],
controlli: new Array<ControlloModel>()
}
},
watch: {
selectedDate: {
async handler(date){
const diff = Math.floor(new Date().getTime() - new Date(date).getTime());
const day = 1000 * 60 * 60 * 24;
const days = Math.floor(diff/day);
if(days <0 || days >1){
const alert = await alertController
.create({
cssClass: 'my-custom-class',
header: 'Errore',
subHeader: 'Data non valida',
message: 'La data selezionata non è valida, verrà utlizzato "OGGI" per la visualizzazione dei dati!',
buttons: ['OK'],
});
alert.present();
await alert.onWillDismiss();
date = new Date().toISOString().split('T')[0];
this.selectedDate = date;
return false;
}
await ApiService.get("/api/CheckGiornalieri?data=" + date.substring(0, 10))
.then(response => this.controlli = response.data);
} , deep: true
}
},
created() {
const data = new Date().toISOString().split('T')[0];
ApiService.get("/api/CheckGiornalieri?data=" + data).then(response => this.controlli = response.data);
},
methods: {
handleCheckChange(srcId: string, srcElemType: string){
const currItem = this.controlli.filter(function(elem){ if(elem.id == srcId) return elem; })[0];
if(srcElemType === "ncp"){
currItem.nonConformitaNonPresente = false;
} else {
currItem.nonConformitaPresente = false;
}
},
async handleSaveButton(srcId: string) {
const currItem = this.controlli.filter(function(elem){ if(elem.id == srcId) return elem; })[0];
const response = await ApiService.post("/api/NonConformita",
{
Data : new Date(this.selectedDate),
IdControllo : srcId,
Presente : currItem.nonConformitaPresente,
NonPresente : currItem.nonConformitaNonPresente,
Problema : currItem.problemaRiscontrato,
Risoluzione : currItem.risoluzione
}
);
if(response.data.state === false){
const alert = await alertController
.create({
cssClass: 'my-custom-class',
header: 'Errore',
subHeader: '',
message: response.data.message,
buttons: ['OK'],
});
alert.present();
}
}
}
}
</script>

How Ii can get input value within ionic 2 in my Component?

I will create a shopping app using ionic 2. In the products details I have created a stepper for increment and decrement value of quantity.
How can I get the input value within ionic 2 in my Component?
Solution:
1- app/pages/index.html
In Angular 2, you should use ngModel in the template.
<ion-header>
<ion-navbar primary>
</ion-navbar>
</ion-header>
<ion-content>
<ion-item>
<button lightgray full (click)="incrementQty()">+</button>
<ion-item>
<ion-input type="number" min="1" [value]="qty" [(ngModel)]="qty"></ion-input>
</ion-item>
<button lightgray full (click)="decrementQty()">-</button>
</ion-item>
</ion-content>
2- app/pages/index.ts
import { Component} from '#angular/core';
import { NavController, Slides} from 'ionic-angular';
#Component({
templateUrl: 'build/pages/titlepage/titlepage.html',
})
export class titlePage {
qty:any;
constructor(private nav: NavController) {
this.qty = 1;
}
// increment product qty
incrementQty() {
console.log(this.qty+1);
this.qty += 1;
}
// decrement product qty
decrementQty() {
if(this.qty-1 < 1 ){
this.qty = 1
console.log('1->'+this.qty);
}else{
this.qty -= 1;
console.log('2->'+this.qty);
}
}
}
Or as an alternative solution you may use the more appropriate Form controls designed for angular 2. (learn more here )
Example:
Typescript
import {Component, Input} from '#angular/core';
import {FORM_DIRECTIVES, FormBuilder, ControlGroup, AbstractControl} from '#angular/common';
import {IONIC_DIRECTIVES} from 'ionic-angular';
#Component({
selector: 'chat-form',
templateUrl: '/client/components/chat-form/chat-form.html',
directives: [IONIC_DIRECTIVES, FORM_DIRECTIVES],
pipes: [],
styleUrls: []
})
export class ChatFormComponent {
chatForm:ControlGroup;
messageInput:AbstractControl;
constructor(private translate:TranslateService, private formBuilder:FormBuilder) {
this.chatForm = formBuilder.group({messageInput: ['']})
this.messageInput = this.chatForm.controls['messageInput'];
}
sendMessage() {
console.log('sendMessage: ', this.messageInput.value);
}
}
Template
<form [ngFormModel]="chatForm" (ngSubmit)="sendMessage()">
<ion-input type="text" [ngFormControl]="messageInput" placeholder="{{'chat.form.placeholder' | translate }}"></ion-input>
<button fab>
<ion-icon name="send"></ion-icon>
</button>
</form>