I am having a difficult time trying to create a messing interface. I have previously had trouble with the scroll assist across ios/android but I have worked around that by creating a directive that essentially performs the ionic scroll/keyboard assist for elements that are not children of the ion-content element.
But I am still having trouble with the keyboard hiding when tapping on the send button, I swear the template below was working before I added the scroll assist directive.
template:
<ion-navbar *navbar secondary>
<ion-title>{{ isNew ? 'New ' : '' }}Thread{{ topic ? ': ' + topic.name : ''}}</ion-title>
</ion-navbar>
<ion-content class="thread">
<ion-list>
<ion-item *ngFor="let message of messages" no-lines>
<ion-avatar item-left *ngIf="!message.isSelf">
<ion-icon [name]="message.user.icon" style="font-size: 36pt"></ion-icon>
</ion-avatar>
<p>{{ message.message }}</p>
<ion-avatar item-right *ngIf="message.isSelf">
<ion-icon [name]="message?.user.icon" style="font-size: 36pt"></ion-icon>
</ion-avatar>
</ion-item>
</ion-list>
</ion-content>
<ion-toolbar position="bottom" class="composeArea" react-to-keyboard>
<form>
<ion-input ref-composeinput type="text" [(ngModel)]="compose" placeholder="Write a message..."></ion-input>
<button (click)="send()" clear type="submit">
<ion-icon name="send"></ion-icon>
Send
</button>
</form>
</ion-toolbar>
scroll assist directive:
@Directive({
selector: '[react-to-keyboard]'
})
export class ReactToKeyboardDirective {
public showSub;
public hideSub;
private tabbarHeight: number;
constructor(public el: ElementRef, public renderer: Renderer, private platform: Platform, public events: Events) {
if (platform.is('ios')) {
this.tabbarHeight = window.document.querySelector('ion-tabbar-section').getBoundingClientRect().height;
this.showSub = Keyboard.onKeyboardShow().subscribe((a) => {
this.renderer.setElementStyle(this.el.nativeElement, 'paddingBottom', (a.keyboardHeight - this.tabbarHeight / 2).toString() + 'px');
this.renderer.setElementStyle(this.el.nativeElement, 'paddingTop', (this.tabbarHeight / 2).toString() + 'px');
this.events.publish('react-to-keyboard:padding-added');
});
this.hideSub = Keyboard.onKeyboardHide().subscribe((b) => {
this.renderer.setElementStyle(this.el.nativeElement, 'paddingBottom', '');
this.renderer.setElementStyle(this.el.nativeElement, 'paddingTop', '');
this.events.publish('react-to-keyboard:padding-removed');
});
}
}
ngOnDestroy() {
if (this.showSub) {
this.showSub.unsubscribe();
}
if (this.hideSub) {
this.hideSub.unsubscribe();
}
}
}
Its not pretty but it worksā¦
1 Like
Answering my own question. This turned out to be super simple after I thought about it.
I set a boolean flag on the page and flip it on the touchstart event (mobile safari required this vs tap) and flip it back after the blur was āpreventedā by an immediate focus.
Page changes
public preventBlur: boolean = false;
...
shouldBlur(event) {
if (this.preventBlur) {
event.target.focus();
// Reset so other elements will blur the keyboard
this.preventBlur = false;
}
}
resetBlur() {
this.preventBlur = false;
}
flipBlur() {
this.preventBlur = true;
}
Template changes:
<ion-input ref-composeinput type="text" [(ngModel)]="compose" placeholder="Write a message..." (blur)="shouldBlur($event)"></ion-input>
<button (click)="send()" (touchstart)="flipBlur()" (touchcancel)="resetBlur()" clear type="submit">
<ion-icon name="send"></ion-icon>
Send
</button>
EDIT: This doesnāt work on iOSā¦
Do you find any workaround for iOs device?
I ended up re-writing basically what the framework does but with a directive so it isnāt tied to the component/css hierarchy. This can definitely be cleaned up (and hopefully can be dropped in a future release) but Iām off to other areas at the moment.
Template:
<ion-footer>
<ion-toolbar position="bottom" class="composeArea" react-to-keyboard>
<form #f="ngForm">
<ion-textarea ref-composeinput [(ngModel)]="compose" placeholder="Write a message..." (blur)="shouldBlur($event)" autocomplete="on" autocorrect="on"></ion-textarea>
<button (click)="send()" (touchstart)="flipBlur()" (touchcancel)="resetBlur()" clear type="submit" class="input-cover">
<span class="button-inner input-cover">
<ion-icon name="send"></ion-icon>
Send
</span>
</button>
</form>
</ion-toolbar>
</ion-footer>
Directive:
@Directive({
selector: '[react-to-keyboard]'
})
export class ReactToKeyboardDirective {
public showSub;
public hideSub;
private defaultHeight: number;
private defaultPaddingTop: number;
private defaultPaddingBottom: number;
private keyboardIsShowing: boolean = false;
constructor(public el: ElementRef, public renderer: Renderer, private platform: Platform, public events: Events) {
if (platform.is('ios')) {
this.defaultHeight = window.document.body.getBoundingClientRect().height;
this.defaultPaddingTop = this.el.nativeElement.style.paddingTop;
this.defaultPaddingBottom = this.el.nativeElement.style.paddingBottom;
this.showSub = Keyboard.onKeyboardShow().subscribe((a) => {
if (this.keyboardIsShowing) {
// Ensure iOS didn't resize the body element randomly... ><
this.renderer.setElementStyle(this.el.nativeElement.parentElement, 'height', this.defaultHeight + 'px');
return;
}
this.keyboardIsShowing = true;
this.el.nativeElement.style.paddingBottom = (a.keyboardHeight + 20).toString() + 'px';
this.el.nativeElement.style.paddingTop = '3rem';
this.events.publish('react-to-keyboard:padding-added');
});
this.hideSub = Keyboard.onKeyboardHide().subscribe((b) => {
this.keyboardIsShowing = false;
this.el.nativeElement.style.paddingTop = this.defaultPaddingTop;
this.el.nativeElement.style.paddingBottom = this.defaultPaddingBottom;
this.events.publish('react-to-keyboard:padding-removed');
});
}
}
ngOnDestroy() {
if (this.showSub) {
this.showSub.unsubscribe();
}
if (this.hideSub) {
this.hideSub.unsubscribe();
}
}
}
Iāve found a solution (Ionic 2-3 donāt know if it works in v1):
<input id="yourText" type="text" >
<label for="yourText" (click)="doSomething();" ><i class="md-send"></i></label>
1 Like
Can you make this work with a Text Area? Cos often you get a long messages with multiple lines and input no longer does a good job there.
Sure:
<textarea rows="1" cols="80" class="message" id="textarea"></textarea>
<label for="textarea" ion-button icon-only round outline class="send" (click)="sendMessage();"><ion-icon name="md-send"></ion-icon></label>
novyc
December 10, 2022, 9:25am
8
sovanyio:
ion-input
Hi @Splinter1997 can you explain a little bit? Do you mean replacing the āion-inputā with an input and a label prevent keyboard hiding when tapping on a button or somewhere else in the screen?
1 Like
I finally found a solution.
This prevents mobile browsers from blurring the input when the components like ion-button button is activated/pressed.
<ion-button
...
..
onPointerDown={(ev) => {ev.preventDefault();}}
...
</ion-button>
Its pretty straight forward.