Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should Angular2 @Inputs be public or can/should we have a stricter API by making them private?

I am using Angular2 with Typescript

Suppose I have the following in my app component's template:

... 
<coffee-cup [coffee]=""
...

My coffee-cup component:

@Component({
  selector: 'coffee-cup',
  ...
})
export class CoffeeCup {

  @Input() 
  public coffee = 0;

}

I am currently unsure of what my Input should look like. It could look like this:

@Input()
public coffee = 0;

Or

@Input()
private coffee = 0;

I am currently leaning towards making the member variable coffee private.

  • I want to define a clear public API for the component
  • I only want to expose setting the coffee property through the template
  • I don't presently have any reason to allow coffee to be read or set directly from the parent component. If the need arises, I can drop the private modifier.

The way that I am viewing a component is that there are two separate APIs to interact with it:

  1. The template API, which consists of the @Inputs and @Outputs
  2. The Typescript API which consists of all of the public properties and methods

I haven't checked what occurs in the following situation, however, it may change the answer:

  • Suppose the coffee member is public. If my appComponent gets access to CoffeeCup using @ViewChild and sets the coffee member, will the lifecycle hooks (like ngOnChange) fire?

To restate the question: Should Angular2 @Inputs be public or can/should we have a stricter API by making them private?

like image 315
sixtyfootersdude Avatar asked Jul 19 '16 22:07

sixtyfootersdude


People also ask

Can private variables be accessed by HTML Angular?

A Question came up on StackOverflow about accessing private variables on an Angular Component class inside an Angular template. How do you do it? The short answer is you can't.

What does @input do in Angular?

Use the @Input() decorator in a child component or directive to let Angular know that a property in that component can receive its value from its parent component. It helps to remember that the data flow is from the perspective of the child component.

What is input decorator Angular?

@Input Decorator. @Input is a decorator to mark a property as an input. @Input is used to define an input property, to achieve component property binding. @Inoput decorator is used to pass data (property binding) from parent to child component.


1 Answers

First, from an API design perspective @Input implies public. This is also true from angular perspective, these decorators describes the interface to interact with a component.

The @Input decorator, or any other meta decorator is used by angular in a way of letting angular know about your intent and have better understanding of the template and it's relations with the component class.

It's also used, in some cases, by the change detection engine. For example, @Input is a field tracked by the change detection, it hints to the CD engine that this property should be monitored.

Having a private property with an @Input decorator shouldn't have any effect in runtime. This modifier is virtual and it's gone after TypeScript to JavaScript compilation.

However, there are some effects that might occur depending on your environment:

A great benefit of having TypeScript and metadata in general is having a smart IDE which means that the IDE can help you while you code. Having a private property might or might not effect that depending on the implementation of each IDE. Having @Input on a property will result in the IDE showing you that property on an intellisense window when you write the HTML markup for that component.

Another risk factor is future support for minification/uglification in typescript. Private properties, as the name suggests, are used inside the class nowhere else. This trait means the complier can change the names of private properties so they take less bytes, it also makes them "more private" as the identifier might change from build to build. For example: private mySpecialProperty: string after minification will be p1 and the compiler will change all references to this identifier in the class to match p1. So, having this today will work but in the future it might limit build features.

Another point to consider is that while angular does not care about modifiers your compiler does, so dynamic component creation will be limited. In other word, creating components in html markup will work without any issues but dynamically creating components using the ComponentResolver -> ComponentFactor will be limited since you won't be able to assign those inputs to the instance of your components using code. If you're not planning to do so, you're good.

If you're building components to be used by others, public modifier is mandatory for @Input/@Output. The users of you'r component should be able to dynamically create your components.

This also answers the question about accessing these properties on a parent/child component getting reference to the coffee component. Binding will be possible via template markup only. For example, you won't be able to manually register to EventEmitters registered on the coffee component. This is sometimes required, see THIS scenario as one example.

As for lifecycle hooks, it should not have any effect since angular does not check the type but does an existance check.

So, to sum up, in most use cases you shouldn't have any issues but as you'r app progresses you might tackle some issues, or not. You might also have to opt-out of advanced minification features in the future...

like image 121
Shlomi Assaf Avatar answered Sep 20 '22 12:09

Shlomi Assaf