Search code examples
angularelementref

If statement and ElementRef to change background color not working [Angular]


I'm trying to get the background of the text to be green if case 3 and "Accepted your offer" is printed. I want to use elementRef to access the DOM and javascript to add the background colour using if statement. I added the if statement at the back of my component.ts file however it broke the programme, before that it was working fine and printing correctly. No error message is shown on the console. How can I achieve my desired behaviour to change the background colour using this method? Thanks in advance. Here is my .component.ts and .html files:

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';

@ViewChild('latestSystemMessage', { static: true })
latestSystemMessage: ElementRef;

export class MessagesComponent implements OnInit {

getLatestSystemMessage(thread: Thread): string {
        const message = thread.messages.slice().reverse().find(m => m.type !== 0);

        const isUserOwner = thread.project.user.id === this.user.id;

        let content = '';

        if (message) {
            switch (message.type) {
                case 1:
                    if (<any>message.content > 0) {
                        content = isUserOwner ?
                            `Offered you $${message.content}` :
                            `You offered $${message.content}`;
                    } else {
                        content = isUserOwner ?
                            `Offered to translate for free` :
                            `You offered to translate for free`;
                    }
                    break;
                case 2:
                    content = isUserOwner ?
                        'Cancelled offer' :
                        'You cancelled your offer';
                    break;
                case 3:
                    content = isUserOwner ?
                        'You accepted the offer' :
                        'Accepted your offer';
                    break;
                case 4:
                    content = isUserOwner ?
                        "You accepted another translator's offer" :
                        "Accepted another translator's offer";
                    break;
            }
        }
        
        if(this.latestSystemMessage.nativeElement === "Accepted your offer" ){
            this.latestSystemMessage.nativeElement.style.backgroundColor="green";
        }else{};

        return content;
    }

Here is my .html file

<p class="mb-0" #latestSystemMessage><strong>{{getLatestSystemMessage(thread)}}</strong></p>

Solution

  • First, your view child is declared outside of the component, it should be inside.

    But you are trying to go against angular principles. Instead of accessing the DOM with a viewchild, define a property and evaluate that property from your markup.

    Define a css/sass class in your component style file for your green background :

    .acceptedoffer {
       background: green;
    }
    

    Then in your component typescript class, define a boolean property indicating whether or not this is an accepted offer:

    export class MessagesComponent implements OnInit {
    isAcceptedOffer: boolean = false;
    

    Set that property at the right time in the getLatestSystemMessage method and get rid of the viewchild.

    To follow your current business logic, I added this line to your case 3 :

    this.isAcceptedOffer = !isUserOwner;
    

    Your component should now look like this :

    import { Component, OnInit } from '@angular/core';
    
    export class MessagesComponent implements OnInit {
    isAcceptedOffer: boolean = false;
    
    getLatestSystemMessage(thread: Thread): string {
            const message = thread.messages.slice().reverse().find(m => m.type !== 0);
    
            const isUserOwner = thread.project.user.id === this.user.id;
    
            let content = '';
    
            if (message) {
                switch (message.type) {
                    case 1:
                        if (<any>message.content > 0) {
                            content = isUserOwner ?
                                `Offered you $${message.content}` :
                                `You offered $${message.content}`;
                        } else {
                            content = isUserOwner ?
                                `Offered to translate for free` :
                                `You offered to translate for free`;
                        }
                        break;
                    case 2:
                        content = isUserOwner ?
                            'Cancelled offer' :
                            'You cancelled your offer';
                        break;
                    case 3:
                        this.isAcceptedOffer = !isUserOwner;
                        content = isUserOwner ?
                            'You accepted the offer' :
                            'Accepted your offer';
                        break;
                    case 4:
                        content = isUserOwner ?
                            "You accepted another translator's offer" :
                            "Accepted another translator's offer";
                        break;
                }
            }
    
            return content;
        }
    

    Last but not least, in your markup, add your css class conditionally to the value of isAcceptedOffer being true, and remove the view child declaration :

    <p class="mb-0" [class.acceptedoffer]="isAcceptedOffer"><strong>{{getLatestSystemMessage(thread)}}</strong></p>
    

    Now I realize you might have shared only a fraction of the component's code and there might be some added complexity, but still I think you should try to refactor and use angular the way it's meant to be used.