0

In my component, I have imported a constant and I'm using it as is.

import { NullGuid } from "src/app/models/constants";
...
@Component({ ... })
export class TheComponent implements OnInit {
  constructor(private service: SomeService) { this.proxy = NullGuid; }
  ...
  proxy: string = NullGuid;
}

In the template, I can reach the service like this.

<div>{{this.service.getStuff()}}</div>

However, I can't do the same with the constant unless it's declared as shown above. Can I somehow expose the imported constant without declaring a proxy property for it?

Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438
  • No, you can only access public properties on the class from the template. – jonrsharpe Dec 22 '19 at 19:02
  • @jonrsharpe I can access the service injected and that's kind of a property, isn't it? It's a private property, as far I understand and can be accessed by *this.service* **both** in the component **and** in the template. What am I confused about here? – Konrad Viltersten Dec 22 '19 at 19:27
  • I believe that will work in JIT mode but fail in AOT; there's no private at runtime. See e.g. https://stackoverflow.com/questions/34574167/angular2-should-private-variables-be-accessible-in-the-template. Also you don't have to use this in the template. – jonrsharpe Dec 22 '19 at 19:30
  • @jonrsharpe Ah, it's me being sloppy. I forgot to check if AOT produces errors. My bad. Though, as for the *this* in the template, I noticed that I can't access properties in the service injected as private in constructor if I only use *service.serviceId* but that it worked when I went *this.service.serviceId*. Might be due to the JIT or something and is not a proper approach anyway. – Konrad Viltersten Dec 22 '19 at 19:38

1 Answers1

2

I have a workaround for that with using pipes. I have a pipe something like below:

import { Pipe, PipeTransform } from '@angular/core';
import * as constants from "./constants";

type key= keyof typeof constants;

@Pipe({
  name: 'constant'
})
export class ConstantPipe implements PipeTransform {
  transform(value: any, args:key): any {
    return constants[args];
  }
}

And use it like this :

{{null | constant:'LuckyNumber'}}

EDIT: As @KonradViltersen mentioned in the comments i was thinking to use value instead of args. But then an idea came with the Angular Language Service. If you change args type from string to type key Language service will provide you auto completion which is good when you have large amount of constants. But if you change values type to key you will only have some error in your template that will tell you about type mismatch and no runtime errors. It becomes a preference how you use it.

Also problem you are having stands with enums too.

import * as enums from './enums';

type key = keyof typeof constants;
type enumKey = (keyof typeof enums) ;


@Pipe({
  name: 'enum'
})
export class EnumPipe implements PipeTransform {
  transform<T extends enumKey, R extends keyof typeof enums[T]>(value: T, args: R): typeof enums[T][R] {
    return enums[value][args];
  }
}

Which can be used like

{{ 'Complexity' | enum : 'Hard' }}

It will provide autocomplete for Hard but not for Complexity

Stackblitz

Eldar
  • 9,781
  • 2
  • 10
  • 35
  • That was nifty. I like that. A question regarding the complexity, though. Wouldn't it make more sense to skip the parameter *args* and only map *value* to whatever that (constant) string value might correspond to? I'm thinking of usage like *{{"LuckyNumber"|constant}}* and then using that specific value to pick from the set of constants imported into the pipe. I figure, since it's a constant, there's no need to provide any (varying) value since it's going to be projected onto the same (constant) value anyway. What do you think? (+1 for niftyness) – Konrad Viltersten Dec 22 '19 at 22:53
  • Yes that way it will have clean syntax. I will update it – Eldar Dec 23 '19 at 04:07