Possible to prevent keyboard hiding when tapping a button?


#1

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:


#2

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:


#3

Do you find any workaround for iOs device?


#4

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();
    }
  }
}

#5

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>

#6

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.


#7

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>