serge
serge

Reputation: 15239

Angular component attribute without value nor square brackets

I need to add the isBold property to an Angular component

<child-component isBold />

Remark the following

the code bellow breaks the first requirement

@Input() isBold: boolean = true

Is there a way, in Angular 13, to do it without writing additional helper functions?

I note I read the similar question here, but the selected solution needs additional code or Material usage I have not.

Upvotes: 1

Views: 944

Answers (2)

Amer
Amer

Reputation: 6706

To achieve that, I would suggest creating your own coercion functions/types similar to the Angular CDK functions/types (If you don't need to add the @angular/cdk to your project dependencies), which are very easy to implement than creating a directive for each Input:

For example, the Boolean Property from @angular/cdk/coercion:

/**
 * Type describing the allowed values for a boolean input.
 * @docs-private
 */
export type BooleanInput = string | boolean | null | undefined;

/** Coerces a data-bound value (typically a string) to a boolean. */
export function coerceBooleanProperty(value: any): boolean {
  return value != null && `${value}` !== 'false';
}

Then you can use them directly in any component, like the following:

@Input()
get isBold(): boolean {
  return this._isBold;
}
set isBold(isBold: BooleanInput) {
  this._isBold = coerceBooleanProperty(isBold);
}
private _isBold = false;
<child-component isBold />
<child-component [isBold]="parentBooleanOrFunction" />

You can find the full list of Angular CDK coercion functions/types under angular/components/src/cdk/coercion/: https://github.com/angular/components/tree/main/src/cdk/coercion

UPDATE:

If you don't want to use any helper functions/types, you can handle it like the following as mentioned in Deprecated APIs and features at Angular 13:

@Input() get isBold(): boolean {
  return this._isBold;
}
set isBold(value: boolean | '') {
  this._isBold = value === '' || value;
}
private _isBold = false;

When a getter/setter pair is being used for the input it may be desirable to let the setter accept a broader set of types than what is returned by the getter, for example when the setter first converts the input value. However, until TypeScript 4.3 a getter/setter pair was required to have identical types so this pattern could not be accurately declared.

Since TypeScript 4.3 the limitation has been removed; setters can now accept a wider type than what is returned by the getter. This means that input coercion fields are no longer needed, as their effects can be achieved by widening the type of the setter.

Read more about "Input setter coercion" deprecation in Angular 13 here: https://angular.io/guide/deprecations#input-setter-coercion

Upvotes: 1

Antoniossss
Antoniossss

Reputation: 32517

You can use directive to do that but it will not work as an ordinary @Input

Working examople on stackblitz

Create input directive

@Directive({
  selector: '[isBold]'
})
export class IsBoldDirective {

  @Input() isBold:boolean;

  constructor() { }

  ngOnInit(){
    if(this.isBold!==false){
      this.isBold=true; // defaults to true
    }
  }
}

Inject it into the component

@Component({
  selector: 'app-testing',
  templateUrl: './testing.component.html',
  styleUrls: ['./testing.component.css']
})
export class TestingComponent {
  constructor(@Self() @Optional() private isBold:IsBoldDirective) { }
  get value(){
    return this.isBold?.isBold ?? true; // this 'true' is the default value if isBold is not there
  }
}

and use it like this

Absence: <app-testing></app-testing>
Presence: <app-testing isBold></app-testing>
Set value: <app-testing [isBold]="false"></app-testing>

with result

Absence:

true
Presence:

true
Set value:

false 

Upvotes: 0

Related Questions