1

I need my component template to render an email address which may is stored in one property of a class, but should fall back to another propery in case the first is null or empty. So I implemented it this way:

my-component.html:

<p>{{ myService.usuario.emailAddress() }}</p>

my-component.ts:

export class MyComponent implements OnInit {
    constructor(private myService: MyService) {
    }
}

my-service.ts:

export class MyService {
    //...
    public user: User;
    //...
}

user.ts:

export class User {
    //...
    mail: string;
    userPrincipalName: string;
    public emailAddress(): string {
        return this.mail || this.userPrincipalName;
    }        
    //..
}

But then the email won't be rendered. However, if I put this inside the template:

<p>{{ myService.usuario.mail || myService.usuario.userPrincipalName }}</p>

Then it will be rendered as intended.

Returnin this.mail ? this.mail : this.userPrincipalName instead of this.mail || this.userPrincipalName did no good either...

EDIT:

In order to better understand what's going on, I changed the template to this:

<p>.mail = {{ azureService.usuario.mail}}</p>
<p>.userPrincipalName = {{ azureService.usuario.userPrincipalName}}</p>
<p>(.mail || .userPrincipalName) = {{ azureService.usuario.mail || azureService.usuario.userPrincipalName}}</p>
<p>.emailAddress() = {{ azureService.usuario.emailAddress()}}</p>

This was the result:

.mail = [email protected]

.userPrincipalName = [email protected]

(.mail || .userPrincipalName) = [email protected]

.emailAddress() =

So...

Is Angular template limited to reading properties but incapable of reading functions' results, as it seems? Or did I something wrong anywhere?

2
  • Are mail and userPrincipalName defined when emailAddress is called? If Angular calls emailAddress before main and userPrincipalName are set, then there is no way for Angular to know that those properties have changed and that it needs to call emailAddress again. Commented Oct 16, 2019 at 21:20
  • Hi @SamHerrmann! Well, they should, I think. Users would land in another component, where they click "sign in". Then an OAuth transaction takes place. Only after this transaction is successful and an User object is instantiated with all its properties, the service changes its flag .authenticated property to true and the component I described in the post is allowed to render. Am I correct about my understanding? I'm used to server side templating (Twig, Django) but I'm new to Angular, I was tweaking a little with this sample. Commented Oct 16, 2019 at 21:33

1 Answer 1

1

As mentioned in my comment above, if Angular calls emailAddress before mail and userPrincipalName are set, then there is no way for Angular to know that those properties have changed and that it needs to call emailAddress again. The behavior you are looking for is certainly attainable in an Angular application using RxJs. Maybe a good introduction read is Observables in the Angular documentation. I have added some sample code to help you get an idea for what you will need to solve your problem:

Service

import { HttpClient } from '@angular/common/http';
import { Observable} from 'rxjs';
import { map} from 'rxjs/operators';

export class MyService {

  constructor(httpClient: HttpClient) {}

  user(): Observable<User> {
    return this.httpClient.get('user/end/point');
  }

  username(): Observable<string> {
    return this.user().pipe(
      map(user => user.mail || user.principalName)
    );
  }
}

Component Class

export class MyComponent implements OnInit {

  username = this.myService.username();

  constructor(private myService: MyService) { }
}

Component Template

<p>{{ username | async }}</p>

The async pipe subscribes to the username observable and displays the result.

Sign up to request clarification or add additional context in comments.

3 Comments

Hi again... Thank you, I will study the docs you provided and try an implementation such as you suggested... But please see my edit. I've come to this hypothesis, I'd like you to tell me whether I am right: Angular runs my .emailAddress() function just once and too early, before the properties it uses are set, and uses whatever it has returned in this early call whenever a template references it...
By default, Angular will call your function when the component is initialized and display whatever the function returns. In other words, Angular can display values returned from functions and not just property values. After initialization, Angular will also call your function when it detects a change in the component. A change can be things like a new component input value or a button click. Values changing in your service are not a change in the component and therefore Angular does not call your function. I hope that answers your question.
Thank you! So I moved the function from the service to the component itself and... voilà, it works. If you edit your answer to include this option, I'll gladly accept it, as I'd never think of that without your explanation. Thank you very much!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.