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

1

15

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?

Floro answered 19/7, 2016 at 22:14 Comment(3)
You had me fooled for a minute, using that coffee component... I was thinking CoffeScriptVictoria
@ShlomiAssaf - Woop!Floro
TS visibility affects compile time, not run time, decorated component members aren't affected with visibility when they are accessed by Angular. The semantics of private and public isn't very clear in the case of components. And TS visibility matters when component class members are accessed directly (e.g. in tests). For this reason it may be convenient to leave most members public and stick to _ Hungarian notation to designate their purposes.Guillotine
V
20

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...

Victoria answered 19/7, 2016 at 22:33 Comment(1)
Where is '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.' taken from? Angular does allow you to use the @Input decorator with a private modifier (I would not have thought so but it does)Theocrasy

© 2022 - 2024 — McMap. All rights reserved.