i have a little big problem!
i have custom directive that put hidden attribute depending of an input entrance.
import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core';
@Directive({
selector: '[ticketingPrimeCreateTicketButtonActive]'
})
export class CreateTicketButtonActiveDirective implements OnInit {
@Input() ticketingPrimeCreateTicketButtonActive: string;
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
ngOnInit() {
console.log(this.ticketingPrimeCreateTicketButtonActive);
if (!(this.ticketingPrimeCreateTicketButtonActive === 'Administrador' || this.ticketingPrimeCreateTicketButtonActive === 'Comercial')) {
this.renderer.setAttribute(this.elRef.nativeElement, 'hidden', 'true');
}
}
}
This directive if working because when i hardcode the entrance it works ok like this:
<button [ticketingPrimeCreateTicketButtonActive]="'Administrador'" [ngClass]="{ addInSmall: isSmallOrXSmall() }" (click)="addTicket()">
<span > add_to_photos </span>
</button>
then problem is that that string i have to take from a database.
i tried to create an observable and use pipe async but it allways takes null. the remarkable code of component:
export class TicketingKanbanComponent implements OnInit, OnDestroy, AfterViewInit {
public activeUserRoleNameSubject: BehaviorSubject<string>;
public activeUserRoleName$: Observable<string>;
....
}
async ngOnInit(): Promise<void> {
const roleUid = (await
this.userBaseGesRoleRepository.findOneByActiveUser().toPromise()).metadata.roleUid;
this.role = (await this.roleRespository.findOne(roleUid).toPromise()).metadata.name;
this.activeUserRoleNameSubject = new BehaviorSubject<string>(this.role)
this.activeUserRoleName$ = this.getActiveUserRoleName$();
....
}
private async getActiveUserRoleName() {
console.log(this.role);
this.activeUserRoleNameSubject.next(this.role);
}
private getActiveUserRoleName$(): Observable<string> {
return this.activeUserRoleNameSubject.asObservable();
}
when i pass the parameter like this:
<button [ticketingPrimeCreateTicketButtonActive]="(this.activeUserRoleName$ | async )" [ngClass]="{ addInSmall: isSmallOrXSmall() }" (click)="addTicket()">
<span > add_to_photos </span>
</button>
the directive allways recive null!
i know that if have to be something of the lifecicle but i have try all that i believe that should works its says NO NO NO to me
EDIT:
using various console.logs, i've seen that the prblem is that it throws th directive before the ngoninit take the value from DDBB.
but i dont know how to make it before.
EDIT 2 (FIXED):
i finally make it works making the DDBB consult inside of the directive.
but i still dont if it should be possible resolve the consult of the database in the component before load the template, ecause in the constructor i cannot use async await.
CodePudding user response:
another solution would have been using resolvers to delay your component. when it hit the ngOnInit the value would be already fetched from database. Take a look to Angular resolvers
CodePudding user response:
From what I can tell, your problem is clearly lifecycle of component and directive building.
Directive is created and takes the value from it's input ticketingPrimeCreateTicketButtonActive
at that time, which since it's a database fetch, will most likely be null. It will continue to listen to this value, but will not execute the behaviour you want and defined in ngOnInit because this lifecycle event only happens once.
I can see at least 2 possible ways of solving this.
First
You can drop the use of a custom directive and rely on [hidden]="logicHere"
.
It's not a very common practice to manipulate HTML through the use of Renderer.
It would look something like this:
<button [hidden]="!((this.activeUserRoleName$ | async ) === 'Administrator' || (this.activeUserRoleName$ | async ) === 'Comercial')" [ngClass]="{ addInSmall: isSmallOrXSmall() }" (click)="addTicket()">
<span > add_to_photos </span>
</button>
Or just replace the logic inside hidden with a custom function.
Second
You can continue with your custom directive logic, but you need to change how the @Input
is managed.
Angular has a good page explaining this: https://angular.io/guide/component-interaction
It would turn out to be something like this:
import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core';
@Directive({
selector: '[ticketingPrimeCreateTicketButtonActive]'
})
export class CreateTicketButtonActiveDirective implements OnInit {
@Input()
get ticketingPrimeCreateTicketButtonActive() {
return this._ticketingPrimeCreateTicketButtonActive;
};
set ticketingPrimeCreateTicketButtonActive(ticketingPrimeCreateTicketButtonActive: string){
this._ticketingPrimeCreateTicketButtonActive = ticketingPrimeCreateTicketButtonActive;
this.customFunction();
}
private _ticketingPrimeCreateTicketButtonActive = '';
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
customFunction() {
if (!(this._ticketingPrimeCreateTicketButtonActive === 'Administrador' || this._ticketingPrimeCreateTicketButtonActive === 'Comercial')) {
this.renderer.setAttribute(this.elRef.nativeElement, 'hidden', 'true');
} else {
this.renderer.setAttribute(this.elRef.nativeElement, 'hidden', 'false');
}
}
}
Here the customFunction
is invoked each time the value of the @Input
changes.