A sneaky ginger
A sneaky ginger

Reputation: 85

How can you explicitly mark @Input() properties as readonly in angular?

Inputs are the main source of communication between components in Angular, but they are mutable by default which can lead to unwanted behavior throughout the application. This can easily slip through a code review (which I am responsible for) and end up in the production codebase.

Given the following code:

export class UserListComponent {
  @Input() users: User[] = [];

  filterLocally(filter: string): void {
    this.users = this.users.filter((u) => u.name === filter);
  }
}

Any array passed on from the parent component to the users input has the risk of being changed. Let's say that this array originated from a cache which is also being used in different pages throughout the application. After applying a filter, this data would become modified and all those pages would receive invalid data. I know that this is theoretically allowed, but this is (in my opinion) such a bad pattern that we should be able to block this, especially when you're working in a team with a lot of junior developers.

Is there a way to explicitly mark an @Input() as readonly? I've tried the following:

export class UserListComponent {
  @Input() readonly users: User[] = []; // <-- added the readonly keyword -|-|-|-|-|-|-|-|-|

  filterLocally(filter: string): void {
    this.users = this.users.filter((u) => u.name === filter);
  }
}

Which will trigger the following error at code/build time which is perfect as it forces you to create a local copy of the variable:

Error in controller

But it also blocks you from initially passing data to this component which kind of destroys the purpose:

Error in template

Marking the input as such also does not work, because this can easily be circumvented in the controller:

@Input() users: Readonly<User[]> = [];

So far I haven't found a way to properly block this. Right now I'd even settle for a way to simply disable the rule inside .html files, but I cannot figure out a way to do this. A simple find & replace from @Input() ... to @Input() readonly ... in our project yielded 200+ errors, so being able to mark the inputs as readonly would be a tremendous help for us.

Upvotes: 1

Views: 2536

Answers (1)

ardentia
ardentia

Reputation: 724

If you are using a service or some sort of a cache to set the value of users internally, you should not be relying on @Inputs at all to pass data between components. The idea of a property marked as an @Input is to react to changes from its parent. I would suggest using a service as a single source of truth for the whole application and then querying it inside all the components that need that data.

Upvotes: 2

Related Questions