167

If a variable is declared private on a component class, should I be able to access it in the template of that component?

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>{{title}}</h2>
      <h2>Hello {{userName}}</h2> // I am getting this name
    </div>
  `,
})
export class App {
  public title = 'Angular 2';
  private userName = "Test Name"; //declared as private
}
drew moore
  • 31,565
  • 17
  • 75
  • 112
3gwebtrain
  • 14,640
  • 25
  • 121
  • 247

8 Answers8

262

UPD

Since Angular 14, it is possible to bind protected components members in the template. This should partially address the concern of exposing internal state (which should only be accessible to the template) as the component's public API.


No, you shouldn't be using private variables in your templates.

While I like the drewmoore's answer and see perfect conceptual logic in it, implementationwise it's wrong. Templates do not exist within component classes, but outside of them. Take a look at this repo for the proof.

The only reason why it works is because TypeScript's private keyword doesn't really make member private. Just-in-Time compilation happens in a browser at runtime and JS doesn't have any concept of private members (yet?). Credit goes to Sander Elias for putting me on the right track.

With ngc and Ahead-of-Time compilation, you'll get errors if you try accessing private members of the component from template. Clone demonstration repo, change MyComponent members' visibility to private and you will get compilation errors, when running ngc. Here is also answer specific for Ahead-of-Time compilation.

Yaroslav Admin
  • 13,880
  • 6
  • 63
  • 83
  • 6
    this is the best comment and imo should be the accepted answer. It's not that you can use private variables once transpiled, that you should.. Keep code clean! – Sam Vloeberghs Sep 26 '16 at 14:48
  • 2
    This is the only one valid answer ! Codelyzer now warns you when you use private var in your template. – maxime1992 Nov 28 '16 at 11:42
  • 14
    My only problem with this is, how do you differentiate between actual publicly exposed members such as @Inputs and Outputs to members which we want to expose only to our template and not the outside world. If you are building reusable components where you want methods / members accessible to the template but not to other components. I think the original answer is correct. Templates are part of the component. – Ashg Oct 04 '17 at 23:28
  • 3
    I agree with @Ashg - and not only w.r.t. Inputs and Outputs. What about when I want to communicate _between_ components, e.g. by injecting a parent component into its child. The child component can then see _everything_ the parent is exposing to its template, instead of just the methods that the parent wants to expose to the outside world. Within the constraints of Angular, this answer remains the correct one, but I don't think this design has been well thought through. – Dan King Jul 11 '19 at 22:41
  • 1
    This is a good answer since it addresses the limitations within Angular's AoT compilation and how to get around them. However, IMO the question was conceptual (intentionally or not). Conceptually, templates are part of class definitions. Templates don't extend nor inherit classes and they don't access instantiated objects externally... it's the other way around. Templates are defined within the class itself so, conceptually, they are part of the class and conceptually should have access to private members. – A-Diddy Jun 08 '20 at 18:23
88

Edit: This answer is now incorrect. There was no official guidance on the topic when I posted it, but as explained in @Yaroslov's (excellent, and correct) answer, this is no longer the case: Codelizer now warns and AoT compilation will fail on references to private variables in component templates. That said, on a conceptual level everything here remains valid, so I'll leave this answer up as it seems to have been helpful.


Yes, this is expected.

Keep in mind that private and other access modifiers are Typescript constructs, whereas Component/controller/template are angular constructs that Typescript knows nothing about. Access modifiers control visibility between classes: Making a field private prevents other classes from having access to it, but templates and controllers are things that exist within classes.

That's not technically true, but (in lieu of understanding how classes relate to decorators and their metadata), it might be helpful to think of it this way, because the important thing (IMHO) is to shift from thinking about template and controller as separate entities into thinking of them as unified parts of the Component construct - this is one of the major aspects of the ng2 mental model.

Thinking about it that way, obviously we expect private variables on a component class to be visible in its template, for the same reason we expect them to be visible in the private methods on that class.

drew moore
  • 31,565
  • 17
  • 75
  • 112
  • 3
    First, I thought just like you drewmoore. But I upgraded tslint to 4.02 and codelyzer to 2.0.0-beta.1 and I had errors saying I cannot use private when accessing variables in view. So @Yaroslav's answer seems more appropriate. – maxime1992 Nov 28 '16 at 11:39
  • 8
    I agree that it doesn't make sense for a component model to not be able to see its private variables, they should probably be mashed into a same class during compile, I mean, you have to expose component specific traits, objects and functions to all other components so that you can use those in your template, not to mention external tweaks or calls to those could cause potential unexpected behavior on the finished component – Felype Apr 19 '17 at 11:26
  • 1
    @drewmoore, hello I've been coding angular only for some months. I was confronted with this issue. Is there any further debate on this? As I don't find anything specific on what pattern to follow. imo, as it's worth what it is, it seems to violate code separation. – Edgar Jul 27 '17 at 15:31
  • 2
    @drewmoore, I must say i'm totally agreeing with your anser logic. and i'm afraid the Angular team has messed up a bit. in AOT mode, they aren't allowing private members, while on the docs they claim otherwise, which in the case of private members is absolutely strengthening your point and only adding more chaos to this topic. From Docs: "Angular treats a component's template as belonging to the component. The component and its template trust each other implicitly. Therefore, the component's own template may bind to any property of that component, with or without the *@*Input decorator." – Orel Eraki Dec 08 '18 at 22:08
  • @drewmoore, Link for the docs: https://angular.io/guide/attribute-directives#appendix-why-add-input (I know it's mainly focus on Input decorator, but a lot of what they are talking about aren't just related to it) – Orel Eraki Dec 08 '18 at 22:08
16

Even though the code example indicates the question is about TypeScript it doesn't have the tag. Angular2 is also available for Dart and this is a notable difference to Dart.

In Dart the template can't reference private variables of the component class, because Dart in contrast to TypeScript effectively prevents access of private members from outside.

I still back @drewmoores suggestion to think about component and it's template as one unit though.

Update (TS) It seems with offline compilation access to private properties will become more limited in Angular2 TS as well https://github.com/angular/angular/issues/11422

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 2
    Is it possible to have Typescript compiler to limit private variables being accessible to the view? – Armeen Moon May 30 '16 at 01:44
  • I don't know. I guess not. – Günter Zöchbauer May 30 '16 at 02:49
  • 2
    I would think that having them private could impact how testable the component is right? For example, if I create a component in the context of a test, I wouldn't be able to call those private methods from my test to confirm the template/class interaction is working. I haven't tried this yet, so forgive me if this is obvious :) – Sam Storie Jun 17 '16 at 13:22
  • In Dart you can't access private members in tests. There is a lot of discussion (independent of the language) whether this should be supported and whether the private API should be tested at all. Testing the public API should be able to reach each code path. I think this is reasonable in general. In Dart private is per library (which can consist of several files) which makes the public API quite broad - IMHO too broad for unit test. – Günter Zöchbauer Jun 17 '16 at 13:27
  • it is possible to test the private functions , please check this thread https://stackoverflow.com/questions/56044471/testing-private-functions-in-typescript-with-jest – sravan ganji Jan 07 '22 at 18:41
6

A workaround could be using private variables in ts file and using getters.

private _userName = "Test Name";
get userName() {
  return this._userName;
}

This is a good approach because the ts file and the html remains independent. Even if you change the _userName variable name in ts file, you dont have to make any change in the template file.

Franklin Pious
  • 3,670
  • 3
  • 26
  • 30
  • i think that if u change _userName to _clientName, for example, for consistency, you need to change getter to get clientName... so there is no win – LeagueOfJava Jul 09 '18 at 17:06
  • It is a bad practice to user underscore for private variables. – Florian Leitgeb Oct 03 '18 at 08:36
  • 2
    @FlorianLeitgeb Which is why [the official Angular docs do it](https://angular.io/guide/component-interaction#intercept-input-property-changes-with-a-setter)? `private _name = '';` – ruffin Dec 12 '18 at 15:12
  • Then this code snippet was not reviewed properly. They follow a style convention, which is declared in the style guide [here](https://angular.io/guide/styleguide#style-03-04). And also in the Typescript classes section on their page [here](https://www.typescriptlang.org/docs/handbook/classes.html) is not using the underscore. – Florian Leitgeb Dec 18 '18 at 16:45
  • 1
    @FlorianLeitgeb So what would be the proposed solution to the interception of setter methods as shown in the link posted by ruffin? i.e. What do you call your setter's private backing field? – El Ronnoco Jan 14 '19 at 10:58
  • It depends on the problem. The example provided is not very good and I would always prefer a more functional approach to this like setChildName(name) or so. To be honest, I never ran into the need of setters. Maybe the problem has to do with the architecture behind. – Florian Leitgeb Feb 04 '19 at 06:59
3

Private variables can be using within the template of component. See angular2 cheat-sheet for guide: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#parent-to-child-setter

A more detailed explanation on public/private members of classes in typescript can be found here: https://www.typescriptlang.org/docs/handbook/classes.html.

All members by default are Public. Public members can be accessed from outside the component class along with the class-instance. But Private members can be accessed only within the class member functions.

anusreemn
  • 1,047
  • 1
  • 10
  • 24
  • I looked at the first link (https://angular.io/guide/component-interaction#!#parent-to-child-setter) and I don't see anywhere it suggest that using private variables in templates is ok. To the opposite, they use getters and setters to access the private variables from the template. – Sebastien Chartier Jan 24 '20 at 15:53
1

The short answer is no you should not be able to access private members from the template because it is technically separated from the TS file.

1

In tsconfig.app.json if you provide the 'fullTemplateTypeCheck' option in compiler options you can see all the invalid references in html files of your project at the time of project build.

"angularCompilerOptions": {
"enableIvy": true,
"fullTemplateTypeCheck": true

}

0

I know that this is a little late, but I've been preocuppied with this problem even since Angular2 and finally I've come up with a nice workaround to allow me to have nice proper components API's and also to be able to access private fields in them from templates with proper type checking:

export interface MyComponentPrivateProperties {
    _label: string;
}
export class MyComponent {
    private _label: string = 'Label';

    public get view(): MyComponentPrivateProperties {
        return this as any;
    }
}
<div>{{view._label}}</div>

In this way, as you can see, we will have even type checking and all we need in the html template and also a proper API of the component. If in another component we reference the MyComponent class like this:

export class OtherComponent {
    private _m: MyComponent;

    ngOnInit() {
        // here, this._label is not visible.
    }
}

we will notice the property _label is not visible.

Of course, notice that the class MyComponent should not implement the interface MyComponentPrivateProperties. There will be no implementation for this interface. This is just a description for the angular ngc compiler. It tells the compiler at compile time which are the private properties that should be accessible from the templates and then dissapears at run time.

Cosmin Popescu
  • 190
  • 2
  • 5