13

So, let's say we got a simple object that holds a string for two different languages like
welcomeText = {"de": "Willkommen zurück!", "en": "Welcome back!"}.

The welcomeText is a property of Texts object that holds all texts and gets delivered asynchronously (so i need to take care of possible undefined values, hence the elvis operator). Now, in my angular2 template i want to show the text based on a current selected language.
This is working (but not what i need):

..
{{Texts?.welcomeText?.de}}   // works, as well as {{Texts?.welcomeText?.en}}
.. 

What i want is this (since language can be changed):

..
{{Texts.?welcomeText?[language]}}
.. 

Unfortunately this results in an error:

EXCEPTION: Template parse errors:
Parser Error: Conditional expression 
      {{Texts?.welcomeText?[language]}}
     requires all 3 expressions at the end of the expression ..

No idea how to fix this error. I'm just not sure if i am using it wrong or if it simply isn't intended to work like that. For the moment i use a simple workaround, but i find it to be somewhat ugly since i have a method call everywhere i want to display text:

..
{{getText('welcomeText')}} 
..
..
getText(name : string){
 if(this.Texts)
   return this.Texts[name][this.language]
..

Is this just the way to go or is there a way to do it the way i want, with the elvis operator?
Thanks a lot for any answers!

ch40s
  • 580
  • 1
  • 7
  • 19

7 Answers7

4

I actually ended up using custom 'safe navigation' pipe, as in my case chain of null-checks and square brackets was getting way to long and Angular's ?. operator wouldn't fit into [] constraint.

import { Pipe } from '@angular/core';
import { path } from 'ramda';

@Pipe({name: 'safe'})
export class SafeNavigationPipe {
    transform(propertyPath: any[], source: any) {
        return path(propertyPath, source);
    }
}

Where path function does all the job - retrieves data at given path and returns undefined as soon as the path can not be resolved.

And can be used like this:

{{ [userId, dayIdx, shiftIdx, 'status'] | safe:usersList  }}

Where array items can be dynamic values.

Daniel Mylian
  • 732
  • 6
  • 12
2

I encountered the same problem, and while the Elvis ?. operator was introduced in this thread: https://github.com/angular/angular/issues/791 it is now closed, @lgalfaso suggested extending it to include ?[ and ?(

In my book it should support this, mainly because the workaround is a tedious thing to have to do.

Mikkel
  • 7,693
  • 3
  • 17
  • 31
1

I think the easier solution for your case would be using a ternary operator, like this:

{{ Texts && Texts.welcomeText ? Texts.welcomeText[language] : '' }}

You could test it your path to desired value and wouldn't have to call a function to do this.

1

You can do something like:

{{(Texts.?welcomeText || {})[language]}}

so basically you are saying "get me welcomeText or empty object" - this will always return an object. Then you can fetch any property.

Sielu
  • 1,070
  • 14
  • 19
1

you can use it like welcomeText?.[language]

{{ Texts?.welcomeText?.[language] }}
kwalski
  • 580
  • 7
  • 15
1

Angular 12.1.0-next.5 (commit) includes support for safe keyed read expressions, so from this version we can just use it normally as we do for non-dynamic properties/methods:

{{ Texts?.welcomeText?.[language] }}
developer033
  • 24,267
  • 8
  • 82
  • 108
-1

I have a feeling what you're trying to do here isn't actually possible there's certainly nothing about it on the template syntax page.

For what its worth I don't think there's anything actually wrong with the solution you've come up with, it's probably how I would have solved this problem myself.

Edit: It's also worth noting that the I18n and L10n features of Angular 2 haven't been implemented yet, I'd stay tuned for this feature to solve the problem

Zyzle
  • 2,210
  • 1
  • 16
  • 19