Angular - Is it possible to destroy component (not dynamically created)? - google-maps

I am using Angular 2 google map one of our application. We are loading marker every 5 secs using Sockets. The problem is need to remove previous marker when new marker receive from socket. There is no proper documents in Angular map official site. So thought of destroy components from our app components. And we got all the child components find the following code.
import { Component, OnInit, ViewChild, ViewChildren, QueryList } from '#angular/core';
import { Socket } from 'ng2-socket-io';
import { Marker } from './google-map';
import { SebmGoogleMapMarker } from 'angular2-google-maps/core';
#Component({
selector: 'app-google-map',
templateUrl: './google-map.component.html',
styleUrls: ['./google-map.component.scss'],
providers: [SebmGoogleMapMarker]
})
export class GoogleMapComponent implements OnInit {
public lat: number = 51.678418;
public lng: number = 7.809007;
public markers: Marker[] = [];
#ViewChildren(SebmGoogleMapMarker) SebmGoogleMapMarkers: QueryList<SebmGoogleMapMarker>;
constructor(private socket: Socket) { }
ngOnInit() {
this.socket.on('markers', this.setMarkers);
}
setMarkers = (data: Marker[]) => {
this.removeMarkers();
for (let marker of data) {
var model = new Marker(marker);
this.markers.push(model);
}
}
removeMarkers() {
if (this.SebmGoogleMapMarkers.length > 0) {
this.SebmGoogleMapMarkers.forEach((ele) => {
ele.ngOnDestroy();
});
}
}
}
<div class="col-lg-12">
<sebm-google-map [latitude]="lat" [longitude]="lng">
<sebm-google-map-marker *ngFor="let marker of markers" [latitude]="marker.latitude" [longitude]="marker.longitude">
</sebm-google-map-marker>
</sebm-google-map>
</div>
We got all child components but still reference are there SebmGoogleMapMarkers query list. Is there anyway to destroy component angular way?
Actually here my concern is this.SebmGoogleMapMarkers.length is increasing every 5 sec. what i am feeling is application performance will be reduce.
Solution: I made silly mistake forgot to make marker array empty before pushing.

As far as I know there is no way to destroy a component that was not dynamically added. You can use *ngIf to remove a component though:
<sebm-google-map-markers *ngIf="show">
You can also create your own *ngIf variant that for example includes the logic to remove the component when not needed anymore. Creating such a structural directive is quite simple (https://angular.io/docs/ts/latest/guide/structural-directives.html)

Related

How can I using angular when I want to click?

I'm a beginner to learn this component. And I going to try to create a online book shop like this link https://www.fishpond.com.hk/Books , and I'm facing some problem now. Could you guys please help me? And first in my website, it have backend and frontend, and now I can show all book , insert new book, and now I want to know how can I do when I click the title of the book and what I have to do to transfer to get that book detail.
How can I click the title and I will see those book detail on the book-details page. And I hope get the isbn code to find that book.
My code here
HTML
<h1>All Books</h1>
<ul *ngIf="books" class="info">
<li *ngFor="let book of books">
<p><img [src]="book.image" class="bookimg"></p>
<a routerLink="/book-detail"><h3>{{ book.title }}</h3></a>
<p>{{ "By "+ book.author }}</p>
<span class="price-block" >{{ "HK$" + book.price}}</span>
</li>
</ul>
ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
h1Style: boolean = false;
books: Object;
constructor(private data: DataService) {}
ngOnInit() {
this.data.getBooks().subscribe(data=> {
console.log({data}); //show data
this.books = data
//console.log(this.books);
})
}
And I have created a component for book-detail
<h1>Book-detail</h1>
<div *ngIf="books" class="book-detail-block">
<div *ngFor="let bookrecord of books" class="book-record">
<h1>{{bookrecord.title}}</h1>
<p>{{bookrecord.image}}</p>
<p>{{bookrecord.author}}</p>
<p>{{bookrecord.price}}</p>
<p>{{bookrecord.isbn}}</p>
<p>{{bookrecord.description}}</p>
</div>
</div>
ts
import { Component, OnInit } from '#angular/core';
import { DataService } from '../data.service';
#Component({
selector: 'app-book-detail',
templateUrl: './book-detail.component.html',
styleUrls: ['./book-detail.component.scss']
})
export class BookDetailComponent implements OnInit {
h1Style: boolean = false;
books: Object;
constructor(private data: DataService) {}
ngOnInit() {
this.data.getOneBook().subscribe(data => {
this.books = data
console.log(this.books);
})
}
}
I can get the data in the service but how can I implement in show component
export class BookDetailComponent implements OnInit {
h1Style: boolean = false;
books: Object;
constructor(private data: DataService) {}
ngOnInit() {
console.log('-0-----' + this.books)
this.data.getBooks().subscribe(data=> {
console.log({data}); //show data
this.books = data
})
}
}
enter image description here
I may be late to the issue and you've already solved it but in the off-chance that you havent i'll hopefully provide some guidance.
What you want for accessing an individual item when clicking the title is to use a click-event on the tag representing the title, probably the h1-tag. It looks something like this:
<h1 (click)="getBookDetail(bookrecord)">{{bookrecord.title}}</h1>
The line above hooks up a clickevent to a function called getBookDetail and takes the individual object as a parameter, as of now this will render an error saying there's no function named getBookDetail(), so you'll need to create it in the component.ts file that corresponds to the view probably the homecomponent.ts and it looks like this:
getBookDetail(book: any) {
console.log(book);
}
If you now reload the application and click the title you'll see the individual book-object being logged in the console. What you'll need after is to set up routing if you havent already (you get the question to include app-routes module when creating the project) and to create a path for the bookDetailComponent. If you have routing in place add an array of routes as following:
const routes: Routes = [
{ path: '', redirectTo: '/books', pathMatch: 'full' },
{ path: 'books', component: HomeComponent},
{ path: 'book/:id', component: BookDetailComponent },
];
The first item in the routes array will match any route that is empty like localhost:4200 and redirect to the HomeComponent, and the other two arrays are routes for the individual component.
And if you dont have a routing-module i suggest you follow angulars tutorial for adding in-app navigation: https://angular.io/tutorial/toh-pt5.
And for making the click on the title actually navigate to the bookcomponent you first need to inject the Router class, so if you go back to the homecomponent you'll see an constructor (if not create one), add the router class like:
constructor(private router: Router) {}
And in the getBookDetail function you can remove the console.log and add:
getBookDetail(book: any) {
// Wrong path this.router.navigateByUrl('/book/' + book.isbn);
this.router.navigateByUrl('/bookdetail/' + book.isbn);
}
All that you need now is to get the isbn from the url and fetch one book with that identifier, but i'll leave those steps to you, everything you'll need is in the angular tutorial i linked previously. Good luck and if anything is confusing or you have any questions feel free to ask.
Added a stackblitz showing my idea:
https://stackblitz.com/edit/angular-ivy-c2znl2?file=src/app/books/books.component.ts

How to access _factories property from ComponentFactoryResolver in Angular9 which was available in Angular7?

I used ComponentFactoryResolver to access all the entry component factories and then add routes of those components dynamically in Angular 7.
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
var factories = this.componentFactoryResolver['_factories']
let route: Route = {
path: MyPath,
component: factories[0].factory[1].componentType
};
this.router.resetConfig(config);
I updated my project to Angular 9 and now _factories are not available in ComponentFactoryResolver.
var factories = this.componentFactoryResolver['_factories']
It returns undefined in Angular 9. It will be great help if someone can suggest some way to access all the entry component list on the go. The problem is that I have don't any information available about the entry components from which I may recompile the components through Compiler and add their routes.
I think a simpler approach might be to import the component and use it directly in place of factories[0].factory[1].componentType. Example:
import { MyComponent } from '#app/my.component';
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
}
let route: Route = {
path: MyPath,
component: MyComponent
};
this.router.resetConfig(config);

What is the right way to use hardware back button in Ionic 4?

i'm creating a ionic 4 app, and i want to use the device's hardware back-button to return at specific page
i'd checked in this page Handling hardware back button in Ionic3 Vs Ionic4 to create my code but it doesn't work
detallesusuario.page.ts
import { Subscription } from 'rxjs';
import { Component, OnInit, AfterViewInit, QueryList, ViewChildren}from '#angular/core';
import { Platform, IonRouterOutlet } from '#ionic/angular';
import { Router } from '#angular/router';
#Component({
selector: 'app-detallesusuario',
templateUrl: './detallesusuario.page.html',
styleUrls: ['./detallesusuario.page.scss'],
})
export class DetallesusuarioPage implements OnInit, AfterViewInit {
#ViewChildren(IonRouterOutlet) routerOutlets: QueryList<IonRouterOutlet> ;
sub:Subscription
constructor(private platform:Platform, private ruta:Router) { }
ngAfterViewInit(): void {
this.backButtonEvent();
}
ngOnInit() {
}
ngOnDestroy() {
this.sub.unsubscribe();
}
backButtonEvent() {
let u=0;
this.sub=this.platform.backButton.subscribeWithPriority(0, () => {
this.routerOutlets.forEach(async(outlet: IonRouterOutlet) => {
console.log('paso');
await this.ruta.navigate(['menu/inicio/tabs/tab1']);
});
});
}
}
When i deploy a app to a device, the button return to the previous page instead to go at the specific page
The hardware back button handling code is found in:
https://github.com/ionic-team/ionic/blob/master/core/src/utils/hardware-back-button.ts#L20
And that is exposed by the platform using platform.backButton:
https://github.com/ionic-team/ionic/blob/f44c17e03bfcd9f6f9375a19a8d06e9393124ac9/angular/src/providers/platform.ts#L21
https://github.com/ionic-team/ionic/blob/f44c17e03bfcd9f6f9375a19a8d06e9393124ac9/angular/src/providers/platform.ts#L48
It looks to me like you are using it correctly. I found this article showing the same technique, and also another forum post.
What looks strange to me is why you are getting IonRouterOutlet involved.
Have you tried it just using the router directly?
backButtonEvent() {
this.sub=this.platform.backButton.subscribeWithPriority(0, () => {
console.log('paso');
await this.ruta.navigate(['menu/inicio/tabs/tab1']);
});
}
Note: I'm assuming your choice of ngAfterViewInit make sense - I haven't double checked this.
However, you should also probably take a detailed look at this issue as it seems there are tons of problems with the hardware back button:
https://github.com/ionic-team/ionic/issues/16611

Cloned value is getting appended to the new template value

I am new to angular. I was trying to clone a set of elements which is working fine but I am not able to get the latest value of the new cloned element when I console it or in the view.
It might be because of a variable called temp which is getting the cloned values. How to get the respective fields values in the console like
example=[{
exp1="aaa",
exp2="bbb",
exp3="tea"
},{
exp1="ddd",
exp2="eee",
exp3="mango"
}]
?
Please refer the below link for the working copy of the code:
https://stackblitz.com/edit/angular-gvwv4g-h2sxnd
You can use it this way
this.example.push(JSON.parse(JSON.stringify(this.temp)));
Please refer the below example
https://stackblitz.com/edit/angular-gvwv4g-effowp
You need to copy your form filled values into a new object, then pushing this new object into your array, then you can reset your object value, so the new added line will be empty.
I have edited your input-overview-example.ts as follow
import { Component , OnInit} from '#angular/core';
import { FormGroup, FormBuilder, FormControl, Validators } from '#angular/forms';
/**
* #title Basic Inputs
*/
#Component({
selector: 'input-overview-example',
styleUrls: ['input-overview-example.css'],
templateUrl: 'input-overview-example.html',
})
export class InputOverviewExample implements OnInit {
// test Start
foods=["mango","tea","apple"]
example= [ ];
temp: any;
//test ends
ngOnInit() {
this.initTemp();
this.example.push(this.temp);
}
testAdd(){
console.log(this.temp)
console.log(JSON.stringify(this.example)+"-------");
this.initTemp();
this.example.push({
exp1: this.temp.exp1,
exp2: this.temp.exp2,
exp3: this.temp.exp3,
});
}
initTemp() {
this.temp = {
exp1:"",
exp2:"",
exp3:""
}
}
}

Ionic2 - invisible tabs - when generated from an observable

My tabs.ts (simpilified) - data used to generated tabs with *ngFor is brought from php backend:
import ...
export interface Group {
id: number;
group: string;
};
#Component( {
template: `
<ion-tabs #myTabs selectedIndex="0">
<ion-tab *ngFor="let tab of userGroups" [root]="page" [rootParams]="tab.id" [tabTitle]="tab.group" tabIcon="pulse"></ion-tab>
</ion-tabs>
`
})
export class GroupsTabsPage {
userGroups: Group[];
page: any = TabStudentsPage;
constructor( public app: App, public api: Api, public navParams: NavParams ) {
this.api.getGroupsList()
.subscribe(
data => {
this.userGroups = data;
},
err => {
this.app.getRootNav().push( LoginPage )
}
);
// ionViewDidEnter() {
// }
}
}
The result is invisible tabs. But when you hover your mouse ovet them, the cursor changes into 'hand' and you can click them. When clicked, the whole tabs bar becomes visible and all works as expected.
When I used #ViewChild to refer to the tabs elements, the interesting thing is that its 'length' property is always 0 (I checked in ionViewDidLoad event). Trying to select one of the tabs programatically also fails - they are like ghosts;)
Also when you place at least one static tab next to *ngFor ones in the template, all *ngFor ones show up but the static is always selected no matter what you select programatically or in selectedIndex property on tabs element.
Any idea guys? I've wasted three days..
that's a known bug, take a look at the element css, the subview's .tabbar has opacity of 0. I've just fixed it with a an override of opacity: 1. Ugly, but works...
Creating ion-tab from observable (dynamically) has some bugs (duplicates, wrong rendering etc) I use a workaround to avoid it, it consist of removing and loading the ion-tabs runtime every time then observable changes.
Parent template:
<div #pluginTabContainer></div>
Parent component:
#ViewChild("pluginTabContainer", {read: ViewContainerRef}) pluginTabContainer:ViewContainerRef;
...
plugins$.subscribe((pluginTabs:Array<PluginTabType>) => { let componentFactory = this.componentFactoryResolver.resolveComponentFactory(PluginTabContainerComponent); this.pluginTabContainer.clear(); this.pluginTabContainertRef = this.pluginTabContainer.createComponent(componentFactory); this.pluginTabContainertRef.instance.data = pluginTabs;
...
ngOnDestroy() { this.pluginTabContainertRef.destroy(); }
Loaded ion-tabs template:
<ion-tabs> <ion-tab *ngFor="let tab of data" [root]="'PluginTabPage'" [rootParams]="tab"></ion-tab> </ion-tabs>
Loaded ion-tabs component (getting parameter):
#Input() data: PluginTabType;
Hope will be helpful for you.
I had a similar issue during development and I was able to solve this by making ngOninit async and calling a timeout to set the selected tab.
view
<ion-tabs #ctrlPanelTabs class="tabs-basic">
<ion-tab *ngFor="let appTab of appTabs" tabTitle={{appTab.name}} [root]="rootPage"></ion-tab>
</ion-tabs>
1) ngOninit is async
2) this.ctrlPanelTabs.select(0); is set inside a timeout function
import { Component, OnInit, ViewChild } from '#angular/core';
import { NavController, Tabs } from 'ionic-angular';
import { AppSettings } from '../../common/app.config';
import { AppTab } from '../../models/app-tab';
import { AppTabService } from '../../services/app-tab.service';
import { PanelTabComponent } from './panel-tab';
#Component({
selector: 'page-control-panel',
templateUrl: 'control-panel.html',
providers: [AppTabService]
})
export class ControlPanelPage implements OnInit {
#ViewChild("ctrlPanelTabs") ctrlPanelTabs: Tabs;
appTabs: AppTab[] = [];
message: string;
rootPage = PanelTabComponent;
constructor(public navCtrl: NavController,
private appTabService: AppTabService) {
console.log("Control Panel Page : Constructor called..");
}
async ngOnInit() {
console.log("Control Panel Page : Entering ngOninit..");
await this.loadAppTabs();
setTimeout(() => {
this.ctrlPanelTabs.select(0);
}, 100);
console.log("Control Panel Page : Exiting ngOninit..");
}
async loadAppTabs() {
console.log("Control Panel Page : Entering loadAppTabs..");
await this.appTabService.getAppTabsHierarchyBySlaveDeviceId(AppSettings.selSlaveDeviceId)
.then((response: any) => {
this.appTabs = JSON.parse(response.result);
console.log(this.appTabs);
console.log("Control Panel Page : Exiting loadAppTabs..");
});
}
}