Set focus on an input with Ionic 2 - input

SOLVED :
import { Component, ViewChild} from '#angular/core';
import { Keyboard } from 'ionic-native';
#Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {
#ViewChild('input') myInput ;
constructor() {}
ionViewDidLoad() {
setTimeout(() => {
Keyboard.show() // for android
this.myInput.setFocus();
},150);
}
}
1) import "ViewChild"
import {Component, ViewChild} from '#angular/core';
2) Create a reference to your input in your html template :
<ion-input #focusInput></ion-input>
3) Use #ViewChild to get access to the input component you just referenced previously.
#ViewChild('focusInput') myInput ;
4) Trigger the focus
Use the ionViewLoaded() method to trigger it each time the view/page is loaded.
setTimeout is needed
ionViewLoaded() {
setTimeout(() => {
Keyboard.show() // for android
this.myInput.setFocus();
},150); //a least 150ms.
}
4) Show the keyboard on Android
import { Keyboard } from 'ionic-native';
Call Keyboard.show() to call the keyboard on Android.
5) Show the keyboard on iOS
add this line to your config.xml to make it work on iOS :
<preference name="KeyboardDisplayRequiresUserAction" value="false" />
With the help from the great article of mhartington : http://mhartington.io/post/setting-input-focus/

You don't need to import the 'Input' from 'angular/core'.
Simply:
import {Component,ViewChild} from '#angular/core';
import {NavController, TextInput } from 'ionic-angular';
#Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {
#ViewChild('input') myInput: TextInput;
constructor(private navCtrl: NavController) { }
ionViewDidLoad() {
setTimeout(() => {
this.myInput.setFocus();
},150);
}
}
And answering comment to Ciprian Mocanu:
It does not work in iOS :(
It works on iOS -> checked on iPhone 6 PLUS with iOS 10

I think you should make a global directive for this as you will probably want this behavior more than once.
import { ViewChild, ElementRef, Directive, OnInit } from '#angular/core';
import { Keyboard } from 'ionic-native';
#Directive({
selector: '[autofocus]'
})
export class FocusInput implements OnInit {
#ViewChild('myinput') input
private focused: boolean
ngOnInit(){
this.focused = true
}
ionViewDidLoad() {
if (this.focused) {
setTimeout(()=>{
this.input.setFocus()
this.focused = false
Keyboard.show()
}, 300)
}
}
}
Now on you ion-input field just add the autofocus attribute
<ion-input #myinput type="..." placeholder="..."
(keyup.enter)="someAction()"
autofocus ></ion-input>

None of the above was working for me. Here is how I resolved:
import { ElementRef, AfterViewChecked, Directive } from '#angular/core';
import {Keyboard} from 'ionic-native';
#Directive({
selector: '[autofocus]'
})
export class FocusInput implements AfterViewChecked {
private firstTime: boolean = true;
constructor(public elem: ElementRef) {
}
ngAfterViewChecked() {
if (this.firstTime) {
let vm = this;
setTimeout(function(){
vm.elem.nativeElement.firstChild.focus();
vm.firstTime = false;
Keyboard.show();
}, 300)
}
}
}
Then in your ion-input field just add the autofocus attribute:
<ion-input #input type="text" placeholder="..."
[(ngModel)]="myBoundVariable"
(keyup.enter)="myEnterKeyAction()"
autofocus></ion-input>
Tested on Browser and Android not IOS yet but no reason it should not work.

import {Component, ViewChild} from '#angular/core';
import {NavController} from 'ionic-angular';
#Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {
#ViewChild('Comment') myInput ;
constructor(private navCtrl: NavController) { }
ionViewLoaded() {
setTimeout(() => {
this.myInput.setFocus();
},150);
}
}
Create a reference to your input in your template :
<ion-input #Comment>

import {Component,ViewChild} from '#angular/core';
import {NavController} from 'ionic-angular';
#Component({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {
#ViewChild('myInput') myInput ;
constructor(private navCtrl: NavController) { }
ionViewDidLoad() {
window.setTimeout(() => {
this.myInput.setFocus();
}, 600); //SET A LONG TIME IF YOU ARE IN A MODAL/ALERT
}
}
<ion-input #myInput ></ion-input>

If you need to set focus on an input at init component, set the class input-has-focus by default to ion-item just like this:
<ion-item class="input-has-focus">
That's all!

I found this solution to also fix the problem that the keyboard is pushing the content away.
<ion-list>
<ion-item>
<ion-label>Name</ion-label>
<ion-input #inputRef type="text"></ion-input>
</ion-item>
<button ion-button (click)="focusMyInput(inputRef)">Focus</button>
#ViewChild(Content) content: Content;
focusMyInput(inputRef) {
const itemTop = inputRef._elementRef.nativeElement.getBoundingClientRect().top;
const itemPositionY = this.content.scrollTop + itemTop -80;
this.content.scrollTo(null, itemPositionY, 500, () => {
inputRef.setFocus();
});
}

In my case, for some reason, ionViewLoaded() was not getting triggered. Tried ionViewDidLoad() and set the timer to 200 and it worked.
150 proved too early for me. Complete Solution:
import { Component, ViewChild } from '#angular/core';//No need to import Input
export class HomePage {
#ViewChild('inputToFocus') inputToFocus;
constructor(public navCtrl: NavController) {}
ionViewDidLoad()
{
setTimeout(() => {
this.inputToFocus.setFocus();
},200)
}
}
And on the input tag:
<ion-input type="text" #inputToFocus></ion-input>

For IOS and Android its fine working for me. put focus code in ionViewWillEnter().
import { Component, ViewChild, ElementRef } from '#angular/core';
import { Keyboard } from '#ionic-native/keyboard';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
#ViewChild("Input") inputEl: ElementRef;
constructor(public keyboard:Keyboard){}
ionViewWillEnter() {
setTimeout(() => {
this.inputEl.nativeElement.focus();
this.keyboard.show();
}, 800); //If its your first page then larger time required
}
Input tag in html file
<ion-input type="text" #Input></ion-input>
And add this line to your config.xml to make it work on iOS :
<preference name="KeyboardDisplayRequiresUserAction" value="false" />

Setting timeout worked for me!
setTimeout(() => {
this.inputToFocus.setFocus();
}, 800);
However, if a new input element is added it sets focus to first input only.

Related

How to navigate to another page and make the badge count empty, on click of mat-icon button?

Requirement:
I am getting badge count based on API value.
I want onclick of icon button it should navigate to another page and make the badge count empty.
Problem:
I am able to navigate to another page but badge count remains same its not becoming empty.
Please anyone help me to resolve this.
I have tried with below code
import { MatBadgeModule } from '#angular/material/badge';
`app-bar-alert.html
<div class="alert-notification">
<button class="mat-icon-button" (click)="navigateTo()">
<mat-icon>notifications_active</mat-icon>
<span class="badge" *ngIf="notificationNumberCount > 0 || null">{{notificationNumberCount}}</span>
</button>
</div>
`app-bar-alert.ts
import { Component, OnInit } from "#angular/core";
import { Router } from '#angular/router';
import { BehaviorSubject, interval, Subscription } from 'rxjs';
import { AlertActions, StoreState } from "../../store";
import { Store } from "#ngrx/store";
import { Actions, ofType } from "#ngrx/effects";
#Component({
selector: "jci-app-bar-alert",
templateUrl: "./app-bar-alert.html",
styleUrls: ["./app-bar-alert.scss"],
})
export class AppBarAlert implements OnInit {
notificationNumberCount: number;
hidden = false;
private subscriptions: Subscription[] = [];
public loading: BehaviorSubject<boolean> = new BehaviorSubject(true);
constructor(private router: Router, protected store: Store<StoreState.IState>, private actions: Actions,) {
this.subscriptions[1] = this.actions.pipe(
ofType<AlertActions.AlertSuccess>(AlertActions.ActionTypes.AlertSuccess))
.subscribe((response: AlertActions.AlertSuccess) => {
this.notificationNumberCount = response.payload.alert.total;
console.log(this.notificationNumberCount);
this.loading.next(false);
});
}
navigateTo() {
this.router.navigateByUrl('/alert/information');
// this.notificationNumberCount = 0;
}
ngOnInit() {
this.subscriptions[0] = interval(30000).subscribe(
(val) => {
this.getAlert(false);
});
this.getAlert(true);
}
private getAlert(isInitialLoad: boolean) {
if (isInitialLoad) {
this.loading.next(true);
}
this.store.dispatch(new AlertActions.AlertRequest());
}
}`

Ionic5: setting a footer on all app pages with a sharedModule and controlling it (on/off) from the app.component

I've created a new app using Ionic5 with a menu. I'm trying to use a Footer on multiple pages (now only on Home page). First I've created a SharedModule and imported in the imports' array of the app.module.ts. I've added the footer component in the declarations' and exports' array of the shared.module.ts. Also I added SharedModule in the imports' array of each page.module.ts, and finally adding <app-footer> in each page.html.
It works as expected, showing the footer in all pages. But now I need to control (on/off) this footer from my app.component, in response to a specific event, for example, when internet is not available (this part is not a problem).
footer.component.ts
import { Component, OnInit } from '#angular/core';
import { FooterService } from 'src/app/footer.service';
#Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss'],
})
export class FooterComponent implements OnInit {
public FooterEnabled: boolean= false;
constructor() { }
ngOnInit() {
}
}
The FooterEnabled variable control if the footer is showed or not and must be modifiable from the app.component
footer.component.html
<div class="footer-conn" *ngIf="FooterEnabled">
Alert!
</div>
sharedfooter.module.ts
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FooterComponent } from '../components/footer/footer.component';
#NgModule({
declarations: [FooterComponent],
imports: [
CommonModule
],
exports: [
FooterComponent, CommonModule
]
})
export class SharedFooterModule { }
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { RouteReuseStrategy } from '#angular/router';
import { IonicModule, IonicRouteStrategy } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { Network } from '#ionic-native/network/ngx';
import { SharedFooterModule } from './shared/sharedfooter.module';
#NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [
BrowserModule,
IonicModule.forRoot(),
AppRoutingModule,
SharedFooterModule
],
providers: [
StatusBar,
SplashScreen,
Network,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}
app.component.ts
import { Component, OnInit } from '#angular/core';
import { Platform } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { Network } from '#ionic-native/network/ngx';
#Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss']
})
export class AppComponent implements OnInit {
public selectedIndex = 0;
public appPages = [
{
title: 'Página 1',
url: 'home',
icon: 'mail'
},
{
title: 'Página 2',
url: 'pagina2',
icon: 'paper-plane'
},
{
title: 'Página 3',
url: 'pagina3',
icon: 'heart'
}
];
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private network: Network
) {
this.initializeApp();
}// Fin constructor
no_internet() {
alert("No internet!")
// In this point make FooterEnabled = true (from the footer component)
}
si_internet() {
alert("Whith internet!")
//In this point make FooterEnabled = false (from the footer component)
}
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
});
}
ngOnInit() {
let disconnectSubscription = this.network.onDisconnect().subscribe(() => {
setTimeout(() => {
if (this.network.type !== 'none') {
this.si_internet();
}
else {
this.no_internet();
}
}, 1000);
});
// watch network for a connection
let connectSubscription = this.network.onConnect().subscribe(() => {
setTimeout(() => {
if (this.network.type !== 'none') {
this.si_internet();
}
else {
this.no_internet();
}
}, 3000);
});
const path = window.location.pathname.split('/')[1];
if (path !== undefined) {
this.selectedIndex = this.appPages.findIndex(page => page.title.toLowerCase() === path.toLowerCase());
}
}
}
home.module.ts (as an example)
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { FormsModule } from '#angular/forms';
import { IonicModule } from '#ionic/angular';
import { HomePageRoutingModule } from './home-routing.module';
import { HomePage } from './home.page';
import { SharedFooterModule } from '../shared/sharedfooter.module';
#NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
HomePageRoutingModule,
SharedFooterModule
],
declarations: [HomePage]
})
export class HomePageModule {}
I've tried with a service imported in the footer.component and app.component.ts that implements observables, but it didn't work. I will appreciate your contributions!!
maybe you can do it like bellow, add input to your footer component :
footer.component.ts
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss'],
})
export class FooterComponent implements OnInit {
#Input() FooterEnabled : string;
//public FooterEnabled: boolean= false;
constructor() { }
ngOnInit() {
}
}
footer.component.ts
<div class="footer-conn" *ngIf="FooterEnabled == 'on'">
Alert!
</div>
on your app.component.ts add new varibale status:
no_internet() {
alert("No internet!")
this.status = 'on'
}
si_internet() {
alert("Whith internet!")
this.status = 'off'
}
put it on app.component.html as :
<app-footer FooterEnabled="status" ></app-footer>

Cannot read property 'setFocus' of undefined in ion-searchbar (ionic 4)

I having the problem of cannot read property setFocus of undefined in ion-searchbar (ionic 5).
when I display console log. Hope some one will help me.
In my HTML
<ion-toolbar *ngIf="showSearchbar">
<ion-searchbar [(ngModel)]="searchTerm" #autofocus (ionChange)="setFilteredItems()">
</ion-searchbar>
</ion-toolbar>
In my ts file.
import { Component, ViewChild } from '#angular/core';
import { IonSearchbar } from '#ionic/angular';
#ViewChild('autofocus', { static: false }) searchbar: IonSearchbar;
ngOnInit(){ setTimeout(() => this.searchbar.setFocus(), 500); }
You can do the following:
home.page.html
<ion-searchbar #searchbar debounce="500" [(ngModel)]="searchParam"></ion-searchbar>
home.page.ts
import { Component, OnInit, ViewChild } from '#angular/core';
import { IonSearchbar } from '#ionic/angular';
#Component({
selector: 'app-home',
templateUrl: './home.page.html',
styleUrls: ['./home.page.scss'],
})
export class HomePage implements OnInit {
#ViewChild('searchbar', { static: false, read: IonSearchbar }) searchbar: IonSearchbar;
public searchParam = ''
constructor() { }
OnInit() { }
ionViewDidEnter() {
this.searchbar?.setFocus()
}
}
By using this.searchbar?.setFocus() in ionViewDidEnter lifecycle, the searchbar element will have focus each time the page component is loaded. You can use other lifecycles like ionViewWillEnter. Read more about ionic angular lifecycles here: https://ionicframework.com/docs/angular/lifecycle
And other ion-searchbar methods: https://ionicframework.com/docs/api/searchbar#methods
Try to use something like this:
ionViewWillEnter(){
this.searchbar.setFocus()
}
and read this: https://ionicframework.com/docs/angular/lifecycle

How to navigate between pages in ionic 4 & 5?

I had a project that I developed with ionic 3. But I took a break and when I started working again with ionic, I saw the navigation system change in the new versions. My project is a simple project. This project that lists the data in the a array and details about the data appear on a different page.
I was doing this on Ionic 3:
homepage.ts
export class HomePage {
items = [];
constructor(public navCtrl: NavController) {
this.initializeItems();}
initializeItems() {
this.items = [
{
'title': 'John',
'image': '',
'hair': 'black',
},
{
'title': 'Brendon',
'image': '',
'hair': 'blonde',
}];
openNavDetailsPage(item) {
this.navCtrl.push(DetailsPage, { item: item });
}
detailspage.ts
export class DetailsPage {
item;
constructor(params: NavParams) {
this.item = params.data.item;
}
}
NavCtrl and NavParams are no longer available in version 5 (and I think in version 4). I did to navigate from the home page to the next page(ionic 5).
homepage.ts:
toDetailsPage(){
this.router.navigate(['details'])
}
However, I couldn't navigate according to the data on my list. How can I do this according to the next generation version?
app.routing.module.ts (Routing Module)
const itemRoutes: Routes = [
{ path: 'item', component: ItemListComponent},
{ path: 'DetailsPage/:id', component: DetailComponent }
];
homepage.ts file
import { Router } from '#angular/router';
export class HomePage {
constructor(private router: Router)
openNavDetailsPage(item) {
this.router.navigate(['/DetailsPage', { item: item }]);
}
}
.html file
If you directly want to route through html page:
<ion-button routerLink="/DetailsPage">Log In </ion-button>
or
<ion-button [routerLink]="['/DetailsPage', item.id]">Log In </ion-button>
detail.ts file
import { ActivatedRoute, Router } from '#angular/router';
export class DetailComponent implements OnInit {
id: any;
constructor(private route: ActivatedRoute,
private router: Router) { }
ngOnInit() {
this.id = this.route.snapshot.paramMap.get('id');
}
}
In addition to using a JSON
homepage.ts file
this.router.navigate(['/DetailsPage', { item: JSON.stringify(item) }]);
detail.ts file
this.item = JSON.parse(this.route.snapshot.paramMap.get('item'));
one more way
homepage.html
<div *ngFor=""let item of items"> // here items is an array
<button (click)="goToDetail(item)" class="rgt-btn">
<ion-icon slot="icon-only" name="add-circle" ></ion-icon>
</button>
</div>
homepage.ts
import { NavigationExtras, Router } from '#angular/router';
export class HomePage {
constructor(private router: Router)
goToDetail(item){
let navigationExtras: NavigationExtras = item;
this.router.navigate(['/DetailsPage'], navigationExtras);
}
}
DetailsPage.ts
import { Router } from '#angular/router';
export class DetailComponent implements OnInit {
item: any;
constructor(private router: Router) {
if (this.router.getCurrentNavigation()) {
this.item = this.router.getCurrentNavigation().extras;
}
}
}
> OR With the help of service page
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class NavExtrasService {
extras: any;
constructor() { }
public setExtras(data){
this.extras = data;
}
public getExtras(){
return this.extras;
}
}
Let's say I'm navigating from home to detail page, In page A:
this.navExtras.setExtras(extras)
this.router.navigateByUrl('detailPage');
Then in Detail Page, I retrieve the extras this way:
let newData: any;
this.newData = navExtras.getExtras();

Angular 5: ERROR TypeError: Cannot read property 'offset' of undefined

Error:
ERROR TypeError: Cannot read property 'offset' of undefined
I had comic component, was working fine, but I decided to make a child component, and now it's not working.
I have a parent component 'comics', and a child component 'pagination'. The comics are displayed fine, but the pagination is not working.
In the code, the console.log(this.pagination); is returning an array like ('offset': 20, 'count':1)
But pagination.component.html ir returning an error Cannot read property 'offset' of undefined so pagination is empty, has no data. So parent comics.component.ts is not sharing this variable with child.
I tried to declare pagination: Pagination; in pagination.component.ts but pagination is still empty.
So I think I'm declaring something in a wrong mode, or I should declare something I'm not declaring. I searched, and I tried to find what's missing but I did not find anything and it's still not working.
my code:
// file: pagination.ts
export class Pagination {
offset: number;
count: number;
}
// file: /comics/comics.component.ts
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Params } from '#angular/router';
import { Comic } from '../comic';
import { Pagination } from '../pagination';
import { ComicService } from '../comic.service';
#Component({
selector: 'app-comics',
templateUrl: './comics.component.html',
styleUrls: ['./comics.component.css']
})
export class ComicsComponent implements OnInit {
comics: Comic;
pagination: Pagination;
constructor(
private route: ActivatedRoute,
private comicService: ComicService
) {}
ngOnInit() {
}
getComics(): void {
const offset = +this.route.snapshot.paramMap.get('offset');
this.comicService.getComics(offset, 20)
.subscribe(
result => {
this.comics = result['data']['results'];
console.log(this.comics);
this.pagination.offset = result['data']['offset'];
this.pagination.count = result['data']['count'];
console.log(this.pagination);
}
);
}
}
// file: /pagination/pagination.component.ts
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Params } from '#angular/router';
import { Pagination } from '../pagination';
#Component({
selector: 'app-pagination',
templateUrl: './pagination.component.html',
styleUrls: ['./pagination.component.css']
})
export class PaginationComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
// file: comics/comics.component.html
<div *ngFor="let comic of comics">
<h5>{{comic.title | uppercase}} </h5>
</div>
<app-pagination></app-pagination>
// file: pagination/pagination.component.html
<div>
<h5>{{pagination.offset}}</h5>
<span>{{pagination.count}}</span>
</div>
Versions used:
Angular CLI: 1.7.3
Node: 8.9.4
OS: darwin x64
Angular: 5.2.8
Given that these two components are in a parent-child relationship, your best option is to simply define offset and count as input properties for the child component, and pass them from the parent, like this:
comics.component.ts
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Params } from '#angular/router';
import { Comic } from '../comic';
import { Pagination } from '../pagination';
import { ComicService } from '../comic.service';
#Component({
selector: 'app-comics',
templateUrl: './comics.component.html',
styleUrls: ['./comics.component.css']
})
export class ComicsComponent implements OnInit {
comics: Comic;
offset;
count;
constructor(
private route: ActivatedRoute,
private comicService: ComicService
) {}
ngOnInit() {
}
getComics(): void {
const offset = +this.route.snapshot.paramMap.get('offset');
this.comicService.getComics(offset, 20)
.subscribe(
result => {
this.comics = result['data']['results'];
console.log(this.comics);
this.offset = result['data']['offset'];
this.count = result['data']['count'];
}
);
}
}
pagination.component.ts
import { Component, OnInit, Input } from '#angular/core';
import { ActivatedRoute, Params } from '#angular/router';
import { Pagination } from '../pagination';
#Component({
selector: 'app-pagination',
templateUrl: './pagination.component.html',
styleUrls: ['./pagination.component.css']
})
export class PaginationComponent implements OnInit {
#Input() offset;
#Input() count;
constructor() { }
ngOnInit() {
}
}
comics.component.html
<div *ngFor="let comic of comics">
<h5>{{comic.title | uppercase}} </h5>
</div>
<app-pagination [offset]="offset" [count]="count"></app-pagination>
pagination.component.html
<div>
<h5>{{offset}}</h5>
<span>{{count}}</span>
</div>