What is the event to handle "enter" or "go" keyboard key on an input?
The input is not used within a form. So clicking on it will not "submit". I just need the event.
(Running android + Ionic 2 on Beta 11)
I did like this:
<ion-input type="text" [(ngModel)]="username" (keyup.enter)="handleLogin()"></ion-input>
And:
handleLogin() {
// Do your stuff here
}
For my case, I'm not getting next button within a form for both Android and IOS. I'm only getting done. so I handled done as a next by using following directive.
import { Directive, HostListener, Output, EventEmitter, ElementRef, Input } from '#angular/core';
import { Keyboard } from '#ionic-native/keyboard';
#Directive({
selector: '[br-data-dependency]' // Attribute selector
})
export class BrDataDependency {
#Output() input: EventEmitter<string> = new EventEmitter<string>();
#Input('br-data-dependency') nextIonInputId: any = null;
constructor(public Keyboard: Keyboard,
public elementRef: ElementRef) {
}
#HostListener('keydown', ['$event'])
keyEvent(event) {
if (event.srcElement.tagName !== "INPUT") {
return;
}
var code = event.keyCode || event.which;
if (code === TAB_KEY_CODE) {
event.preventDefault();
this.onNext();
let previousIonElementValue = this.elementRef.nativeElement.children[0].value;
this.input.emit(previousIonElementValue)
} else if (code === ENTER_KEY_CODE) {
event.preventDefault();
this.onEnter();
let previousIonElementValue = this.elementRef.nativeElement.children[0].value;
this.input.emit(previousIonElementValue)
}
}
onEnter() {
console.log("onEnter()");
if (!this.nextIonInputId) {
return;
}
let nextInputElement = document.getElementById(this.nextIonInputId);
// On enter, go to next input field
if (nextInputElement && nextInputElement.children[0]) {
let element: any = nextInputElement.children[0];
if (element.tagName === "INPUT") {
element.focus();
}
}
}
onNext() {
console.log("onNext()");
if (!this.nextIonInputId) {
return;
}
let nextInputElement = document.getElementById(this.nextIonInputId);
// On enter, go to next input field
if (nextInputElement && nextInputElement.children[0]) {
let element: any = nextInputElement.children[0];
if (element.tagName === "INPUT") {
element.focus();
}
}
}
}
const TAB_KEY_CODE = 9;
const ENTER_KEY_CODE = 13;
How to use?
<form [formGroup]="loginForm" (ngSubmit)="login(loginForm.value)">
<ion-input br-data-dependency="password" type="text" formControlName="username" placeholder="USERNAME" (input)="userNameChanged($event)"></ion-input>
<ion-input id="password" password type="password" formControlName="password" placeholder="PASSWORD"></ion-input>
<button submit-button ion-button type="submit" block>Submit</button>
</form>
Hope this help someone!!
Edit: Let me know if you are abled to show next button for the first input box?
The right way to do that might be to use Ionic2 forms. I'v found this: https://blog.khophi.co/ionic-2-forms-formbuilder-and-validation/
Otherwise - If you "just want the "Enter" event handler" this is quite complex (!) and not out of the box as you might be thinking:
HTML:
<ion-input id="myInput" #myInput type="submit" [(model)]="textValue" (input)="setText( $event.target.value )" placeholder="Send Message ..." autocorrect="off"></ion-input>
TS:
...
declare let DeviceUtil: any;
...
export class Component_OR_PAGE
{
public textValue: string;
#ViewChild( 'myInput') inputElm : ElementRef;
#HostListener( 'keydown', ['$event'] )
keyEvent( e )
{
var code = e.keyCode || e.which;
log.d( "HostListener.keyEvent() - code=" + code );
if( code === 13 )
{
log.d( "e.srcElement.tagName=" + e.srcElement.tagName );
if( e.srcElement.tagName === "INPUT" )
{
log.d( "HostListener.keyEvent() - here" );
e.preventDefault();
this.onEnter();
DeviceUtil.closeKeyboard();
}
}
};
...
setText( text )
{
log.d( "setText() - text=" + text );
this.textValue = text;
}
onEnter()
{
console.log( "onEnter()" );
this.inputText.emit( this.textValue );
this.textValue = "";
// ionic2 beta11 has issue with data binding
let myInput = document.getElementById( 'myInput' );
let innerInput: HTMLInputElement = <HTMLInputElement>myInput.children[0];
innerInput.value = "";
}
}
JS:
DeviceUtil =
{
closeKeyboard: function()
{
cordova.plugins.Keyboard.close();
}
}
Related
I program an angular application and use a dynamic datalist
<input id="inputCodePostalId" type="text" (keyup)="findPostalCode($event)" placeholder="Code Postal" formControlName="codePostal" list="codePostalId" />
<datalist id="codePostalId">
<option *ngFor="let code of postalCodeList" [value]="code">{{code}}</option>
</datalist>
the datalist is alimented with values coming from a request
findPostalCode(event: any) {
let text = event.target.value;
if (event.which != 16 && text != null && text.length > 2) {
this.postalCodeList = [];
this.produitImmobilierService.getPostalCodes(text).subscribe(
result => {
this.postalCodeList = result;
setTimeout( function () {
var elem = document.getElementById("inputCodePostalId");
elem.click();
}, 500)
}
);
}
}
When I enter 595 for example in the input, the request is made, and afterwards, when I manually click in the input, the datalist values appear, but when I programmatically click as above (see in setTimeout function) the datalist values don't appear
UPDATE
I added a click listener on the input
<input id="inputCodePostalId" type="text" (click)="method1();"................
and
method1() {
console.log('ca passe CLICK');
}
I tried also this
findPostalCode(event: any) {
let text = event.target.value;
if (event.which != 16 && text != null && text.length > 2) {
this.postalCodeList = [];
const event1 = new Event('click');
const element = document.getElementById("inputCodePostalId");
this.produitImmobilierService.getPostalCodes(text).subscribe(
result => {this.postalCodeList = result;
setTimeout( function () {element.dispatchEvent(event1); },
500)});
}
}
I have a email subject textbox where I can input combination of dynamic dropdown value and other text entered by user, here I can enter only one dynamic value at start, cant add another dynamic value after that please help.
its an email subject for example there are 3 dynamic value here ["creditcard","accountno","Amount"].
sample output - "Hi jack your creditcard with accoutno has due Amount" here creditcard accoutno and Amount are dynamic value from dropdown and rest are normal text
HTML
<div class="textbox">
<label class="textbox__label" [for]="id">{{label}}</label>
<input class="textbox__input" [type]="type" [id]="id" [placeholder]="placeholder" [value]="value" [name]="name"
autocomplete="off" (input)="onChange($event)" (keyup.Space)="doSomething()" [(ngModel)]="model" />
<ul class="textbox__dropdown" *ngIf="show">
<ng-container *ngFor="let list of listData ; let i = index">
<li (click)="handleSetValue(list)">
<span [style.background-color]="colors[i % colors.length]">{{list.value.charAt(0)}}</span>
{{list.value}}
</li>
</ng-container>
</ul>
</div>
TS
import { Component, VERSION,Output, EventEmitter } from '#angular/core';
import { isEmpty } from 'lodash';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular ' + VERSION.major;
public dynamicList = ["creditcard","accountno","Amount"]
public value;
public listData: any;
public show: boolean = true;
public model: any;
#Output() selectedValue = new EventEmitter();
onChange(event: any) {
if (event.target.value !== '' && this.dynamicList) {
console.log(event.target.value);
this.show = true;
const item = this.dynamicList.filter((items) => items.toLowerCase().includes(event.target.value));
if (!isEmpty(item)) {
this.listData = item;
console.log(item)
} else {
this.show = false;
this.selectedValue.emit(event.target.value)
}
} else {
this.listData = this.dynamicList;
this.show = false
}
}
Stackblitz-link
You can split the event.target.value that you are making in onChange() method like,
const getLastSearch = event.target.value.split(' ');
Then in the filter method you can use the last recent search into includes to check and display the latest search result like,
const item = this.dynamicList.filter((items) => items.toLowerCase().includes(getLastSearch[getLastSearch.length - 1]));
In app.component.html , for the input box you could use space bar event and call a method to show the dropdown items like,
<input class="textbox__input" type="type" id="id" name="name"
autocomplete="off" (input)="onChange($event)" (keyup.Space)= "spaceEvent($event)" [(ngModel)]="model" />
And the spaceEvent() as follows,
spaceEvent(event: any){
this.listData = this.dynamicList;
this.show = true;
}
Then finally you could split the strings available in the text box and then you can join it by removing the last one and append the clicked list item to as the last item like,
handleSetValue(list) {
let splittedSearch = this.model.split(' '); // Split each string with space
splittedSearch[splittedSearch.length - 1] = ''; // Make the last string empty
splittedSearch = splittedSearch.join(' ') // Join all the splitted string with space
splittedSearch += list; // Concat the splitted search with the selected list item
this.model = splittedSearch; // Assign the splittedsearch to model
this.show = false;
}
Forked Stackblitz here...
Please make the changes as per this
. In TS file
public model: any = "";
onChange(event: any) {
var value = event.target.value;
var inputarray = value.split(" ");
var lastinput = inputarray[inputarray.length - 1]
console.log(lastinput)
if ((value !== '' || inputarray.length> 1) && this.dynamicList) {
console.log(value);
this.show = true;
const item = this.dynamicList.filter((items) => items.toLowerCase().includes(lastinput));
if (!isEmpty(item)) {
this.listData = item;
console.log(item)
} else {
this.show = false;
this.selectedValue.emit(value)
}
} else {
this.listData = this.dynamicList;
this.show = false
}
}
handleSetValue(list) {
debugger
if(this.dynamicList.find(s=>s == list)){
this.model += " "+list;
}
this.show = false;
// this.selectedValue.emit(list.key)
}
in HTML
<div class="textbox">
<input class="textbox__input" type="type" id="id" name="name"
[ngModel]="model" autocomplete="off" (input)="onChange($event)"/>
<ul class="textbox__dropdown" *ngIf="show">
<ng-container *ngFor="let list of listData">
<li (click)="handleSetValue(list)">
{{list}}
</li>
</ng-container>
</ul>
</div>
I have a Reactive Form (#myform) with a button "foo", to which is attached (click) = myform.submit() so that the form will only submit when that button is clicked, and not on Enter events. The component listens for Enter events, and dispatches blur in that case, and the FormControls all have {updateOn: 'blur'} so blur events trigger updates (i.e., valueChanges, etc).
My problem is this: If I key data into an Input (or select an item in a Select) without hitting Enter or Tabbing out of the element and then click on the "foo", i.e. "Submit" button it seems that clicking the "foo" button only causes a blur event without triggering submission of the form (through myform.submit). I.e., the submit gets "swallowed" by the blur event.
How can I set this up so that if I don't Enter or Tab out of an element, and immediately click "foo", the element will be updated through the blur event AND THEN the form will ALSO be submitted?
I've tried things such as: 1. first processing the blur and then trying to programatically call myform.submit() in the component (by accessing the Form through #ViewChild), and 2. adding a (blur)= (submitNow && setTimeout(()=>myform.submit(),50) listener in the template view. At this point, I haven't even got this setTimeout call to compile, and none of the approaches I've tried seem to work so far.
The original code (simplified) is like this:
template details.component.html
<form #myform [formGroup]="form" method="POST" action="http://localhost:3000/users/{{userID}}/details">
<ng-container *ngFor="let sec of datArr">
<ng-container *ngFor="let row of sec; let i = index">
<input type="text"
[appTag]="row[datColEnum.tabCol]"
(keydown.enter)="onEnter($event)"
tabindex = "{{row[datColEnum.tabCol]}}"
name="{{row[datColEnum.controlNameCol]}}" value="{{row[datColEnum.valueCol]}}"
formControlName="{{row[datColEnum.controlNameCol]}}"/>
</ng-container>
</ng-container>
<button name="foo" type="button" (click)="myform.submit()">Submit</button>
</form>
component details.component.ts
export class DetailsComponent implements OnInit, AfterViewInit {
#ViewChildren(TagDirective) ipt!: QueryList<ElementRef>;
#ViewChild('myform', {static: true}) myform: any;
data: Object;
meta: Object;
datArr: any[] = [];
formCtls: any = {};
form: FormGroup = new FormGroup({});
tabIdx: number = 0;
focusItm: number = 0;
submitNow: boolean = false;
constructor(private member: MemberService, private route: ActivatedRoute, private el: ElementRef) { }
ngAfterViewInit() {
this.ipt.changes.subscribe(list=>{
setTimeout(()=>
list.filter(itm=>+itm.id===this.focusItm%this.tabIdx).forEach(itm=>{
if (!this.first_pass) this.navigateTo(itm.id);
}
),100)
})
}
doSubmit() {
this.submitNow = true;
}
navigateTo(ti: number) {
console.log(`NAVIGATING TO ITEM WITH TAB INDEX: ${ti}`)
this.ipt["_results"][ti-1].el.nativeElement.focus();
this.ipt["_results"][ti-1].el.nativeElement.style.borderWidth = "3px";
}
onEnter(e: Event) {
let cname = (<HTMLInputElement | HTMLSelectElement>e.target).name;
let idx = 0;
for (let itm of this.ipt["_results"]) {
if (itm.el.nativeElement.name === cname) {
this.focusItm = (itm.id+1)%this.ipt["_results"].length;
itm.el.nativeElement.style.borderWidth = "1px";
break;
}
}
e.target.dispatchEvent(new Event('blur'));
}
ngOnInit() {
. . .
. . .
//SET EVERYTHING UP
this.renderDataArray();
. . .
. . .
})
};
// SET UP ALL THE FormControls, etc. AND THE DATA MODEL datArr USED TO RENDER THE TEMPLATE
renderDataArray() {
this.datArr = [];
this.tabIdx = 0;
Object.keys(this.meta).forEach((sec,idx) => {
let rowCnt = this.getActiveArrayLen(sec);
for (let i = 0; i < rowCnt; i++) {
Object.keys(this.data[sec]).
.map((fld,idx)=>{
let itm = this.data[sec][fld];
this.tabIdx += 1;
let val = getVal();
let ctlName = sec+this.FLD_SPLIT_CHAR+fld;
this.removeControl(ctlName);
this.formCtls[ctlName] = new FormControl(val, {updateOn: 'blur'});
this.form.addControl(ctlName, this.formCtls[ctlName]);
of(this.tabIdx).
pipe(switchMap(ti=>this.formCtls[ctlName].valueChanges.
pipe(map(newVal => [newVal,ti]))
)
).subscribe(([newVal,ti])=>{
if (Array.isArray(itm["value"])) {
itm["value"][+(itm["value"].length || 1)-1]=newVal || '';
} else {
itm["value"]=newVal || '';
}
this.focusItm = (ti+1)%this.ipt["_results"].length;
this.renderDataArray();
});
. . .
. . .
I have written a directive for text input, to support int values.
Here is it
import { NgControl } from '#angular/forms';
import { HostListener, Directive } from '#angular/core';
#Directive({
exportAs: 'number-directive',
selector: 'number-directive, [number-directive]'
})
export class NumberDirective {
private el: NgControl;
constructor(ngControl: NgControl) {
this.el = ngControl;
}
// Listen for the input event to also handle copy and paste.
#HostListener('input', ['$event.target.value'])
onInput(value: string) {
// Use NgControl patchValue to prevent the issue on validation
this.el.control.patchValue(value.replace(/[^0-9]/g, ''));
}
}
And HTML
<div class="form-group">
<label>{{ l("RoomWidth") }}</label>
<input
decimal-number-directive
#roomWidthInput="ngModel"
class="form-control nospinner-input"
type="text"
name="roomWidth"
[(ngModel)]="room.roomWidth"
maxlength="32"
/>
</div>
But I need it to support decimal values. For example 99.5
How do I need to modify it?
Try this:
#HostListener('input', ['$event.target.value'])
onInput(value: string) {
// Use NgControl patchValue to prevent the issue on validation
this.el.control.patchValue(value.replace(/[^0-9].[^0-9]/g, ''));
}
Working_Demo
In my case I have need to considerate two consegutive point too and replace caracter. Maybe is writable with regex but this work for me.
Example if you type or paste 45.456.6 it will be repaced with 45.4566
#HostListener('input', ['$event']) onInputChange(event) {
let initalValue: string = this.el.nativeElement.value;
initalValue = initalValue.replace(/[^\.|0-9]/g, '');
// elimina le seconde occorrenze del punto
const count = (initalValue.match(/\./g) || []).length;
for (let i = 1; i < count; i++) {
initalValue = this.repaceSecondDotOccurrence(initalValue);
}
this.el.nativeElement.value = initalValue;
}
repaceSecondDotOccurrence(inputString): string {
let t = 0;
return inputString.replace(/\./g, function (match) {
t++;
return (t === 2) ? '' : match;
});
}
When i press the Date control,
and i do not enter a date,
I click Esc ==> The Date control does not disappear
I click outside ==> The Date control does not disappear
here my code html and ts :
<input class="cssInputDate" type="text" id="dateDebut" name="dateDebut"
#dateDebut="ngModel"
(keyup)="onKeyUp($event)"
(blur)="checkDateDebut()"
required [ngModel]="dateDebutModel" (ngModelChange)="dateDebChange($event)" ngbDatepicker #ddeb="ngbDatepicker" >
<button tabindex="3" (click)="ddeb.toggle(); openDatepicker(ddeb)" type="button" style="margin-left: 0;" *ngIf="modificationMode" >
<i class="fa fa-calendar" aria-hidden="true"></i>
</button>
here the .ts :
openDatepicker(id){
console.log(" id =",id);
console.log(" dateDebInput =",this.dateDebInput);
this.dynamicId = id;
}
onClick(event) {
if(this.dynamicId == undefined){
console.log("Dynamic id ===",this.dynamicId);
}
else if(!this._eref.nativeElement.contains(event.target)) {
this.dateDebInput.close();
}
}
here the console log output :
any solution ?
i find a solution
i use #HostListener here the code :
#HostListener('mousedown', ['$event'])
mouseEvent(event) {
if(event.target.offsetParent.tagName !== 'NGB-DATEPICKER'){
this.dateDebInput.close();
}
}
same of escape :
#HostListener('window:keyup', ['$event'])
keyEvent(event: KeyboardEvent) {
if (event.key === "Escape") {
this.dateDebInput.close();
}
}
ere the code of checkDateDebut :
checkDateDebut() {
const check = this.dateDebutModel != null && this.dateDebutModel !== '';
const checkFinAfterDebut = this.checkDateFinAfterDateDebut();
if (!check) {
this.pushMessageFront('DIAG_ERR_029');
} else {
this.spliceMessageFront('DIAG_ERR_029');
}
if (!check || !checkFinAfterDebut) {
this.highlight('dateDebut', true);
} else {
this.highlight('dateDebut', false);
}
return check && checkFinAfterDebut;
}
the css :
.cssInputDate{
min-width: 85px;
}