Skip to content

Creating Observables in RxJS

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Observables are the foundation of RxJS. Everything to do with RxJS revolves around Observables. In this article, we will look at the many different methods of creating Observables provided to us by RxJS.

There are two main methods to create Observables in RxJS. Subjects and Operators. We will take a look at both of these!

What is an Observable?

But first, what is an Observable?

Observables are like functions with zero arguments that push multiple values to their Observers, either synchronously or asynchronously.

This can be kind of confusing, so let's take a very basic example of an Observable that pushes 4 values to any of its Observers.

const obs$ = Observable.create((observer) => {
  observer.next(1);
  observer.next(2);
  observer.next(3);
  setTimeout(() => observer.next(4), 1000);
});

console.log("before subscribe");
const observer = obs$.subscribe((v) => console.log("received: ", v));
console.log("after subscribe");

In the example above, we create the Observable and tell it to send 1, 2 and 3 to it's Observer immediately when it subscribes to the Observable. These are the synchronous calls. However, 4 doesn't get sent until 1 second later, occurring after we've logged after subscribe, making this an async operation.

You can see this in the output:

before subscribe
received:  1
received:  2
received:  3
after subscribe 
received:  4

The Observer will keep receiving values until the Observable notifies it that it has completed pushing values. If we modify the example above, we can see this in action.

const obs$ = Observable.create((observer) => {
  observer.next(1);
  observer.next(2);
  observer.complete();
  observer.next(3);
  setTimeout(() => observer.next(4), 1000);
});

console.log("before subscribe");
obs$.subscribe((v) => console.log("received: ", v));
console.log("after subscribe");

We've added a call to observer.complete(); after observer.next(2) which will notify the Observer that the Observer has finished pushing values.

Take a look at the new output:

before subscribe 
received:  1
received:  2
after subscribe 

We can see that even though we try to push the values 3 and 4 to the Observer, the Observer does not receive them.

A method of creating an Observable using the static create method is illustrated above. Now, we will take a look at creating Observables with Subjects and Operators.

Creating Observables with Subjects

A Subject can be thought of as a combination of EventEmitters and Observables. They act like both. An Observer can subscribe to a Subject to receive the values it pushes, while you can use the Subject directly to push new values to each Observer, or to tell each Observer that the Subject has completed pushing values.

There are 4 types of Subjects that RxJS exposes to us. We'll take a look at each in turn.

Subject

Subject is the most basic Subject that we can use to create Observables. It's very simple to use, and we can use it to push values to all Observers that are subscribed to it. Each Observer will only receive values that are pushed by the Subject after the Observer has subscribed.

Let's see this in action.

const subject$ = new Subject();

const observerA = subject$.subscribe((v) => console.log("Observer A: ", v));
const observerB = subject$.subscribe((v) => console.log("Observer B: ", v));

subject$.next(1);

const observerC = subject$.subscribe((v) => console.log("Observer C: ", v))

subject$.next(2);

We start by creating the subject, then create two Observers that will log each value they receive from the Subject (Observable).
We tell the Subject to push the value 1.
We then create ObserverC which also logs each value it receives from the Subject.
Finally, we tell the Subject to push the value 2.

Now, take a look at the output of this:

Observer A:  1
Observer B:  1
Observer A:  2
Observer B:  2
Observer C:  2

We can see that ObserverA and ObserverB both received 1 but ObserverC only received 2, highlighting that Observers of the basic Subject will only receive values that are pushed after they have subscribed!

BehaviorSubject

Another type of Subject we can use is BehaviorSubject. It works exactly the same as the basic Subject with one key difference. It has a sense of a current value. When the Subject pushes a new value, it stores this value internally. When any new Observer subscribes to the BehaviorSubject, it will immediately send them the last value that it pushed to its Observers.

If we take the example we used for Subject and change it to use a BehaviorSubject we can see this functionality in action:

const behaviorSubject$ = new BehaviorSubject();

const observerA = behaviorSubject$.subscribe((v) => console.log("Observer A: ", v));
const observerB = behaviorSubject$.subscribe((v) => console.log("Observer B: ", v));

behaviorSubject$.next(1);

const observerC = behaviorSubject$.subscribe((v) => console.log("Observer C: ", v))

behaviorSubject$.next(2);

Let's see the output to see the difference:

Observer A:  1
Observer B:  1
Observer C:  1
Observer A:  2
Observer B:  2
Observer C:  2

We can see that ObserverC was sent the value 1 even though it subscribed to the BehaviorSubject after the 1 was pushed.

ReplaySubject

The ReplaySubject is very similar to the BehaviorSubject in that it can remember the values it has pushed and immediately send them to new Observers that have subscribed. However, it allows you to specify how many values it should remember and will send all these values to each new Observer that subscribes.

If we modify the example above slightly, we can see this functionality in action:

const replaySubject$ = new ReplaySubject(2); // 2 - number of values to store

const observerA = replaySubject$.subscribe((v) => console.log("Observer A: ", v));

replaySubject$.next(1);
replaySubject$.next(2);
replaySubject$.next(3);

const observerB = replaySubject$.subscribe((v) => console.log("Observer B: ", v))

replaySubject$.next(4);

This time, we are going to have the ReplaySubject push 4 values to its Observers. We also tell it that it should always store the two latest values it emitted.

Let's take a look at the output:

Observer A:  1
Observer A:  2
Observer A:  3
Observer B:  2
Observer B:  3
Observer A:  4
Observer B:  4

We see that ObserverA receives the first 3 values perfectly fine. Then ObserverB subscribes to the ReplaySubject and it is immediately sent the values 2 and 3, which were the last two values the Subject had pushed. Then both Observers receive the next value of 4 correctly.

AsyncSubject

The AsyncSubject exposes all the same methods as Subject, however it works differently. It only ever sends the last value it has been told to push to its Observers, and it will only do this when the Subject is completed (by calling complete()). Therefore, Observers only receive values when the Subject completes and any Observers that subscribe after will immediately receive the value it pushed when it completed.

We can see this in action:

const asyncSubject$ = new AsyncSubject(2);

const observerA = asyncSubject$.subscribe((v) =>
  console.log("Observer A: ", v)
);

asyncSubject$.next(1);
asyncSubject$.next(2);

const observerB = asyncSubject$.subscribe((v) =>
  console.log("Observer B: ", v)
);

asyncSubject$.next(3);
asyncSubject$.complete();

const observerC = asyncSubject$.subscribe((v) =>
  console.log("Observer C: ", v)
);

The output of this is:

Observer A:  3
Observer B:  3
Observer C:  3

We can see that although ObserverA had subscribed before any values were pushed, it only received 3, the last one. We can also see that ObserverC also immediately received the value 3 even though it subscribed after the AsyncSubject had completed.

Creating Observables with Operators

An alternative method of creating Observables comes from the operators that RxJS exposes. These operators can be categorized based on their intention. In this article, we are going to look at the Creation Operators, so named as they create Observables.

You can see a list of these operators here: http://reactivex.io/rxjs/manual/overview.html#creation-operators

ajax

ajax is an operator that creates an Observable to handle AJAX Requests. It takes either a request object with URL, Headers etc or a string for a URL. Once the request completes, the Observable completes. This allows us to make AJAX requests and handle them reactively.

const obs$ = ajax("https://api.github.com/users?per_page=2");
obs$.subscribe((v) => console.log("received: ", v.response));

The output of this will be:

received:  (2) [Object, Object]

bindCallback

bindCallback allows you to take any function that usually uses a callback approach and transform it into an Observable. This can be quite difficult to wrap your head around, so we'll break it down with an example:

// Let's say we have a function that takes two numbers, multiplies them
// and passes the result to a callback function we manually provide to it
function multiplyNumbersThenCallback(x, y, callback) {
  callback(x * y);
}

// We would normally use this function as shown below
multiplyNumbersThenCallback(3, 4, (value) =>
  console.log("Value given to callback: ", value)
);

// However, with bindCallback, we can turn this function into
// a new function that takes the same arguments as the original
// function, but without the callback function
const multiplyNumbers = bindCallback(multiplyNumbersThenCallback);

// We call this function with the numbers we want to multiply
// and it returns to us an Observable that will only push 
// the result of the multiplication when we subscribe to it
multiplyNumbers(3, 4).subscribe((value) =>
  console.log("Value pushed by Observable: ", value)
);

By using bindCallback, we can take functions that use a Callback API and transform them into reactive functions that create Observables that we can subscribe to.

defer

defer allows you to create an Observable only when the Observer subscribes to it. It will create a new Observable for each Observer, meaning they do not share the same Observable even if it appears that they do.

const defferedObs$ = defer(() => of([1, 2, 3]));

const observerA = defferedObs$.subscribe((v) => console.log("Observer A: ", v));
const observerB = defferedObs$.subscribe((v) => console.log("Observer B: ", v));

This outputs:

Observer A:  (3) [1, 2, 3]
Observer B:  (3) [1, 2, 3]

Both Observers received an Observable with the same values pushed from it. These are actually different Observables even though they pushed the same values. We can illustrate that defer creates different Observables for each Observer by modifying the example:

let numOfObservers = 0;
const defferedObs$ = defer(() => {
  if(numOfObservers === 0) {
    numOfObservers++;
    return of([1, 2, 3]);
  }

  return of([4,5,6])
});

const observerA = defferedObs$.subscribe((v) => console.log("Observer A: ", v));
const observerB = defferedObs$.subscribe((v) => console.log("Observer B: ", v));

We've changed the defer object to give the first Observer an Observable of [1, 2, 3] and any other Observers [4, 5, 6]. Which we can then see in the output:

Observer A:  (3) [1, 2, 3]
Observer B:  (3) [4, 5, 6]

empty

The empty operator creates an Observable that pushes no values and immediately completes when subscribed to:

const obs$ = empty();
obs$.subscribe((v) => console.log("received: ", v));

This produces NO output as it never pushes a value.

from

from is a powerful operator. It can convert almost anything into an Observable, and pushes the values from these sources in an intelligent manner, based on the source itself.

We'll take two examples- an array and an iterable from a generator:

const obs$ = from([1,2,3]);
obs$.subscribe((v) => console.log("received: ", v));

With an array, from will take each element in the array and push them separately:

received:  1
received:  2
received:  3

Similarly, with the iterable from the generator, we will get each value separately:

function* countToTen() {
  let i = 0;
  while(i < 11) {
    yield i;
    i++;
  }
}

const obs$ = from(countToTen());
obs$.subscribe((v) => console.log("received: ", v));

If we create a generator that counts to 10, then from will push each number from 0-10:

received:  0
received:  1
received:  2
received:  3
received:  4
received:  5
received:  6
received:  7
received:  8
received:  9
received:  10

fromEvent

The fromEvent operator will create an Observable that pushes a every event of a specified type that has occurred on a specified event target, such as every click on a webpage.

We can set this up very easily:

const obs$ = fromEvent(document, "click");
obs$.subscribe(() => console.log("received click!"));

Every time you click on the page it logs "received click!":

received click!
received click!

fromEventPattern

The fromEventPattern is similar to the fromEvent operator in that it works with events that have occurred. However, it takes two arguments. An addHandler function argument and a removeHandler function argument.

The addHandler function is called when the Observable is subscribed to, and the Observer that has subscribed will receive every event that is set up in the addHandler function.

The removeHandler function is called when the Observer unsubscribes from the Observable.

This sounds more confusing than it actually is. Let's use the example above where we want to get all clicks that occur on the page:

function addHandler(handler) {
  document.addEventListener('click', handler)
}

function removeHandler(handler) {
  document.removeEventListener('click', handler)
}

const obs$ = fromEventPattern(addHandler, removeHandler);
obs$.subscribe(() => console.log("received click!"));

Every time you click on the page it logs "received click!":

received click!
received click!

generate

This operator allows us to set up an Observable that will create values to push based on the arguments we pass to it, with a condition to tell it when to stop.

We can take our earlier example of counting to 10 and implement it with this operator:

const obs$ = generate(
  1,
  (x) => x < 11,
  (x) => x++
)

obs$.subscribe((v) => console.log("received: ", v));

This outputs:

received:  0
received:  1
received:  2
received:  3
received:  4
received:  5
received:  6
received:  7
received:  8
received:  9
received:  10

interval

The interval operator creates an Observable that pushes a new value at a set interval of time. The example below shows how we can create an Observable that pushes a new value every second:

const obs$ = interval(1000);
obs$.subscribe((v) => console.log("received: ", v));

Which will log a new value every second:

received:  0
received:  1
received:  2

never

The never operator creates an Observable that never pushes a new value, never errors, and never completes. It can be useful for testing or composing with other Observables.

const obs$ = never();
// This never logs anything as it never receives a value
obs$.subscribe((v) => console.log("received: ", v));

of

The of operator creates an Observable that pushes values you supply as arguments in the same order you supply them, and then completes.

Unlike the from operator, it will NOT take every element from an array and push each. It will, instead, push the full array as one value:

const obs$ = of(1000, [1,2,4]);
obs$.subscribe((v) => console.log("received: ", v));

The output of which is:

received:  1000
received:  (3) [1, 2, 4]

range

The range operator creates an Observable that pushes values in sequence between two specified values. We'll take our count to 10 example again, and show how it can be created using the range operator:

const obs$ = range(0, 10);
obs$.subscribe((v) => console.log("received: ", v));

The output of this is:

received:  0
received:  1
received:  2
received:  3
received:  4
received:  5
received:  6
received:  7
received:  8
received:  9
received:  10

throwError

The throwError operator creates an Observable that pushes no values but immediately pushes an error notification. We can handle errors thrown by Observables gracefully when an Observer subscribes to the Observable:

const obs$ = throwError(new Error("I've fallen over"));
obs$.subscribe(
  (v) => console.log("received: ", v),
  (e) => console.error(e)
);

The output of this is:

Error: I've fallen over

timer

timer creates an Observable that does not push any value until after a specified delay. You can also tell it an interval time, wherein after the initial delay, it will push increasing values at each interval.

const obs$ = timer(3000, 1000);
obs$.subscribe((v) => console.log("received: ", v));

The output starts occurring after 3 seconds and each log is 1 second apart

received:  0
received:  1
received:  2
received:  3

Hopefully, you have been introduced to new methods of creating Observables that will help you when working with RxJS in the future! There are some Creation Operators that can come in super handy for nuanced use-cases, such as bindCallback and fromEvent.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

Introducing @this-dot/rxidb cover image

Introducing @this-dot/rxidb

When we are working on PWAs (Progressive Web Applications), we sometimes need to implement features that require us to store data on our user's machine. One way to do that is to use IndexedDb. Using the IndexedDb browser API has its challenges, so our team at This Dot has developed an RxJS wrapper library around it. With @this-dot/rxidb, one can set up reactive database connections and manipulate its contents the RxJS way. It also provides the ability to subscribe to changes in the database and update our UIs accordingly. In this blog post, I'd like to show you some small examples of the library in action. If you'd like to see the finished examples, please check out the following links: - Auto Increment Object Store example. - Key-value pair Object Store example You can also check our OSS repository for more examples. Storing data in chronological order Imagine that you are working on a special text editor app, or something that needs to keep data between page reloads. These kinds of apps usually need to track larger amounts of data. It would be a bad practice to use localStorage for that. In the following example, we will focus on how to store and delete rows in an Object Store that has autoIncrement enabled. For the sake of simplicity, every time the user presses the Add Item button, a timestamp of the event will be stored in the database. We would also like to be able to remove items from the beginning and the end of this store. We will add two buttons to our UI to deal with that, and we would like them to be disabled if there are no entries in the store. We have a starter HTML that looks like the following: ` Initiating the Database For us to be able to store data in IndexedDb, we need a database connection and an Object Store set up. We want this Object Store to automatically increment, so we don't need to manually keep track of the last key. We do want to listen to every update that happens in the database, so let's set up our listeners for the keys and the key-value pairs using the entries() and the keys() operators. ` We want to display the contents of the database when they get updated in the #container div. For that, we need to subscribe to our keyValues$ observable. Whenever it emits, we want to update our div. ` Manipulating the data We have three buttons on our UI. One for adding data to the Object Store, and two for removing data from it. Let's set up our event emitters, using the fromEvent observable creator function from RxJS. ` We can use the addItem operator to add rows to an automatically incrementing Ojbect Store. When the Add Item button gets clicked, we want to save a timestamp into our database. ` Removing elements from the database will happen on the other two button clicks. We need the keys$ observable so we can delete the first or the last items in the store. ` Toggling button states The last feature we want to implement is toggling the remove buttons' disabled state. If there are no entries in the database we disable the buttons. If there are entries, we enable them. We can easily listen to the keyValues$ with the tap operator. ` Real-world use cases for autoIncrement Object Stores An automatically incrementing object store could be useful when your app needs to support offline mode, but you also need to log certain events happening on the UI to an API endpoint. Such audit logs must be stored locally and sent when the device comes online the next time. When the device goes offline, every outgoing request to our logging endpoint can instead put the data into this Object Store, and when the device comes online, we just read the events and send them with their timestamps. Storing objects Have you ever needed to fill out an extremely long form online? Maybe even that form was part of a wizard? It is a very bad user experience when you accidentally refresh or close the tab, and you need to start over. Of course, it could be implemented to store the unfinished form in a database somehow, but that would mean storing people's sensitive PII (Personal Identifiable Information) data. IndexedDb can help here as well because it stores that data on the user's machine. In the following example, we are going to focus on how to store data in specific keys. For the sake of simplicity, we set up some listeners and automatically save the information entered into the form. We will also have two buttons, one for clearing the form, and the other for submitting. Our HTML template looks like the following: ` Based on the above template, we do have a shape of how the object that we would like to store would look like. Let's set up a type for that, and some default constants. ` Set up the Object Store and the event listeners Setting up the database is done similarly as in the previous example. We open a connection to the IndexedDb and then create a store. But this time, we just create a default store. This way, we have full control over the keys. With this form, we want to write the value of the USER_INFO key in this Object Store. We also want to get notified when this value changes, so we set up the suerInfo$ observable using the read() operator. ` To be able to write values into our Object Store and update the data on our UI, we need some HTML elements. We set up constants that point towards our form, the two buttons, and all of the inputs inside the form. ` And finally, we set up some event listener Observables to be able to act when an event occurs. Again, we use the fromEvent creator function from rxjs. ` Set up some helper methods Before we set up our subscriptions, let's think through what behaviour we want with this form and the buttons. It is certain that we need a way to get the current value of the form that matches the UserFormValue type. We also want to set the input fields of the form, especially when we reload the page and there is data saved in our Object Store. If there is no value provided to this setter method, it should use our predefined EMPTY_FORM_VALUE constant. ` The UI should block the user from certain interactions. The submit button should be disabled while the form is invalid, and while a database write operation is still in progress. For handling the submit button state, we need two helper methods. ` Now we have every tool that we need to implement the logic. Setting up our subscriptions We would like to write the form data into the Object Store as soon as the form changes, but we don't want to start such operations on _every_ change. To mitigate this, we are going to use the debounceTime(1000) operator, so it waits for 1 second before starting the write operation. We use our getUSerFormValue() helper method to get the actual data from the input fields and we use the setItem() method on the store$ observable, inside a switchMap operator to write the values. We also want to disable the Submit button when the form changes and re-enable it if the form is valid and the write operation is finished. ` We also want to set the values of the input fields, for example, when we refresh the page. We also handled the submit button state and we set the values only if there are values to set. We use our setInputFieldValues() method to update the UI. ` When we submit the form, we will probably want to do something asynchronously. When that succeeds, we want to clear our Object Store, so we don't keep the submitted data on our user's machine. We also want to update the UI and clear the input fields. In this example, our form would send a POST request when we press the submit button. Therefore, we call event.preventDefault() on the submit event, so we stay on the page. ` And when we want to clear the form, we need to do the same with the data stored in our Object Store. ` Real-world use cases for Key-Value pair Object Stores Having persistent forms between page refreshes is just one useful feature you can do with IndexedDb. Our example above is very simple, but you can have a multi-page form. One could store the progress on such forms and allow the user to continue with the forms later. Keeping the constraints of IndexedDb in mind, one very cool feature is storing data for offline use....

State of Angular Ecosystem - Updates in RxJS, NgRx, Ionic, Nx, Cypress, NgGirls, and more. cover image

State of Angular Ecosystem - Updates in RxJS, NgRx, Ionic, Nx, Cypress, NgGirls, and more.

In the latest State of Angular Ecosystem, core contributors of major Angular ecosystem projects shared their thoughts on the new APIs released with Angular 14, and how the community is integrating or updating other libraries like NgRx, Nx, Ionic, and RxJS for the release. Panelists also talked about community initiatives and trainings in Angular, including NG Girls and This Is Angular. Here is a complete list of the hosts and panelists that participated in the online event. Hosts: - Tracy Lee, CEO, This Dot Labs, @ladyleet - Nacho Vazquez, Senior Software Engineer, This Dot Labs, @nacho_devc Panelists: - Ben Lesh, RxJs Core Team Lead, @BenLesh - Mike Ryan, Principal Architect, LiveLoveApp & Co-creator, NgRx, @MikeRyanDev - Liam DeBeasi, Lead Developer, Ionic Framework, [@LiamDeBeasi] (https://twitter.com/LiamDeBeasi) - Juri Strumpflohner, Director of Developer Experience at Nrwl, @juristr - Jordan Powell, DX Engineer at Cypress.io, @JordanPowell88 - Shmuela Jacobs, Web Development Consultant, Founder at ngGirls, @ShmuelaJ - Erik Slack, SE Technical Lead at Cisco, Co-Producer of NgXP.show, @erik_slack - Lars Gyrup Brink Nielsen, Co-Founder of This is Learning, @LayZeeDK You can watch the full State of Angular Ecosystem event on This Dot Media's YouTube Channel. Libraries And Tools NgRx with Angular 14 NgRx 14 is in its beta version, and contributors are working on integrations and features, including adopting Angular 14, to highlight both the store package and the NgRx Components Package. The NgRx Store package update will include a number of features and benefits: The team is working to make it easier to implement an NgRx Store, requiring less code. A new createActionGroup() function to simplify and reduce the amount of code it takes to create a group of actions for an NgRx Store. NgRx ESLint Plugin will be added to your project automatically when you install an NgRx store to help improve code implementation best practices. NgRx Component Package NgRx Component is a project that looks toward the future of Angular by improving the process of developing Reactive components using Observable. NgRx Component is completely separate from NgRx store, and it features new improvements, including: - Type checking templates - More control for error handling - Improved performance when you don’t have Zone.js Make sure to check out NgRx 14.0 beta ng add @ngrx/store@next. Nx with Angular 14 Nx version 14 was released three weeks ago, with a minor release (14.2) soon following. With these products, the team focused on reducing configuration for Nx to make it simple to just install Nx in a project, and start working with it immediately. Other updates include: Nx.json file is optional with this release It comes with Angular 14 out of the box It includes ng update for automatic migration to the latest Angular version It includes TypeScript 4.7 support Storybook 6.5 support and upgraded Prettier Preparation for integrating the just released Cypress 10 is on the way Nrwl Nrwl has now officially ​​taken over the stewardship of Lerna.js. Nx was always able to integrate with Lerna directly to publish features, bootstrap, and use Nx for task scheduling. It was only made official recently when the main readme was updated, but Nrwl has already maintained Lerna for a couple of years. The purpose of this is to help the Lerna community continue to evolve. The team already made some updates, released version 5 with security patch support, and deprecated some old packages. Additionally, they removed support for old Node versions, and improved the developer experience. Nrwl has a roadmap for upcoming Lerna features that you can check out. For version 5.1, the team added a very easy opt-in feature for Nx which allows developers to install Nx and start using it without the need for configurations. However, this version allows developers to still have the Lerna commands with Nx as an additional package. Ionic Angular At the most recent Ionic Conf, we learned that Ionic and Angular power about 10% of Apps on the Apple App store, and 20% on the Google Play Store. Ionic 6.1 was released with a number of improvements to UI Components. Ionic is working on a Component Playground feature to be released under the Ionic docs website soon. This feature will help developers interact with the UI, see exactly what it will look like, and switch between iOS mode, Material Design mode, or Dark and Light Design modes. With Capacitor, the Ionic team announced the Native Google Map plugin, a highly requested plugin from the community. Ionic’s next release will come with Angular 14 support. Angular Girls (Ng Girls) Ng Girls is an organization that trains women on the fundamentals of Angular. As physical events return, the team is working on workflows and processes to make it easy to join or host mentoring sessions for Ng Girls. Ng Girls founder Shmuela Jacobs recently released a course that helps developers learn How to Build applications with Angular. You can check out her course to learn more. This Is Angular This Is Learning is the non-profit organization behind the β€œThis is Angular” course. All of the courses on This is Learning are free for anyone, and are also available for anyone to contribute to. Check out the website This Is Learning to learn more! Changes to Angular for Library Authors It is highly recommended that library authors who are not using IVY compilation yet migrate to the latest version before Angular compatibility support is officially removed. This removal could render such libraries unusable with new versions of Angular. The new Standalone APIs give library authors the opportunity to provide independent configuration functions that can be used without the need for Ng Modules. The independent inject function constructors and property initializers allow library authors to try out new patterns that are best suited for libraries and Angular applications. Starter.dev This Dot Labs recently released Starter.dev in beta, which offers starter kits in a variety of languages that allow developers to figure out architecture configurations. Starter.dev has a starter kit for Angular and it is available to test. Check out https://starter.dev to learn more. Other Announcements Our event concluded with the announcement of a new book called The Angular Developer's Nx Handbook by Lars Gyrup Brink Nielsen. Want to learn more about Angular? Check out the official Angular blog and head over to the This Dot blog for more Angular content!...

Mapping Returned HTTP Data with RxJS cover image

Mapping Returned HTTP Data with RxJS

In frontend development, it's likely that you will make API requests to retrieve data from some backend resource. However, some backends are set up in such a way that they either send too much data or not enough data back. When we combine what we know already about Reactive Programming and RxJS, we can handle these situations in a very elegant manner using some useful operators that RxJS provides us. In this article, we will look at handling both scenarios and look at four operators in particular: map, mergeMap, concatMap and switchMap. We will also provide links to some StackBlitz examples so you can see these live. For fun, we'll use the Star Wars API to fetch data and use Angular as our Frontend framework of choice as its HTTP library has Reactive Bindings out of the box. Handling too much data (part 1) Ok, let's set up the scenario. We want to find out some details about Luke Skywalker such as his name, birth year, height, weight, and eye color. With Angular, we can do this by simply making an HTTP request: ` If we do this, we _do_ get back the info we need, but we _also_ get back a lot of info we _don't_ need such as when the API entry was created and edited, Luke's species, his Vehicles etc. Right now, we don't care about those details. Let's see how we could use RxJS's map operator to only return the data we want. ` We can see from the example above that it's super easy to only pull the values you need from the response object! Note You can also see how we managed to map some of the fields in the response from snake_case to camelCase which is more of a convention in JavaScript _as well as_ mapping some fields in the response to a new name (mass -> weight). We can also convert some types to other types, such as mass as string to weight as number. This can be super helpful to keep the domain language of your frontend codebase intact. Live Example You can check this code out in action over at the live example here on StackBlitz. Feel free to fork it and play with it. Handling too much data (part 2) Another common use case of having too much data is search results. Some times we only want to display the first result from a bunch of search results. Let's set up a new scenario. We'll create a typeahead search that only shows the most relevant search result. We want to request search results after every key press the user makes; however, we also don't want to make requests that are not needed. Therefore, we want to cancel any inflight requests created as the user types. For this scenario, we'll be using a mix of the map and switchMap operators. We'll also use Angular's Reactive Forms Module to provide us with some reactive bindings. Our code for this is very simple: ` It's that simple to create an eager typeahead search with Angular and RxJS. You can see the live example of this here on StackBlitz. Handling too little data We've looked at how to handle too much data in the API response with RxJS, but how about when we have too little data? We can build this scenario out perfectly with the Star Wars API. Let's say we want to see what films the characters we are searching for appear in. The API response for the characters _does_ contain what films they are in, but it _does not_ give us details, just a URL that we can send a new request to get that film's data. This is also an Array of films so we _may_ want to get the details for all the films they appear in at this time. Let's see how we can transform our search code to allow us to fetch more data _related_ to the results and map it into the final object that we use to render the data. ` I highly recommend reading the code above and the comments to understand exactly how to achieve this scenario. This pattern is very powerful, especially for example if you need to iterate over an array of user IDs and fetch the user details associated with those IDs. There is also a live example of this here on StackBlitz that you can use to try it out. Conclusion This is a small introduction to mapping data returned from HTTP requests with RxJS, but hopefully you can see use this as a reference point if you ever need to perform complex data mapping that involves additional API requests....

Implementing Dynamic Types in Docusign Extension Apps cover image

Implementing Dynamic Types in Docusign Extension Apps

Implementing Dynamic Types in Docusign Extension Apps In our previous blog post about Docusign Extension Apps, Advanced Authentication and Onboarding Workflows with Docusign Extension Apps, we touched on how you can extend the OAuth 2 flow to build a more powerful onboarding flow for your Extension Apps. In this blog post, we will continue explaining more advanced patterns in developing Extension Apps. For that reason, we assume at least basic familiarity with how Extension Apps work and ideally some experience developing them. To give a brief recap, Docusign Extension Apps are a powerful way to embed custom logic into Docusign agreement workflows. These apps are lightweight services, typically cloud-hosted, that integrate at specific workflow extension points to perform custom actions, such as data validation, participant input collection, or interaction with third-party services. Each Extension App is configured using a manifest file. This manifest defines metadata such as the app's author, support links, and the list of extension points it uses (these are the locations in the workflow where your app's logic will be executed). The extension points that are relevant for us in the context of this blog post are GetTypeNames and GetTypeDefinitions. These are used by Docusign to retrieve the types supported by the Extension App and their definitions, and to show them in the Maestro UI. In most apps, these types are static and rarely change. However, they don't have to be. They can also be dynamic and change based on certain configurations in the target system that the Extension App is integrating with, or based on the user role assigned to the Maestro administrator on the target system. Static vs. Dynamic Types To explain the difference between static and dynamic types, we'll use the example from our previous blog post, where we integrated with an imaginary task management system called TaskVibe. In the example, our Extension App enabled agreement workflows to communicate with TaskVibe, allowing tasks to be read, created, and updated. Our first approach to implementing the GetTypeNames and GetTypeDefinitions endpoints for the TaskVibe Extension App might look like the following. The GetTypeNames endpoint returns a single record named task: ` Given the type name task, the GetTypeDefinitions endpoint would return the following definition for that type: ` As noted in the Docusign documentation, this endpoint must return a Concerto schema representing the type. For clarity, we've omitted most of the Concerto-specific properties. The above declaration states that we have a task type, and this type has properties that correspond to task fields in TaskVibe, such as record ID, title, description, assignee, and so on. The type definition and its properties, as described above, are static and they never change. A TaskVibe task will always have the same properties, and these are essentially set in stone. Now, imagine a scenario where TaskVibe supports custom properties that are also project-dependent. One project in TaskVibe might follow a typical agile workflow with sprints, and the project manager might want a "Sprint" field in every task within that project. Another project might use a Kanban workflow, where the project manager wants a status field with values like "Backlog," "ToDo," and so on. With static types, we would need to return every possible field from any project as part of the GetTypeDefinitions response, and this introduces new challenges. For example, we might be dealing with hundreds of custom field types, and showing them in the Maestro UI might be too overwhelming for the Maestro administrator. Or we might be returning fields that are simply not usable by the Maestro administrator because they relate to projects the administrator doesn't have access to in TaskVibe. With dynamic types, however, we can support this level of customization. Implementing Dynamic Types When Docusign sends a request to the GetTypeNames endpoint and the types are dynamic, the Extension App has a bit more work than before. As we've mentioned earlier, we can no longer return a generic task type. Instead, we need to look into each of the TaskVibe projects the user has access to, and return the tasks as they are represented under each project, with all the custom fields. (Determining access can usually be done by making a query to a user information endpoint on the target system using the same OAuth 2 token used for other calls.) Once we find the task definitions on TaskVibe, we then need to return them in the response of GetTypeNames, where each type corresponds to a task for the given project. This is a big difference from static types, where we would only return a single, generic task. For example: ` The key point here is that we are now returning one type per task in a TaskVibe project. You can think of this as having a separate class for each type of task, in object-oriented lingo. The type name can be any string you choose, but it needs to be unique in the list, and it needs to contain the minimum information necessary to be able to distinguish it from other task definitions in the list. In our case, we've decided to form the ID by concatenating the string "task_" with the ID of the project on TaskVibe. The implementation of the GetTypeDefinitions endpoint needs to: 1. Extract the project ID from the requested type name. 1. Using the project ID, retrieve the task definition from TaskVibe for that project. This definition specifies which fields are present on the project's tasks, including all custom fields. 1. Once the fields are retrieved, map them to the properties of the Concerto schema. The resulting JSON could look like this (again, many of the Concerto properties have been omitted for clarity): ` Now, type definitions are fully dynamic and project-dependent. Caching of Type Definitions on Docusign Docusign maintains a cache of type definitions after an initial connection. This means that changes made to your integration (particularly when using dynamic types) might not be immediately visible in the Maestro UI. To ensure users see the latest data, it's useful to inform them that they may need to refresh their Docusign connection in the App Center UI if new fields are added to their integrated system (like TaskVibe). As an example, a newly added custom field on a TaskVibe project wouldn't be reflected until this refresh occurs. Conclusion In this blog post, we've explored how to leverage dynamic types within Docusign Extension Apps to create more flexible integrations with external systems. While static types offer simplicity, they can be constraining when working with external systems that offer a high level of customization. We hope that this blog post provides you with some ideas on how you can tackle similar problems in your Extension Apps....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co