DEV Community

Cover image for Angular Composition API
Michael Muscat
Michael Muscat

Posted on • Edited on

Angular Composition API

⚠️ This article is based on an early version of the library. Click here for the most recent version.


Angular Composition API is a lightweight (3kb), experimental library for writing functional Angular applications.

function State(props: Props) {
  Subscribe(() => {
    console.log("Hello World!")
  })
}
Enter fullscreen mode Exit fullscreen mode

Concepts

This library introduces an execution context that removes a lot of the ceremony needed to wire and orchestrate Angular components. It provides a layer of abstraction on top of existing Angular constructs, such as lifecycle hooks, change detection, queries, host bindings and host listeners. It embraces the power of RxJS with composable subscriptions. Angular Composition API is designed to feel native to the Angular developer.

There are two core APIs: View and Service.

View

The View API is a mixin that extends an Angular component or directive. It takes a State factory function and optional Props argument. The function will run in an execution context that allows other context-dependant APIs to be called.

Service

The Service API is a mixin that creates a tree-shakable service from a factory function. The function will run in an execution context that allows other context-dependant APIs to be called.

Definitions

When this library refers to Value, it means BehaviorSubject, and when it refers to an Emitter, it means EventEmitter.

Example

To give you an idea of what application development with this library looks like, let's write a component to display some todos from a service.

First define the props interface. The component will inherit its metadata.

@Directive()
class Props {
  @Input() userId: string
}
Enter fullscreen mode Exit fullscreen mode

Next define a state function. It will receive props and return an object containing the todos Value.

function State(props: Props) {
  const userId = DoCheck(() => props.userId) // <1>
  const [todos, loadTodosByUserId] = Inject(LoadTodosByUserId) // <2>

  Subscribe(userId, loadTodosByUserId) // <3>

  return {
    todos // <4>
  }
}
Enter fullscreen mode Exit fullscreen mode

A few things to observe:

  1. We create a userId value that will update when the userId prop changes.
  2. We Inject the LoadTodosByUserId token, which returns an array containing a Value and an Emitter.
  3. We set up todos to be loaded whenever a new userId is emitted.
  4. We return the todos Value, which will be automatically subscribed in the template. Change detection is scheduled whenever a returned Value changes.
@Component({
  selector: "todo-list",
  template: `
    <todo *ngFor="let todo of todos"></todo>
  `
})
export class TodoList extends View(Props, State) {}
Enter fullscreen mode Exit fullscreen mode

Lastly connect the Props and State to the component with the View mixin.

Service

What about LoadTodosByUserId? This is implemented using a Service. The example below is provided without comment.

function loadTodosByUserId() {
    const http = Inject(HttpClient)
    const emitter = Emitter()
    const value = Value()

    Subscribe(emitter, (userId) => {
        const source = http.get(`//example.com/api/v1/todo?userId=${userId}`)
        Subscribe(source, set(value))
    })

    return [value, emitter]
}

export const LoadTodosByUserId = Service(loadTodosByUserId, {
    providedIn: "root"
})
Enter fullscreen mode Exit fullscreen mode

Subscribe

Effects are performed using Subscribe. It is similar to the subscribe method in RxJS, except you can return teardown logic from the observer. The teardown logic will be executed each time a new value is received by the observer, or when the context is destroyed. It can also be called with just an observer, which is called once when the view is mounted.

function State(props: Props) {
  Subscribe(() => {
    console.log("Hello World! I am only called once")
    return () => console.log("Goodbye World!")
  })
}
Enter fullscreen mode Exit fullscreen mode

Subscribe can be used in both View and Service contexts.

A Prelude

Perhaps when NgModule and NgZone opt out arrives from the Angular roadmap, we will gain access to more ergonomic, functional and type safe component APIs. Angular Composition API is a step in that direction.

That's it! Thanks for reading.


GitHub logo antischematic / angular-composition-api

Composition model for functional reactive Angular applications.

Top comments (1)

Collapse
 
bahlulhasanli profile image
Bahlul Hasanli

It looks very useful. I will try. Thank you