ionic 2:在页面内的FORM中添加自定义输入组件/最终目标:集成"cordova-plugin-datepicker"离子2形式 [英] ionic 2: add custom input component in a FORM within a page / Final goal: integrate "cordova-plugin-datepicker" in Ionic 2 Form
问题描述
在ionic 2版本中:
In ionic 2 version:
- 科尔多瓦CLI:6.4.0,
- 离子框架版本:2.0.0-rc.0,
- 离子CLI版本:2.1.0,
- Ionic App Lib版本:2.1.0-beta.1,操作系统:
- 节点版本:v6.7.0
使用Ionic 2 FORM,输入:<ion-datetime>
碰巧很慢(参见此处).
With Ionic 2 FORM, the input: <ion-datetime>
happens to be slow (see here).
我想解决这个问题并使用"cordova-plugin-datepicker" 代替.我对此有很多疑问,以使其起作用.但是,我将从要做的第一步开始:实施一个可用作<ion-[something for a form input]>
标签的自定义选择器.
I want to go around it and use the "cordova-plugin-datepicker" instead. I have many question about it to make it work. But I'll start here with the first step I need to achieve: To implement a custom selector that can be use as a <ion-[something for a form input]>
tag.
从这里开始,我们将尝试通过另一个组件实现标签<ion-datetime>
.
To start with here, we are just going to try to implement the tag <ion-datetime>
thru another component.
我在此处找到了类似的问题.它告诉import:import {IONIC_DIRECTIVES} from 'ionic-angular';
并在@Component
批注中添加元数据:directives: [IONIC_DIRECTIVES]
.但是在 Angular 2文档元数据directives
不再存在.如果尝试这样做,我会得到一个错误.
I've found similar issue here. It tells to import:import {IONIC_DIRECTIVES} from 'ionic-angular';
and to add in the @Component
annotation the metadata: directives: [IONIC_DIRECTIVES]
. But in Angular 2 documentation the metadata directives
does not exist anymore. And I get an error if I try that.
现在我的代码:
我有一个用户表单页面:
I have a User form page:
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NavController, NavParams } from 'ionic-angular';
import { NativeDatePickerTag } from '../../custom-components/native-date-picker/native-date-picker';
@Component({
selector:'user-form',
templateUrl: 'user-form.html',
providers: [Validators]
})
export class UserFormPage {
private readonly PAGE_TAG:string = "UserFormPage";
public birthdate:any;
public userForm:FormGroup;
constructor(public navCtrl: NavController, public navParams: NavParams, public fb:FormBuilder, public validators:Validators){}
public updateUserData = () => {
console.log(this.userForm.value);
}
ionViewDidLoad(){
console.log(this.PAGE_TAG + " ionViewDidLoad() starts");
this.userForm = this.fb.group({
birthday: [this.birthdate,Validators.required],
});
}
}
在我的"user-form.html"中,它看起来像这样:
In my 'user-form.html' it looks like this:
<ion-content>
<form (ngSubmit)="updateUserData()" [formGroup] = "userForm" >
<ion-item>
<ion-label stacked>Birthdate</ion-label>
<native-date-picker [controlName]="birthday"></native-date-picker>
</ion-item>
<button ion-button type="submit" block>Submit</button>
</form>
</ion-content>
还有我的自定义组件NativeDatePickerTag(同样,这是尚未实现cordova-plugin-datepicker的概念证明):
And my custom component NativeDatePickerTag (again, this a proof of concept not yet implementing the cordova-plugin-datepicker) :
import { Component, Input, ViewChild, ElementRef } from '@angular/core';
import { Platform } from 'ionic-angular';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'native-date-picker',
template: `
<ion-datetime [formControlName]='this._controlName'></ion-datetime>
`
})
export class NativeDatePickerTag {
private readonly COMPONENT_TAG = "NativeDatePickerObj";
public _controlName: FormControl;
@Input () set controlName(newControl: FormControl){
this._controlName = newControl;
}
constructor(public platform:Platform){
}
}
如果我这样运行代码,它将在console.log中告知:
If I run the code like that, it tells in the console.log:
formControlName必须与父formGroup指令一起使用
formControlName must be used with a parent formGroup directive
我不明白为什么它不考虑选择器native-date-picker
嵌入在"user-form.html"内部的原因.因此,我尝试通过'customer-form.html'中的formGroup
来纠正此错误.
I don't understand why it does not take into account the formGroup
the selector native-date-picker
is embedded in inside 'user-form.html'. So I've tried to pass the formGroup
from 'customer-form.html' to correct this error.
在"user-form.html"中,我进行了更改,
<native-date-picker [controlName]="birthday"></native-date-picker>
和:
<native-date-picker [groupName]="userForm" [controlName]="birthday"></native-date-picker>
In 'user-form.html' I've changed,
<native-date-picker [controlName]="birthday"></native-date-picker>
with:
<native-date-picker [groupName]="userForm" [controlName]="birthday"></native-date-picker>
在NativeDatePickerTag中,我通过以下方式更改了注释:
And in NativeDatePickerTag, I changed the annotation with:
@Component({
selector: 'native-date-picker',
template: `<div [formGroup]='this._formGroup'>
<ion-datetime [formControlName]='this._controlName'></ion-datetime>
</div>
`
})
我在类NativeDatePickerTag中添加了以下内容: public _formGroup:FormGroup;
And I added inside my class NativeDatePickerTag the following: public _formGroup: FormGroup;
@Input () set groupName(newGroup: FormGroup){
this._formGroup = newGroup;
}
现在我进入console.log:
Now I get in console.log:
找不到具有未指定名称属性的控件
Cannot find control with unspecified name attribute
我真的不明白我在做什么错.谁有这个主题的经验,能给我一些指导吗?
I really don't understand what I am doing wrong. Could anyone, with experience regarding this topic,give me some directions?
推荐答案
I found a solution, the key was to understand how works ControlValueAccessor interface
我通过阅读那些链接来做到这一点:
I did that by reading thru those link:
- http://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html ,
- Angular 2自定义表单输入
- https://github.com/angular/angular/issues/6639
- http://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html,
- Angular 2 custom form input
- https://github.com/angular/angular/issues/6639
请按以下要求输入代码:
As requested here is the code:
native-date-picker.ts:
The native-date-picker.ts:
import { Component, Input, Output, ViewChild, ElementRef, forwardRef, EventEmitter } from '@angular/core';
import { FormGroup, FormControl, NG_VALUE_ACCESSOR, NG_VALIDATORS, ControlValueAccessor } from '@angular/forms';
import { FormValidators } from '../../form-validators/form-validators';
import { Platform } from 'ionic-angular';
import { DatePicker } from 'ionic-native';
import { TranslationService } from '../../services/translation/translation';
import moment from 'moment/min/moment-with-locales.min.js';
export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => NativeDatePickerTag),
multi: true
};
declare var datePicker: any;
@Component({
selector: 'native-date-picker',
templateUrl: 'native-date-picker.html',
providers:[CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class NativeDatePickerTag implements ControlValueAccessor {
private readonly COMPONENT_TAG = "NativeDatePickerTag";
//The internal data model
public dateValue: any = '';
public pickerType=null;
public _labelForNDP:any;
public displayFormatForIonDateTime:string;
public ionDateTimeMonthsShort:string;
public ionDateTimeMonthsLong:string;
@Input() submitAttempt;
@Input() control;
@Input () set labelForNDP(value:any){
this._labelForNDP = value;
console.log("labelForNDP : " + value);
}
@Output () onChange: EventEmitter<any> = new EventEmitter();
//Set touched on ionChange
onTouched(){
console.log(this.COMPONENT_TAG + " onTouched() starts");
this.control._touched=true;
}
//From ControlValueAccessor interface
writeValue(value: any) {
console.log(this.COMPONENT_TAG + " writeValue("+value+") starts");
if (value !== undefined || value !== null) {
this.dateValue = (new moment(value)).format('YYYY-MM-DD');
}
console.log(this.COMPONENT_TAG + " writeValue("+value+") this.dateValue " + this.dateValue);
}
diplayDateAccordingToSettings(date:any){
console.log(this.COMPONENT_TAG + " diplayDateAccordingToSettings("+date+")");
let dateToBeDisplayed:any;
if(moment(date,'YYYY-MM-DD').isValid()){
dateToBeDisplayed = (new moment(date)).locale(this.trans.getCurrentLang()).format(this.displayFormatForIonDateTime);
console.log(this.COMPONENT_TAG + " diplayDateAccordingToSettings("+date+")" + " GIVES " + dateToBeDisplayed);
} else {
dateToBeDisplayed="";
}
return dateToBeDisplayed;
}
updateDate(event:any) {
console.log(this.COMPONENT_TAG + " updateDate() starts");
console.info(event);
let newValue = "I'm new value";
let dateToSetOn = (new moment(event)).format('YYYY-MM-DD');
console.log(this.COMPONENT_TAG + " updateDate() about to return " + dateToSetOn);
this.onTouched();
this.onChange.next(dateToSetOn);
}
//From ControlValueAccessor interface
registerOnChange(fn: any) {
console.log(this.COMPONENT_TAG + " registerOnChange() starts");
console.info(fn);
this.onChange.subscribe(fn);
}
//From ControlValueAccessor interface
registerOnTouched(fn: any) { //leave it empty
}
// get the element with the # on it
@ViewChild("nativeDatePicker") nativeDatePicker: ElementRef;
@ViewChild("ionDatePicker") ionDatePicker: ElementRef;
constructor(public platform:Platform, public trans:TranslationService){
console.log(this.COMPONENT_TAG + " constructor() starts");
console.info(this);
this.displayFormatForIonDateTime = moment.localeData(this.trans.getCurrentLang())._longDateFormat['LL'];
this.ionDateTimeMonthsShort = moment.localeData(this.trans.getCurrentLang()).monthsShort();
this.ionDateTimeMonthsLong = moment.localeData(this.trans.getCurrentLang()).months();
this.setFieldWhenPlatformReady();
}
private setFieldWhenPlatformReady = () => {
this.platform.ready().then(() => {
if(this.platform.is('android')){
this.pickerType = "android";
} else if (this.platform.is('ios')) {
// ios case: NOT DONE YET
} else if (this.platform.is('core')) {
this.pickerType = "core";
}
}
);
}
public dateInputManagement = () => {
console.log(this.COMPONENT_TAG + " dateInputManagement() starts");
let dateToUseOnOpening = (moment(this.dateValue,'YYYY-MM-DD').isValid())?new Date(moment(this.dateValue,'YYYY-MM-DD')):new Date();
console.info(dateToUseOnOpening);
let options = {
date: dateToUseOnOpening,
mode: 'date',
androidTheme: datePicker.ANDROID_THEMES.THEME_HOLO_LIGHT
};
DatePicker.show(options).then(
(date) => {
let lang = this.trans.getCurrentLang();
this.writeValue(new moment(date));
this.updateDate(new moment(date));
}).catch( (error) => { // Android only
});
}
}
和native-date-picker.html:
And native-date-picker.html:
<ion-item>
<ion-label stacked>{{_labelForNDP}}</ion-label>
<ion-datetime #ionDatePicker [displayFormat]="displayFormatForIonDateTime" [monthShortNames]="ionDateTimeMonthsShort" [monthNames]="ionDateTimeMonthsLong" *ngIf="pickerType=='core' || pickerType=='ios'" name="birthday" [ngModel]="dateValue" (ngModelChange)="updateDate($event)" [class.invalid]="!control.valid && (control.touched||submitAttempt)"></ion-datetime>
<ion-input #nativeDatePicker type="text" disabled=true (click)="dateInputManagement()" *ngIf="pickerType=='android'" name="birthday" [ngModel]="diplayDateAccordingToSettings(dateValue)" (ngModelChange)="updateDate($event)" [class.invalid]="!control.valid && (control.touched||submitAttempt)"></ion-input>
</ion-item>
在包含调用它的表单的组件的HTML模板中,为了遵守需要提供给NativeDatePicker类的@input
,它必须看起来像这样:
And in the HTML template of the component containing a form that calls it, to respect the @input
that need to be given to the class NativeDatePicker, it must look like that:
<native-date-picker [labelForNDP]="LABEL" #nativeDatePickerOnUserPage formControlName="date" [control]="userForm.controls['date']" [submitAttempt]=submitAttempt>
</native-date-picker>
这篇关于ionic 2:在页面内的FORM中添加自定义输入组件/最终目标:集成"cordova-plugin-datepicker"离子2形式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!