Home > Back-end >  problem binding pipe async inn custom directive input
problem binding pipe async inn custom directive input

Time:10-18

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.

  • Related