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

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

Related

Vue TypeError: this is undefined

Why this is undefined here? On logout click this is the error shown in the browser console TypeError: this is undefined
<script lang="ts">
import Vue from "vue";
import { getModule } from "vuex-module-decorators";
import Component from "vue-class-component";
import AuthModule from "#/store/auth";
import Store from "#/store";
const authModule = getModule(AuthModule, Store);
#Component({})
export default class App extends Vue {
mounted() {
console.log("App mounted");
}
onLogoutClick() {
authModule.logout().then(function() {
this.$router.push("/login");
});
}
}
</script>
try this.
methods: {
onLogoutClick() {
let self = this
authModule.logout().then(function() {
self.$router.push("/login");
});
}
Using an arrow function to the anonymous function solves this. As arrow functions bind this to the lexical scope's this (in this case onLogoutClick's this).
onLogoutClick() {
authModule.logout().then(() => {
this.$router.push("/login");
});
}

How to use the Capacitor Browser API

I am currently porting my project to Ionic 4 and wanted to replace the Cordova InAppBrowser with the Capacitor browser but with little success so far...
This is my page:
import { Component, OnInit } from '#angular/core';
import {Plugins} from '#capacitor/core';
const { Browser } = Plugins;
#Component({
selector: 'app-srd',
templateUrl: './srd.page.html',
styleUrls: ['./srd.page.scss'],
})
export class SrdPage implements OnInit {
constructor() {
}
async ngOnInit() {
const url = 'http://capacitor.ionicframework.com/';
await Browser.open({'url': url});
}
}
There is no console output and the page stays blank.
Any ideas what went wrong?
This should work.
Remove the quote around the first url:
import { Component, OnInit } from '#angular/core';
import {Plugins} from '#capacitor/core';
const { Browser } = Plugins;
#Component({
selector: 'app-srd',
templateUrl: './srd.page.html',
styleUrls: ['./srd.page.scss'],
})
export class SrdPage implements OnInit {
constructor() {
}
async ngOnInit() {
const url = 'http://capacitor.ionicframework.com/';
await Browser.open({'url': url});
}
}
import { Browser } from '#capacitor/browser';
async openLink(Url){
await Browser.open({ url: Url });
}
const url = 'http://capacitor.ionicframework.com/';
await Browser.open({url: url});

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>

How to use extends for this angulr2 scenario?

In my application core component is root component, In this page header , sidemenu designs are existed,
SideMenu is dynamic, so I write logic of the code in other sidemenu component,
Now I want to use the functionality of sidemenu in core component, I think it is
done by using extends,
My componets are
core component
import { Component, OnInit } from '#angular/core';
import {SidemenuComponent} from './sidemenu.component';
#Component({
selector: 'my-admin',
templateUrl: '../views/admin-header.html'
})
export class CoreComponent extends SidemenuComponent {
constructor( private router: Router) {
}
}
sidemenucomponent
import {OnInit } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/toPromise';
import {MenuService} from './core.service';
export class SidemenuComponent implements OnInit{
userroleId : any;
roleName: string;
menuItems:any;
constructor(private http: Http,private MenuService:MenuService) {
this.userroleId = localStorage.getItem("roleId")
}
ngOnInit(): void {
this.getSideMenu();
}
getSideMenu () {
if( this.userroleId == 1) {
this.MenuService.getAdminMenu().subscribe(menuItems => this.menuItems= menuItems, error => console.log(error));
}
if(this.userroleId == 2){
this.MenuService.getpractitionerMenu().subscribe(menuItems => this.menuItems= menuItems, error => console.log(error));
console.log('ss')
}
}
}
MenuService
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class MenuService {
constructor(private http: Http) {}
public getAdminMenu(): Observable<any> {
return this.http.get("./app/core/adminsidemenu.json")
.map((res:any) => res.json())
.catch(this.handleError);
}
public getpractitionerMenu(): Observable<any> {
return this.http.get("./app/core/practitioner.json")
.map((res:any) => res.json())
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message || error);
}
}
Here I am unable extends the sidemenu component in core component.
I am getting error in my console:
Constructors for derived classes must contain a 'super' call.
Please help me
It appears you may be missing the #Component decorator on the SidemenuComponent.
Here's a Plunker with a simple extended Component, and here's a good article on it: Component Ineritance.
You need to call the constructor of the parent class in your child-class:
#Component({
selector: 'my-admin',
templateUrl: '../views/admin-header.html'
})
export class CoreComponent extends SidemenuComponent {
constructor( private router: Router, http: Http, menuService:MenuService) {
super(http,menuService);
}
}

Set focus on an input with Ionic 2

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.