Passing Arguments by Reference Instead of by Value in Angular

The code for this article is available on my GitHub:
JeffreyBane/ALF-My-By-Ref-App (github.com)

Recently this issue came up and I thought it would be a great use case to demonstrate where if we could only pass variables ByRef in TypeScript, our code could be so much cleaner.

In JavaScript (i.e., TypeScript, i.e., Angular), function arguments are almost always passed by value, which essentially means that any variable you pass into a function is passed in as a copy of that variable, not the original variable itself. There are many cases where you could save a bunch of coding if you could only get the method to modify the original variable and not a copy of it. This is called passing by reference.

In this example, I’m going to demonstrate a way to pass function arguments by reference rather than by value and save some coding. So, let’s just jump right into the example.

The hypothetical company we work for is a telemarketer that sells extended car warranties. Did I intentionally pick a type of company that we hate the most? You bet. We can hate the example company and still learn.

Let’s say the salespeople have 3 metrics that are tracked via surveys:

  • Knowledge
  • Courtesy
  • Speed

(In our fantasy world, they sell a LOT of warranties and people even stick around to take the phone survey every time. Pretend is fun).

When the manager pulls up a telemarketer employee, he or she wants to see these metrics for the employee both for the current day and the previous day. In addition, they’d like to display an arrow to indicate the trend of each metric, i.e., if the current day’s rating is more than the previous day’s rating, an up arrow will be displayed to indicate the metric is trending up. Similarly, if the current day’s rating is less than yesterday’s rating, a down arrow should be displayed to indicate the metric is trending down. If the current and previous ratings are the same, a simple dash is to be displayed to indicated there is no trend up or down. One final requirement – if there’s a problem retrieving either piece of data, no trending image should be shown at all.

So for this article, let’s use Font Awesome as it comes with, well, an awesome bunch of icons that look great, many of which of are free.
https://www.npmjs.com/package/@fortawesome/angular-fontawesome

Following the instructions and using the version table they provide; this is what I used to install Font Awesome. I have Angular 13 on the machine I’m using, so I used 0.10.2 for my Font Awesome version:

1 npm install @fortawesome/fontawesome-svg-core
2 npm install @fortawesome/free-solid-svg-icons
3 npm install @fortawesome/angular-fontawesome@0.10.2

If you haven’t played with the Font Awesome icons, you’re missing out on some cool stuff, so added bonus of working through this. Now that we’ve got that set up, let’s create a component for our ratings ByVal page:

>> ng g c ratings-by-val  

We’ll create some mocked calls to an API via a service we’ll create shortly that will return several things:

  • A boatload of employee attributes (simulated)
  • A current and previous value for each of our 3 metrics.

Let’s start with the HTML file to see our UI elements:

Ratings By Val HTML (ratings-by-val.component.html)

<div class="container">
    <div class="row">
        <div class = "col">Knowledge Rating</div>
        <div class = "col">{{ knowledgePreviousRating }}</div>
        <div class = "col">{{ knowledgeCurrentRating }}</div>
        <div class = "col">
            <fa-icon [icon]="faKnowledge" class="{{ knowledgeClass }}"></fa-icon>
        </div>
    </div>
    <div class="row">
        <div class = "col">Courtesy Rating</div>
        <div class = "col">{{ courtesyPreviousRating }}</div>
        <div class = "col">{{ courtesyCurrentRating }}</div>
        <div class = "col">
            <fa-icon [icon]="faCourtesy" class="{{ courtesyClass }}"></fa-icon>
        </div>
    </div>
    <div class="row">
        <div class = "col">Speed Rating</div>
        <div class = "col">{{ speedPreviousRating }}</div>
        <div class = "col">{{ speedCurrentRating }}</div>
        <div class = "col">
            <fa-icon [icon]="faSpeed" class="{{ speedClass }}"></fa-icon>
        </div>
    </div>
</div>

So easy enough – we’ll be displaying a section for each rating that has previous rating, current rating, then the above-mentioned arrow in the [icon] property of the fa-icon tag, binding to a variable in each case (faKnowledge, etc). Also, we have the class of the fa-icon element being bound to a variable – knowledgeClass, speedClass, etc. This is how we’ll display or hide the icon if there’s a “problem” retrieving any data values, supposing our API might not be able to retrieve some data for whatever reason from time to time. In our scss file, we add two classes to handle showing or hiding the icon:

Ratings ByVal style sheet (ratings-by-val.component.scss)

.fa-hide
{
visibility:hidden !important;
}


.fa-display
{
visibility:visible !important;
}

Now we’ll create a service that is a mock API that fetches our data from a database of employee data by employeeId, and returns, as previously mentioned, a boatload of employee attributes (simplified to one field: employeeAttributes), and the current and previous values for our 3 metrics.

Employee Service (employee-service.ts)

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class EmployeeService {

  constructor() { }


  getEmployeeDetails(empId: number): Observable<any> {


   const mockJson = {

            employeeAttributes: 'A bunch of employee related attributes',
            knowledgePreviousRating: 8,
            knowledgeCurrentRating: 10,
            courtesyPreviousRating: 12,
            courtesyCurrentRating: 12,
            speedPreviousRating: 7,
            speedCurrentRating: 5

   };
   return of(mockJson);
  }

}

Ok that’s all the setup, so let’s have a look at our component file to hook everything up:

Ratings By Val Component (ratings-by-val.component.ts)

import { Component, OnInit } from '@angular/core';
import { faArrowUp, faArrowDown, faMinus} from '@fortawesome/free-solid-svg-icons';
import { EmployeeService } from '../employee-service';

@Component({
  selector: 'app-ratings-by-val',
  templateUrl: './ratings-by-val.component.html',
  styleUrls: ['./ratings-by-val.component.scss']
})
export class RatingsByValComponent implements OnInit {


  faArrowUp = faArrowUp;
  faArrowDown = faArrowDown;
  faMinus = faMinus;

  employeeAttributes: string;

  knowledgePreviousRating: number;
  knowledgeCurrentRating: number;
  faKnowledge = faMinus; // set to minus for now
  knowledgeClass = 'fa-hide'; 

  courtesyPreviousRating: number;
  courtesyCurrentRating: number;
  faCourtesy = faMinus; // set to minus for now
  courtesyClass = 'fa-hide'; 

  speedPreviousRating: number;
  speedCurrentRating: number;
  faSpeed = faMinus; // set to minus for now
  speedClass = 'fa-hide'; 


  constructor(private _employeeService: EmployeeService) { }

  ngOnInit(): void {

    this._employeeService.getEmployeeDetails(1234).subscribe((data) =>{

      this.employeeAttributes = data['employeeAttributes'];
      this.knowledgePreviousRating = data['knowledgePreviousRating'];
      this.knowledgeCurrentRating = data['knowledgeCurrentRating'];
      this.courtesyPreviousRating = data['courtesyPreviousRating'];
      this.courtesyCurrentRating = data['courtesyCurrentRating'];
      this.speedPreviousRating = data['speedPreviousRating'];
      this.speedCurrentRating = data['speedCurrentRating'];

      this.assignIcons();


    });
  }

  assignIcons(): void {

    this.knowledgeClass = 'fa-display';

    if ((this.knowledgePreviousRating == null) ||
         (this.knowledgeCurrentRating == null))
    {
      this.knowledgeClass = 'fa-hide'
    } else if (this.knowledgePreviousRating > this.knowledgeCurrentRating) 
    {
      this.faKnowledge = faArrowDown;
    } else if (this.knowledgePreviousRating < this.knowledgeCurrentRating) 
    {
      this.faKnowledge = faArrowUp;
    } else if (this.knowledgePreviousRating === this.knowledgeCurrentRating)         
    {
      this.faKnowledge = faMinus;
    }


    this.courtesyClass = 'fa-display';

    if ((this.courtesyPreviousRating == null) || 
         (this.courtesyCurrentRating == null))
    {
      this.courtesyClass = 'fa-hide'
    } else if (this.courtesyPreviousRating > this.courtesyCurrentRating) {
      this.faCourtesy = faArrowDown;
    } else if (this.courtesyPreviousRating < this.courtesyCurrentRating) {
      this.faCourtesy = faArrowUp;
    } else if (this.courtesyPreviousRating === this.courtesyCurrentRating) 
    {
      this.faCourtesy = faMinus;
    }


    this.speedClass = 'fa-display';

    if ((this.speedPreviousRating == null) || 
         (this.speedCurrentRating == null))
    {
      this.speedClass = 'fa-hide'
    } else if (this.speedPreviousRating > this.speedCurrentRating) {
      this.faSpeed = faArrowDown;
    } else if (this.speedPreviousRating < this.speedCurrentRating) {
      this.faSpeed = faArrowUp;
    } else if (this.speedPreviousRating === this.speedCurrentRating) 
    {
      this.faSpeed = faMinus;
    }
  
  }
}

So a decent chunk of code for sure. Some things you’ll want to notice in the above file: We’re importing the icons we need from font-awesome:

import { faArrowUp, faArrowDown, faMinus} from '@fortawesome/free-solid-svg-icons';

Then we create a class variable for each, named the same:

faArrowUp = faArrowUp;
faArrowDown = faArrowDown;
faMinus = faMinus;

If you hover over these in your code, you’ll notice they are of the IconDefinition type, also from the font-awesome library. In in the OnInit(), we’re assigning the fields returned from the service to our UI, but also note the call to assignIcons() as the final step in our init method:

   this._employeeService.getEmployeeDetails(1234).subscribe((data) =>{

      this.employeeAttributes = data['employeeAttributes'];
      this.knowledgePreviousRating = data['knowledgePreviousRating'];
      this.knowledgeCurrentRating = data['knowledgeCurrentRating'];
      this.courtesyPreviousRating = data['courtesyPreviousRating'];
      this.courtesyCurrentRating = data['courtesyCurrentRating'];
      this.speedPreviousRating = data['speedPreviousRating'];
      this.speedCurrentRating = data['speedCurrentRating'];

      this.assignIcons();

So once our current and previous variables are set, a method called assignIcons() is called to handle which arrow to display (or hide) for each metric. Think what you will about our extended car warranty telemarketing company, but this is an excellent example of a method that would benefit
if we could pass in parameters ByRef (which we will do shortly!)

First though, let’s look at what it does currently:

    this.knowledgeClass = 'fa-display';

    if ((this.knowledgePreviousRating == null) || 
         (this.knowledgeCurrentRating == null))
    {
      this.knowledgeClass = 'fa-hide'
    } else if (this.knowledgePreviousRating > this.knowledgeCurrentRating) 
    {
      this.faKnowledge = faArrowDown;
    } else if (this.knowledgePreviousRating < this.knowledgeCurrentRating) 
    {
      this.faKnowledge = faArrowUp;
    } else if (this.knowledgePreviousRating === this.knowledgeCurrentRating) 
    {
      this.faKnowledge = faMinus;
    }

// rinse / repeat for courtesy metric

// rinse / repeat for speed metric

As you can see, assignIcons() consists of the same block of logic duplicated for each of our 3 metrics and does the following:

1) Sets the class of the icon to fa-display. As the icon is only hidden in one condition (either field is null), this effectively sets the class for the other 3.
2) If either value is null or undefined, sets the class to ‘fa-hide’. Per the requirement, this will hide the arrow if some of the data was unavailable. We’ll test this in a moment.
3) If previous is greater than current, set the icon to the down arrow.
4) If current is greater than previous, set the icon to the up arrow.
5) Finally, if they equal, set the icon to the minus sign (dash).

This is then repeated for the remaining 2 metrics, resulting in quite a bit of duplicated code. (Imagine if there were 5 or 10 metrics!). But you can see this is working as expected in our UI:

You can also test the class swap logic by removing one of the values. Comment out the following line in the service and you’ll see the hiding of the arrow works as well:

// knowledgePreviousRating: 8,

Now we’re simulating a situation where the API was unable to retrieve the knowledge previous rating, and no icon is displayed at all as the ‘fa-hide’ class has been applied to the icon as expected:

So cool, we have functional code. A bit repetitive, but it does work so clearly, we can go home for the week. (We cannot go home for the week).

You might be thinking, this would be much easier if we could modify the data returned, and that’s typically true. However, you might also be stuck using a 3rd party API, which happens a fair amount in the world, and have no control over the shape of the data (or simply an API from another group the in the company that has no desire to change it). The purpose of this article is to demonstrate byVal vs byRef which this example very much does, so let’s make the assumption were using a someone else’s API and we’re stuck with this data format.

Let’s examine the problem a bit deeper by briefly by writing a quick method that demonstrates ByVal behavior. makeIconUpArrow() is a method where we pass an icon in as a parameter and modify it to see what happens. Have a look at this method and let’s, for the moment, add it to our component.ts file:

makeIconUpArrow(icon: IconDefinition): IconDefinition {
  icon = faArrowUp;
  return icon;
}

Our new method simply takes an icon as a parameter, sets that icon to the arrow up, then returns it. Let’s add a call to this method for all 3 icons at the end of onInit() and see what happens. Add these 3 lines after the call to assignIcons() in our init method:

      this.assignIcons();

      // New code
      this.makeIconUpArrow(this.faKnowledge);
      this.makeIconUpArrow(this.faCourtesy);
      this.makeIconUpArrow(this.faSpeed);

So regardless of what icons are assigned to each metrics, we’re just going to try to override that at the end of the OnInit() and assign the up arrow to each icon. The result of this:

And you can see, nothing has changed. Because in Angular, variables are passed byRef. It’s actually a copy of the variable that’s being modified in the makeIconUpArrow() method, not the original icon itself. This is not an Angular issue, nor is it really a TypeScript issue. Since at the end of the day we’re compiling (technically transpiling) to JavaScript, the ByRef / ByVal
behavior has its roots in JavaScript. In JavaScript, primitives (plain old variables) are passed by value. There is no altering that behavior. HOWEVER, and great news for us, Arrays and Objects are passed by reference. So, if the little hamster that spins the wheel that powers your brain is running at full speed and you’re thinking “So if we wrap everything in an object, it will work….”

Correct, that is the solution! (My little hamster broke his leg years ago).

So, let’s make some changes and create a much cleaner method that modifies our icons ByRef. First thing we’ll do is create a Ratings ByRef component:

>> ng g c ratings-by-ref

The one thing we’ll copy over from our previous component is the css styles, so add these 2 styles again:

Ratings ByRef style sheet (ratings-by-ref.component.scss)

.fa-hide
{
visibility:hidden !important;
}


.fa-display
{
visibility:visible !important;
}

Now we’ll build an object, actually an array of objects, for our data so we can pass everything ByRef into our icon logic. First thing we’ll do is create a model which makes life much easier when dealing with objects (yes, I’m guilty of using “any” more than I should in these examples). Our model for rating items:

Rating Item Model (rating-item.ts)

import { IconDefinition } from "@fortawesome/fontawesome-svg-core";

export class RatingItem {
    ratingName: string;
    previousRating: number;
    currentRating: number;
    icon: IconDefinition;
    class: string
}

Next we’ll create the Ratings ByRef component file. We’ll both import that class and create a new variable called ratingGroup. This will hold an array of RatingItem objects. In our Init method, we’ll now build this array of RatingItems from the data retrieved from the service:

Ratings ByRef Component (ratings-by-ref.component.ts)

import { Component, OnInit } from '@angular/core';
import { faArrowUp, faArrowDown, faMinus } from '@fortawesome/free-solid-svg-icons';
import { EmployeeService } from '../employee-service';
import { RatingItem } from '../rating-item';

@Component({
  selector: 'app-ratings-by-ref',
  templateUrl: './ratings-by-ref.component.html',
  styleUrls: ['./ratings-by-ref.component.scss']
})
export class RatingsByRefComponent implements OnInit {


  faArrowUp = faArrowUp;
  faArrowDown = faArrowDown;
  faMinus = faMinus;

  employeeAttributes: string;

  ratingGroup: RatingItem[] = [];


  constructor(private _employeeService: EmployeeService) { }

  ngOnInit(): void {

    this._employeeService.getEmployeeDetails(1234).subscribe((data) => {

      const riKnowledge: RatingItem = {
        ratingName: 'Knowledge',
        previousRating: data['knowledgePreviousRating'],
        currentRating: data['knowledgeCurrentRating'],
        icon: faMinus,
        class: 'fa-hide'
      };

      const riCourtesy: RatingItem = {
        ratingName: 'Courtesy',
        previousRating: data['courtesyPreviousRating'],
        currentRating: data['courtesyCurrentRating'],
        icon: faMinus,
        class: 'fa-hide'
      };

      const riSpeed: RatingItem = {
        ratingName: 'Speed',
        previousRating: data['speedPreviousRating'],
        currentRating: data['speedCurrentRating'],
        icon: faMinus,
        class: 'fa-hide'
      };

      this.ratingGroup.push(riKnowledge);
      this.ratingGroup.push(riCourtesy);
      this.ratingGroup.push(riSpeed);


      this.assignIcons();



    });
  }


  assignIcons(): void {

  }

}

Now we have an array of the 3 groups of data points for our UI, with some placeholder values for icon and class which will get changed by our assignIcons() method, which is blank for the moment, and we’ll fill in shortly.
First let’s look at our new html file, wired up to our new array of objects:

Ratings ByRef HTML (ratings-by-ref.component.html)

<div class="container">
    <div class="row">
        <div class = "col">ratingGroup[0]['ratingName']</div>
        <div class = "col">{{ ratingGroup[0]['previousRating'] }}</div>
        <div class = "col">{{ ratingGroup[0]['currentRating'] }}</div>
        <div class = "col">
            <fa-icon [icon]="ratingGroup[0]['icon']" 
             class="{{ ratingGroup[0]['class'] }}"></fa-icon>
        </div>
    </div>
    <div class="row">
        <div class = "col">ratingGroup[1]['ratingName']</div>
        <div class = "col">{{ ratingGroup[1]['previousRating'] }}</div>
        <div class = "col">{{ ratingGroup[1]['currentRating'] }}</div>
        <div class = "col">
            <fa-icon [icon]="ratingGroup[1]['icon']" 
             class="{{ ratingGroup[1]['class'] }}"></fa-icon>
        </div>
    </div>
    <div class="row">
        <div class = "col">ratingGroup[2]['ratingName']</div>
        <div class = "col">{{ ratingGroup[2]['previousRating'] }}</div>
        <div class = "col">{{ ratingGroup[2]['currentRating'] }}</div>
        <div class = "col">
            <fa-icon [icon]="ratingGroup[2]['icon']" 
             class="{{ ratingGroup[2]['class'] }}"></fa-icon>
        </div>
    </div>
</div>

This is similar to our previous html file; however, our values are now bound to the appropriate item in the array of objects we are creating now. If we start our app and have a look at the current output, we see that everything is bound correctly in the UI, however our icon logic isn’t working yet as expected since the assignIcons() method is empty at the moment:

Now let’s add the guts to our new assignIcons() method and have a look. First alter the final line in the Init method, modify the call to assignIcons() to pass in (ByRef!) our ratingGroup array of objects:

  this.assignIcons(this.ratingGroup);

Then replace the empty assingIcons() method with the actual code:

  assignIcons(rg: RatingItem[]): void {

       rg.forEach(item => {
  
          item['class'] = 'fa-display';

          if ((item['previousRating'] == null) 
            || (item['currentRating'] == null)) 
          {
            item['class']= 'fa-hide';
          } else if (item['previousRating'] > item['currentRating']) {
            item['icon'] = faArrowDown;
          } else if (item['previousRating'] < item['currentRating']) {
            item['icon'] = faArrowUp;
          } else if (item['previousRating'] === item['currentRating']) {
            item['icon'] = faMinus;
          }
    
        });

  }

Now that’s a method you can show to your children with pride. As you can see, since we’re working with an object, everything is now passed byRef to this method, so we simply loop through the array of objects. assignIcons() can loop through 5, 10, 100 ratings no problem:

Look at that, a winner is you!

So, in summary, while it would be great if in TS / JS we could simply specify byRef or byVal on method parameters, with some creative use of Arrays and Objects, we can achieve the same result.

For your reading:

All you care to know about JavaScript objects: Working with objects – JavaScript | MDN (mozilla.org)

3 thoughts on “Passing Arguments by Reference Instead of by Value in Angular”

  1. You’re funny as hell and this was really good. Keep doing these please, some of us like long ass explanations and source code we can copy. (Just being honest lol)

  2. This was very informative and quite funny honestly; I love how you put some humor into an Angular tutorial 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *