Possible to prevent keyboard hiding when tapping a button?

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ā€¦ :confused:

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ā€¦ :unamused:

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>

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 found this:

keep keyboard open on Ionic when button click ( chat app )