Table of Contents

Evergreen Angular Development Best Practices

Work in progress. Loose collection of practices to review with developers so we can agree on UI developer guidelines.

Code quality

UI

ng lint

We use a tool called ng lint in the Angular client to keep our code readable, consistently formatted, and to avoid some common bugs. Under the hood, ng lint uses a tool called eslint – the eslint website is a good place to find documentation about the lint rules.

Running ng lint

You should run ng lint early and often in the development process, as it can help you avoid certain bugs. Definitely run it before submitting your code for community review, or before committing any Angular code to the Evergreen repository.

Preparing to run ng lint

Ng lint does not require a running Evergreen system. You just need to install a few dependencies before running it for the first time.

  1. In a new terminal, cmd, or powershell window, use the cd command to navigate to the Open-ILS/src/eg2 directory within the Evergreen git repository
  2. Run the command npm install
  3. Optionally, install the Angular cli globally with npm install -g @angular/cli@^15 (replace 15 with the version of Angular that Evergreen currently uses, which you can find in the file Open-ILS/src/extras/install/Makefile.common).
Running the ng lint command

Addressing issues found by ng lint

// eslint-disable-next-line no-var
var myVariable = 1;

It's a good practice to also say a bit more in this line about why we can't do the version lint would prefer, for example:

// eslint-disable-next-line eqeqeq -- we have to use ==, rather than ===, here since result could be a string or an integer
if (result == '2') {
Common lint errors
// This code contains 2 rxjs/no-nested-subscribe errors
const idsToDelete = [3, 12, 30];
this.pcrud.search('acp', {id: idsToDelete}).subscribe(item => {
  item.deleted(true);
  this.pcrud.autoApply(item).subscribe(deletedItem => {
    this.pcrud.search('acn', {id: deletedItem.call_number()}).subscribe(callNumber => {
	  console.log(`We deleted an item related to call number ${callNumber.label()}`);
	});
  });
});

Note that there are three `subscribe()` calls, nested inside of each other, each of which will probably handle three events (for each of the ids we want to change). Troubleshooting this complexity can get tricky! Instead, you could refactor the above code to use RxJS switchmap, which is built for accepting data from one observable, then switching to a different observable that uses the same data.

// This code has no rxjs/no-nested-subscribe errors
const idsToDelete = [3, 12, 30];
this.pcrud.search('acp', {id: idsToDelete}).pipe(
  switchMap(item => {
    item.deleted(true);
    return this.pcrud.autoApply(item);
  }),
  switchMap(deletedItem => {
    return this.pcrud.search('acn', {id: deletedItem.call_number()});
  })
).subscribe(callNumber => {
  console.log(`We deleted an item related to call number ${callNumber.label()}`);
});

In this code, the distinction between the different calls and their sequence of events is a bit clearer at a glance, making for easier troubleshooting.

Unit Tests