mercredi 22 juillet 2020

How to manage separation of concerns with multiple custom directives in Angular?

Feature based visibility in the app
I'm working on a solution in a large-scale angular application to manage the availability of components to users regarding their features that they bought. So the users don't have all the features of the application by default, but only the features that they have purchased.

Feature and permission based visibility in the same app
The app already has a role/permission based visibility management of components. I add new feature based visibility besides it, but to separate the concerns I don't want to mix the new ones logic into the current one.


Say the whole app having 50 features in total and [A-Z] features belongs them:

Feature1 //not a module! conceptual feature that I have mentioned. Think as new dimension besides role/permission
  -PageComponentA   //Permission/role based management in it also
  -PageComponentB   //role based visibility again
Feature2
  -PageComponentC   //role check
  -PageComponentD   //same
  -PageComponentE   //...
...
Feature50
  -PageComponentZ
  -PageComponentX

I have created a FeatureDirective with an input to map components to the items in a feature set.

@Directive({
  selector: '[appFeature]'
})
export class FeatureDirective {
  @Input() itemKey: string;
  featureList: any[] = [];
  constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, private FeaturesService: FeatureService,private applicationRef: ApplicationRef) {
      
  }
  ngOnInit() {
    setTimeout(()=>{   //this one I have found as a solution to set order between multiple directives but not works 
      console.log(`feature directive processing`); 
      this.isFeatureAvailableWithExpiry();
      this.applicationRef.tick();
    }, 1000);  
  }

   isFeatureAvailable() {      
    this.FeaturesService.getToken().pipe(
      mergeMap(token => this.FeaturesService.getFeatures("userxxx", "otheruserinputyyy", token))
    ).subscribe((features: any[]) => {
      let feature = FEATURES.filter(FEATURE => features.some(feature => FEATURE.Code === feature.code))
                            .find(FEATURE => FEATURE.ItemKey === this.itemKey);
      if(feature) {
        if(feature.status) {
          this.viewContainer.createEmbeddedView(this.templateRef);
        } else {
          this.viewContainer.clear();
        }
      }
    }, 
      err => console.log(err) 
    );
  }

Then in a page component I have mapped component to the directive.

//view template starts here...
<ng-template appFeature appPermission [itemKey]="'milkingSettings'" [itemType]="'view'">
...
</ng-template>
@Directive({
  selector: '[appPermission]'
})
export class PermissionDirective {
  @Input() itemKey: string;
  @Input() itemType: string;
  constructor(private element: ElementRef, private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, private ApiService: ApiService) {

  }

  ngOnInit() {
    let userInfo = this.ApiService.getUserInfo();

    (async () => {
      let access = await this.ApiService.getPermission(this.itemKey, userInfo.RoleId);
      if (this.itemType == "view" && access && access.view) {
        this.viewContainer.createEmbeddedView(this.templateRef);
      } else if (this.itemType == "action" && access && access.action) {
        this.viewContainer.createEmbeddedView(this.templateRef);
      } else {
        this.viewContainer.clear();
      }
    })();
  }
}

The problem is like seen above there is also a permission directive which has the same mechanism to show/hide the components according to the role of user logged in. So the question arises is in this setup I need to set a priority against this multiple custom directives since feature check should run first and then role check should happen.

I have found that there is no mechanism to directly set the load/run order of the multiple custom directives, but there are maybe some workarounds like setTimeout(..., 0) or using ApplicationRef.tick() which I'm not sure how exactly.

While thinking about order and the fact that this may not be a best practice since Angular deliberately remove the priority concept, I start to think about whether my solution which is creating a new directive regarding separation of concerns is suitable one or not.

My considerations are:

  1. Provide SoC
  2. (Nearly) All the templates already have permission directive so if I create new feature directive then there will be 100+ template source changes just to add feature directive in my current solution
  3. Managing multiple custom directive priority/order in my current solution

Instead of my solution should I just use the current by putting the feature check logic in PermissionDirective also and change its name FeaturePermissionDirective? So I don't have to manage priority and change all the template source files just to add new directive. Is there any other way to provide SoC but also changing all the templates in this scenario and not using multiple directives?

Aucun commentaire:

Enregistrer un commentaire