60

I want to implement something similar with angular-permisssion. And with requirement to control the element's existance, I need to use angular structural directive.

At the beginning, i think such syntax would work:

<h2 *permissionIf [permissionIfExcept]="'Read'">Except</h2>

However, it doesn't work that way.

Moreover, the offical guide only teach you how to write custom structural directive with single input. With multi-inputs, some third-party tutorials involve a bit. But that's using the angular template micro-syntax to achieve data binding. Then one problem occurs: template syntax doesn't support pure key-value inputs:

<h2 *permissionIf="except: map.except;only: 'test'">Except</h2>

It expands into this(which is illegal):

<h2 template="permissionIf except: map.except;only: 'test'">Except</h2>

A stupid temporary solution is add a useless variable declaration.

<h2 *permissionIf="let i;except: map.except;only: 'test'">Except</h2>

Another inconvenient way is to use template element to wrap the code.

<template permissionIf [permissionIfExcept]="'Read'">
  <h2>Except</h2>
</template>

The above all are not accepetable enough. But I can't find a bette way to resolve it.

Hope some guys can give some suggestion:).

Jon Jaques
  • 4,262
  • 2
  • 23
  • 25
e-cloud
  • 4,331
  • 1
  • 23
  • 37

3 Answers3

109

The input names need all to be prefixed with the selector of the directive, followed by the input name capitalized (i.e. permissionIfExcept). Example:

  @Directive({
    selector: '[permissionIf]'
  })
  export class PermissionIfDirective implements AfterContentInit {

    private _permissionIf:string[];
    @Input() 
    set permissionIf(value: string[]) {
      this._permissionIf=value;
      console.log('permissionIf: ', value);
    }

    private _except:string;
    @Input() 
    set permissionIfExcept(value: string) {
      this._except = value;
      console.log('except: ', value);
    }
  }

To use them with the '*' syntax:

<div *permissionIf="permissions;except:'Read'"></div>

Note here you're using the name following the prefix uncapitalized (i.e. except). Also note the : in the assignment.

The explicit syntax (using template) would look like this:

<template [permissionIf]="permissions" [permissionIfExcept]="'Read'">
  </div></div>
</template>

but with <ng-container> it could look like

<ng-container *permissionIf="permissions;except:'Read'">
  <div></div>
</ng-container>

Plunker example

See also the source of NgFor as an example.

Dennis Vash
  • 50,196
  • 9
  • 100
  • 118
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • you answer is similar to my first solution which means the `permissions` is useless. – e-cloud Jan 23 '17 at 03:56
  • Sorry, I don't understand what you mean with useless. I just tried to demonstrate how to pass values to multiple inputs. I don't see more than one solution. There is only this one way. – Günter Zöchbauer Jan 23 '17 at 04:04
  • "useless" means that identifier is for avoiding syntax error, there is no actual usage of it. – e-cloud Jan 23 '17 at 05:48
  • 1
    sorry too. In fact, i only need to pass value to those explicit keys like `except: map.except;only: 'test'`. There is no need pass value to the core directive. Perhaps there is no more solution indeed. – e-cloud Jan 23 '17 at 05:49
  • 1
    Ok, now I understand what you mean with your first example. Why don't you change the selector to `[permissionIfExcept]` and use it like `*permissionIfExcept="'Read'"`? I find it usually more convenient to make the selector and the main input names match. You can also make the selector `[permissionIf],[permissionIfExcept]` to be able to just use `*permissionIf` (with no params at all) or alternatively `*permissionIfExcept="'Read'"` (as shown before) – Günter Zöchbauer Jan 23 '17 at 08:08
  • Because i want *multi-inputs*. If i use `*permissionIfExcept="'Read'"`, i can't pass other constraint(s) like `only` in my example. Attribute directive can access other properties of the element, but can't control element structure. That's a dilemma. Aonther solution is pass an object to a single input, but it's not a declarative way. – e-cloud Jan 23 '17 at 12:17
  • @e-cloud did you find the solution for your problem. facing the same problem – Ravin Singh D Feb 20 '18 at 15:06
  • 4
    Thank you, that answer really helped, and there's no much documentation regarding that question. – Juangui Jordán Mar 13 '18 at 08:34
  • 1
    Thank you @GünterZöchbauer. As mentioned by Juangui this really helps especially since the Angular doc is so minimal on this topic. – Gaurav Nov 05 '18 at 06:27
  • Günter Zöchbauer's solution seems to only work with standard, but not structural directives. I was only able to pass multiple values via an object, like in answer by Jon Jaques below: https://stackoverflow.com/a/58846834/1691640 – ilya.chepurnoy Dec 07 '20 at 10:53
7

@Günter Zöchbauer answer is almost correct.

Actually right now to make his answer working you need to explicitly rename the secondary @Input name. So it should be:

@Input("permissionIfExcept") 
set permissionIfExcept(value: string) {
  this._except = value;
  console.log('except: ', value);
}
6

Then one problem occurs: template syntax doesn't support pure key-value inputs:

True

A stupid temporary solution is add a useless variable declaration.

I think you are using this in a way it was not meant to be.

From the docs:

The microsyntax parser title-cases all directives and prefixes them with the directive's attribute name, such as ngFor. For example, the ngFor input properties, of and trackBy, become ngForOf and ngForTrackBy, respectively. That's how the directive learns that the list is heroes and the track-by function is trackById.

microsyntax translation

https://angular.io/guide/structural-directives#microsyntax-examples

Bottom line is in the context of your question, the microsyntax accepts "expression", followed by optional "keyed expression"s and I'm afraid those are your only options.

One could of course pass an object as the first expression—similar to ngIf—, the difference being you can teach your directive how to evaluate the expression:

*permissionIf="{ only: 'whatever', except: ['things', 'stuff'] }"

Jon Jaques
  • 4,262
  • 2
  • 23
  • 25