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

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

Related

Init pinia state

We use pinia to manage app state. As it's mentioned in title, I'm looking for NuxtServerInit hook analogue for pinia.
A little context: User lands on First page of form; Form calls (f.e.) state.getCountries() to fetch the list of items for one of the select inputs; User selects a country and navigates to Second page, which has to have access to countries list as well; It's ok, when User goes to Second page from the First page; But countries list is empty (obvious) if User refreshes the Second page;
Atm I do like if (state.countries.length === 0) state.getCountries()
But, I believe, it's not a good way
Page 1
<template>
<app-select :items="send.countries" />
</template>
<script>
import { defineComponent } from '#nuxtjs/composition-api'
import { useSend } from '~/store/send'
export default defineComponent({
setup() {
const send = useSend()
send.getCountries()
return { send }
}
}
</script>
Page 2
<template>
<app-select :items="send.countries" />
</template>
<script>
import { defineComponent } from '#nuxtjs/composition-api'
import { useSend } from '~/store/send'
export default defineComponent({
setup() {
const send = useSend()
// if User refreshed Second page, countries is empty list
if (send.countries.length === 0) {
send.getCountries()
}
return { send }
}
}
</script>
store/send.ts
import { defineStore } from 'pinia'
export const useSend = defineStore({
state: () => {
return {
countries: []
}
},
actions: {
getCountries() {
const res = this.$nuxt.$api.countries.get()
this.countries = res.data
}
}
})

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>

How to access a click handler in custom attribute in Aurelia?

Is it possible to access click handler of the element in the custom attribute? I would like to achieve something like this:
<button click.delegate="callSomeMethod()" log-click>Click</button>
where log-click is a custom attribute that wraps the click call and decorates it with some behavior.
A non-working example, but showing what I want to achieve:
class LogClickCustomAttribute {
#bindable click;
attached() {
let originalClick = this.click;
this.click = () => {
console.log('decoreated!');
return originalClick();
};
}
}
The real use case I am trying to achieve is a button that disables itself until promise returned by click handler resolves. Like promise-btn for Angular.
<button click.delegate="request()" disable-until-request-resolves>Click</button>
I have no idea if it is possible to access attributes of standard HTML elements like button within a custom attribute. However this is easy if you create a custom element for buttons:
GistRun: https://gist.run/?id=d18de213112c5f21631da457f218ca3f
custom-button.html
<template>
<button click.delegate="onButtonClicked()">Test</button>
</template>
custom-button.js
import {bindable} from 'aurelia-framework';
export class CustomButton {
#bindable() onClicked;
onButtonClicked() {
if (typeof this.onClicked === 'function') {
this.onClicked();
}
}
}
log-click.js
import {inject} from 'aurelia-framework';
import {CustomButton} from 'custom-button';
#inject(CustomButton)
export class LogClickCustomAttribute {
constructor(customButton) {
this.customButton = customButton;
}
bind() {
let originalOnClicked = this.customButton.onClicked;
this.customButton.onClicked = () => {
console.log('decorated!');
return originalOnClicked();
};
}
}
app.html
<template>
<require from="./custom-button"></require>
<require from="./log-click"></require>
<custom-button on-clicked.call="test()" log-click>Test</custom-button>
</template>
app.js
export class App {
test() {
console.log("The button was clicked.");
}
}
You can add event handlers to the element in the constructor of the custom attribute.
#inject(Element)
export class ClickThisCustomAttribute {
constructor(element) {
element.addEventListener('click', () => {
this.doSomething();
});
}
}
Given how Aurelia attaches event handlers, you're not going to be able to do exactly what you want.
That being said, you could use a simple custom attribute like the one below to log out an event to the console:
log-event.js
import { inject } from 'aurelia-framework';
#inject(Element)
export class LogEventCustomAttribute {
constructor(el) {
this.el = el;
}
attached() {
const eventName = this.value || 'click';
let handler = (e) => console.log('event logged', e);
if (this.el.addEventListener) { // DOM standard
this.el.addEventListener(eventName, handler, false)
} else if (this.el.attachEvent) { // IE
this.el.attachEvent(eventName, handler)
}
}
}
The closest thing to a promise click I made was this:
import { autoinject, bindable } from "aurelia-framework";
#autoinject
export class PromiseClickCustomAttribute {
#bindable({ primaryProperty: true }) delegate: Function;
constructor(private element: Element) {
this.element.addEventListener("click", async () => {
try {
this.element.classList.add("disabled");
this.element.classList.add("loading");
await this.delegate();
}
catch (error) {
console.error(error);
}
finally {
this.element.classList.remove("disabled");
this.element.classList.remove("loading");
}
})
}
}
<div class="ui container">
<h2>Promise Click</h2>
<div class="ui input">
<button class="ui button" promise-click.call="alertLater()">Toast Later</button>
</div>
</div>
alertLater = () => {
return new Promise((resolve) => {
setTimeout(() => {
alert("Promise Resolved");
resolve();
}, 3000);
});
}

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.