Is it possible to trigger a function that has a parameter in it like trigger(id) in ngOnInit?
I have a function that I want to trigger on (click) event as well as in ngOnInit so when the page first load it triggers basically the same function because it calls to the same API end-point.
function to be triggered in ngOnInit
getOrdersFromOrigin(id: number): void {
...
}
so in ngOnInit would be something like this maybe? its showing an error "Cannot find name 'id'"
ngOnInit() {
this.getOrdersFromOrigin(id);
}
Id is coming from my ngFor, its an Id of an item which I want to click (click)="getOrdersFromOrigin(id)"
You can handle it if its in route like param. For example if you are editing some record in table by clicking edit button then your app navigate to edit form you can handle it like this:
import { ActivatedRoute, Params, Router } from '#angular/router';
constructor(
private route: ActivatedRoute
) { }
ngOnInit() {
this.route.params.forEach((params: Params) => {
let id = +params['id']; // (+) converts string 'id' to a number
this.getOrdersFromOrigin(id);
});
}
getOrdersFromOrigin(id: number){
//some code
};
The param id is in getOrdersFromOrigin(), so it would not work in the scope of ngOnInit(), my suggestion is that you can declare a property id and marked it as #Input(). then you can use it outside. The codes is below:
export class SameComponent implement OnInit {
#Input() id:number=0; //initialize
getOrdersFromOrigin(id: number): void {
...
}
ngOnInit() { this.getOrdersFromOrigin(id);}
}
Related
In an Angular 11 app, I have a simle service that mekes a get request and reads a JSON.
The service:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Promo } from '../models/promo';
#Injectable({
providedIn: 'root'
})
export class PromoService {
public apiURL: string;
constructor(private http: HttpClient) {
this.apiURL = `https://api.url.com/`;
}
public getPromoData(){
return this.http.get<Promo>(`${this.apiURL}/promo`);
}
}
In the the component, I need to compare the array of products with the array of campaign products (included in the JSON mantioned above) and higlight the promoted products:
export class ProductCardComponent extends DestroyableComponent implements OnInit, OnChanges
{
public promoData: any;
public promoProducts: any;
public isPromoProduct: boolean = false;
public ngOnInit() {
this.getCampaignData();
}
public ngOnChanges(changes: SimpleChanges): void {
this.getCampaignData();
}
public getPromoData() {
this.promoService.getPromoData().pipe(takeUntil(this.destroyed$)).subscribe(data => {
this.promoData = data;
this.promoProducts = this.promoData.products;
let promoProduct = this.promoProducts.find((product:any) => {
return this.product.unique_identifier == product.unique_identifier;
});
if (promoProduct) {
// Update boolean
this.isPromoProduct = true;
}
});
}
}
In the component's html file (template), I have:
<span *ngIf="isPromoProduct" class="promo">Promo</span>
There are no compilation errors.
The problem
For a reason I have been unable to understand, the template does not react to the change of the variable isPromoProduct and the template is not updated, despite the fact that I call the function inside ngOnInit and ngOnChanges.
Questions:
Where is my mistake?
What is a reliable way to update the template?
subscribing to Observable inside .ts file it's mostly not a best practice.
try to avoid it by using async pipe of Angular.
you need to store the observable in the variable and not the data returned from the observable, for example:
// this variable holds the `observable` itself.
this.promoData$ = this.promoService.getPromoData()
and then in the template you can do it like this:
<div *ngIf="promoData$ | async as promoData">
here you can access the promoData
</div>
you can still use pipe() to map the data etc but avoid the subscribe()
The isPromoProduct boolean is not an input. The ngOnChanges gets triggered for changes on your properties that are decorated with the #Input decorator. For your particular case, you can inject the ChangeDetectorRef and trigger change detection manually:
constructor(private cdr: ChangeDetectorRef) {}
// ...
public getPromoData() {
this.promoService.getPromoData().subscribe(data => {
// ...
if (promoProduct) {
// Update boolean
this.isPromoProduct = true;
this.cdr.detectChanges();
}
});
}
You also don't need to manage httpClient subscriptions. The observables generated by a simple get or post request will complete after they emit the response of the request. You only need to explicitly manage the unsubscribe for hot observables (that you create from subjects that you instantiate yourself).
I have a service file auth.service.ts
export class AuthService {
private _isLoggedIn = new BehaviorSubject<boolean>(false);
isLoggedIn = this._isLoggedIn.asObservable()
constructor(private http: HttpClient) {}
//if admin
//setter method
updateAuthenticated(newValue: boolean){
this._isLoggedIn.next(newValue);
}
//for guarding routes
//getter method
isAuthenticated(){
return this.isLoggedIn.subscribe(isLoggedIn =>{
console.log(isLoggedIn);
});
}
On calling the updateAuthenticated() method inside the login.component.ts
this.authService.updateAuthenticated(true);
this.route.navigate(['/admin']);
Changes made to the variable inside the updateAuthenticated() method are not reflected if I check the variable value in isAuthenticated() method which still remains "false" as initialized. However, Checking the value inside the setter method returns the correct value. I tried using BehaviourSubject and can't get it right. Is there a way to get the new value inside the isAuthenticated() method (getter method) or what am I doing wrong with the BehaviourSubject?
The function isAuthenticated() inside the service will not emit the value, because it will return you the whole observable instance. You need to subscribe to the observable isLoggedIn directly inside the component where you need to listen for the BehaviorSubject event.
So instead of putting isAuthenticated() is the service.ts file, go the component that needs the emitted value, and do following:
export class SomeComponent implements OnInit() {
latestValue: boolean;
constructor(private authService: AuthService) {}
ngOnInit() {
this.authService.isLoggedIn.subscribe(isLoggedIn =>{
this.latestValue = isLoggedIn;
// now use this.latestValue within the component
});
}
}
So say i have page one:
This page contains multiple variables and a constructor. it could look something like this:
export class TestPage implements OnInit {
testInt: number;
testString: string;
constructor(private someService: SomeService) {
}
ngOnInit() {
this.testInt = this.someService.getInt();
this.testString = this.someService.getLongText();
}
}
Now when this page loads it correctly sets the values.
Now say that I change page and on this page, I change some of the values in the service.
When I then come pack to this TestPage it hasn't updated the values.
Does this have something to do with caching? or with push state?
How can I make sure that the page is "reloaded" ?
Try using RxJS.
#Injectable({...})
class SomeService {
private _testInt: BehaviorSubject<number> = new BehaviorSubject<number>(0); // initial value 0
setTestInt(value: number) {
this._testInt.next(value);
}
getTestInt(): Observable<number> {
return this._testInt.asObservable();
}
}
#Component({...})
class TestPage implements OnInit {
public testInt: number;
public testInt$: Observable<number>;
private subscription: Subscription;
constructor(private someService: SomeService) {}
ngOnInit() {
// one way
this.testInt$ = this.someService.getTestInt();
// or another
this.subscription = this.someService.getTestInt()
.subscribe((value: number) => {
this.testInt = value;
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
in the HTML:
<p>{{ testInt }}</p>
<p>{{ testInt$ | async }}</p>
If you are subscribing to a Observable, make sure you unsubscribe after the usage (usually On Destroy lifecycle hook).
Async Pipe does that out of the box.
Or try the ionViewWillEnter lifecycle hook.
As you can see in the official documentation:
ngOnInit will only fire each time the page is freshly created, but not when navigated back to the page.
For instance, navigating between each page in a tabs interface will only call each page's ngOnInit method once, but not on subsequent visits.
ngOnDestroy will only fire when a page "popped". link That means that Page is cached, yes. Assigning value On Init will set the value only the first time page is visited and therefore not updated.
This question related to Syntactically anonymous/Arrow Function/add-hoc/factory DP functions:
I have a component which is embedded in the Html.
The component has a click event which is binded to a function. This function content depend on another component which has a reference to this component.
This is the component with the click event:
HTML:
<div (click)="doSomething()">Content.....</div> \\ Should it be with a brackets ?
In the component I just want to define the function signature:
#Component({
selector: 'app-embedded'
})
export class className
{
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) => any; //The function get a boolean parameter as input and return void or any
}
Now this is where the component is embedded:
<div >
<app-embedded #emb></app-embedded>
</div>
This is the component of the container of the embedded component, which has a reference to the embedded component:
#Component({
selector: 'app-container',
})
export class container
{
#ViewChild('emb') private emb: ElementRef;
booleanParam : booelan;
constructor()
{
emb.doSomething = containerFunction(true);
}
containerFunction(booleanParam : boolean)
{
// do something in this context
}
}
The idea is that this embedded component is embedded in many other containers and whenever the click event triggered a function that was set in the doSomething function variable should be executed.
What changes in the code I need to do in order to accomplish this ?
The best way i see of doing this would be to simply use an event emitter and capture the event on the other side? so embedded would have this:
#Component({
selector: 'app-embedded'
})
export class className
{
#Output()
public something: EventEmitter<boolean> = new EventEmitter<boolean>();
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) {
this.something.emit(booleanparams);
}; //The function get a boolean parameter as input and return void or any
}
Then where it is called:
<div >
<app-embedded #emb (something)="doSomething($event)"></app-embedded>
</div>
Other solution that would allow a return
#Component({
selector: 'app-embedded'
})
export class className
{
#Input()
public somethingFunc: (boolean)=>any;
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) {
let w_potato = this.somethingFunc(booleanparams);
//Do whatever you want with w_potato
}; //The function get a boolean parameter as input and return void or any
}
in this case the view would be
<div >
<app-embedded #emb [somethingFunc]="doSomething"></app-embedded>
</div>
I hope this helps! Passing the function or emitting an event will be much more angular than trying to modify an instance of a component. On top of that, a constructor is only called once when Angular starts up so #emb at that time will not be defined to be anything. If you wanted to do it that way you would have to bind yourself in something ngAfterViewInit.
But again, I think that passing it through attributes will be much more angular looking.
Good Luck let me know if this doesn't suit your answer.
I am new to angular2. I have a requirement to call a function when a template loads/initializes. I know how to do this in angular1.x., but I am not able to find out how it can be done in angular-2.
This is how I tried in angular1.x
In html
<div ng-init="getItems()">
//some logic to get my items
</div>
In controller
getItems = function(){
console.log('in the getitems function call');
//logic to get my items from db/localStorage
}
This is how I used ng-init in angular1.x, but there is no ng-init in angular-2?Please help me on this issue.
#Component({
...
})
class MyComponent {
constructor() {
// when component class instance is created
}
ngOnChanges(...) {
// when inputs are updated
}
ngOnInit() {
// after `ngOnChanges()` was called the first time
}
ngAfterViewInit() {
// after the view was created
}
ngAfterContentInit() {
// after content was projected
}
}
See also https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#hooks-overview for the full list
Check lifecycle events of a component https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html . From what you are saying you probably needs ngAfterViewInit
In angular2 you can use component phase ngOnInit it is equal to on-init in angularJS. Here is more information about lifecycle in angular.
Example:
export class PeekABoo implements OnInit {
constructor(private logger: LoggerService) { }
// implement OnInit's `ngOnInit` method
ngOnInit() {
this.logIt(`OnInit`);
}
protected logIt(msg: string) {
this.logger.log(`#${nextId++} ${msg}`);
}
}