DEV Community

Cover image for Aurelia 2: A Fresh Take on JavaScript Frameworks
Almir Hodzic
Almir Hodzic

Posted on • Edited on

12 3

Aurelia 2: A Fresh Take on JavaScript Frameworks

Lately, conversations about JavaScript frameworks have been dominated by popular names like React (technically a library), Next.js, Svelte, Angular, and more.

These are all great tools, but have you heard of Aurelia 2?

When I first came across Aurelia, my reaction was, "What is that?" But after 2 years of working with it, I now believe it’s one of the best frameworks out there, if not the best.

Why do I think so?

Let me explain. I transitioned to Aurelia from React.js, and initially, I assumed it was just another JavaScript framework. However, as I delved deeper, I began to realize its true potential and power.

This article will serve as an introduction to Aurelia 2, where I’ll showcase some of its powerful concepts and why it stands out.

1. Event Aggregator

First up is the Event Aggregator, a concept you might be familiar with if you’ve worked in the C# ecosystem, but here’s how it works in Aurelia:

The Event Aggregator functions similarly to the event-based messaging patterns commonly used in C#. It’s a pub/sub system that allows you to publish and subscribe to custom events within your Aurelia applications.

This facilitates decoupled communication between different parts of your app. Just like in C#, where event aggregators or mediators are used to streamline event handling, Aurelia’s Event Aggregator is leveraged by the framework itself to publish events at various stages of the application's lifecycle and during specific actions.

import { IEventAggregator, resolve } from 'aurelia';

export class FirstComponent{

    readonly ea: IEventAggregator = resolve(IEventAggregator);

    bound() {
        this.ea.publish('ea_channel', ‘PAYLOAD’)
    }
 }

Enter fullscreen mode Exit fullscreen mode
import { IEventAggregator, resolve } from 'aurelia';

export class SecondComponent {

    readonly ea: IEventAggregator = resolve(IEventAggregator);

    bound() {
        this.ea.subscribe('ea_channel', payload => {
            // Do stuff inside of this callback
        });
    }
 }
Enter fullscreen mode Exit fullscreen mode

With this, we can effortlessly implement an event-driven architecture, addressing and resolving the coupling headaches often encountered with React and similar frameworks.

2. Dependency Injection

Dependency Injection (DI) is a design pattern that facilitates the creation of objects with their required dependencies, without the objects themselves being responsible for creating those dependencies. This promotes loose coupling between classes and their dependencies, enhancing modularity and testability.

Aurelia offers a powerful and flexible DI system that simplifies the process of wiring up different parts of your application. With Aurelia's DI, managing and injecting dependencies becomes seamless, resulting in cleaner, more maintainable code.

Additionally, this approach makes Test-Driven Development (TDD) easier, as it allows for straightforward mocking and testing of individual components without the need for complex setup or tightly coupled dependencies.

How it looks in practice:

import { IFileReader } from '@svc/file-reader.ts';
import { resolve } from 'aurelia';

export class FileImporter {

  constructor(readonly fileReader = resolve(IFileReader)) {
  }

}
Enter fullscreen mode Exit fullscreen mode

This example demonstrates Aurelia 2's DI, injecting FileReader into FileImporter with resolve. Similar to C#, it promotes loose coupling and cleaner, more maintainable code.

3. Dynamic Composition

Aurelia’s element enables dynamic composition of views and view-models. It acts like a custom element but without requiring a specific tag name, allowing for flexible and reusable UI components.

Inside the view model being used with , you have access to all of Aurelia's standard lifecycle events, along with an additional activate method that can be used to initialize or pass parameters to the view model.

Using the au-compose element in practice:

<template>
  <h1>Main Application</h1>
  <au-compose component.bind="viewModel" model.bind="{ title: 'Aurelia 2'}"></au-compose>
</template>
Enter fullscreen mode Exit fullscreen mode

This example showcases in Aurelia 2, acting as a dynamic component factory. It allows for flexible composition by binding a viewModel and passing a model, enabling reusable and modular UI components without hardcoding specific elements.

How It Works:

  • Dynamic Composition: The component.bind attribute dynamically binds the DynamicComponent as the view model.

  • Passing Parameters: The model.bind attribute passes parameters to the activate method in the dynamically composed view model.

Separation of Concerns

One of the reasons I love Aurelia 2 is its clean Separation of Concerns through the MVVM (Model-View-ViewModel) pattern.

View: The UI structure is entirely decoupled from the logic. It simply binds to the ViewModel to display data and capture user interactions.

ViewModel: This is where all the logic happens. It controls the data, handles business rules, and updates the View, without worrying about how it's displayed.

Model: Aurelia keeps the core application data separate from both the View and ViewModel, maintaining clarity and focus.

This separation makes the application highly modular, easier to maintain, and much simpler to test, allowing for more flexible, scalable code.

Conclusion

In this post, I’ve focused on just three powerful concepts from Aurelia 2—Event Aggregator, Dependency Injection, and Dynamic Composition—but these are only a small part of what the framework has to offer.

Aurelia 2 is packed with features that help build clean, scalable, and maintainable applications.

To get the full picture and dive deeper into its capabilities, I highly encourage you to explore the Aurelia 2 documentation for a more comprehensive understanding.

PS: This is my first post, and I hope you liked it!

Top comments (14)

Collapse
 
dansasser profile image
Daniel T Sasser II

Just from what I read here, it reminds me of NestJS but with templating. I'll definitely have to take a look at it. I do live Astro though. I've been using it for about 2 years now.

Collapse
 
hdzcalmir profile image
Almir Hodzic

Aurelia 2 offers a modular architecture and strong templating, similar to NestJS but with more focus on data binding and flexibility. If you enjoy Astro, Aurelia's reactive, component-driven approach is definitely worth exploring!

Collapse
 
glyad profile image
David Kossoglyad (Hire me!)

Anyway,... Let me add a few words to the technical aspects learned from the article:

  1. Regarding Event Aggregator: Event Aggregator as a design pattern targets initially distributed systems or low-coupled modules on the Model level. Using the EA for broadcasting events on the View-Model level is one of the greatest mistakes because the only source of trues in the MVVM application is the Model. Using EA may cause general inconsistency in the application's state. Just to remind us, EA is an optional addition to the Aurelia Framework.

  2. Regarding DI, IoC, and Containers: Yes, both Aurelia 1 and Aurelia 2 bring robust DI/IoC Containers. The interesting point is that both containers can work as standalone packages in any JS/TS application in the browser and server-side Node.js environments, and it's without bringing Aurelia dependencies. The second important point with DI/IoC containers is lifetime management. I haven't found something like Aurelia DI/IoC Container for JS/TS apps that provides the same lifetime management capabilities. Same as above I must say about registration by interface.

  3. I haven't tried the Dynamic Composition in Aurelia 2, but I actively use it with Aurelia 1. It's the killer feature for a web framework, which can be found in Aurelia only. In Aurelia 1, it allowed reusing the View-Model in front of the number of different contextual views, where routing is redundant. Also, it allows the creation of a clean MVVM ViewModel-first architecture. I hope Aurelia 2 preserved all the good parts of the Dynamic Composition from Aurelia 1.

Collapse
 
mroeling profile image
Mark Roeling

Well done!

Collapse
 
hdzcalmir profile image
Almir Hodzic

Thanks!

Collapse
 
glyad profile image
David Kossoglyad (Hire me!)

I’m using the Aurelia since 2015 and successfully built over ten web applications. The Aurelia 2 promises to be more cool

Collapse
 
hdzcalmir profile image
Almir Hodzic

Love to hear it!

Collapse
 
beggars profile image
Dwayne Charrington

People sleep on Aurelia because it's not a big name framework. But, mature developers realise that eventually it comes down to the right tools for the right job over trends.

Collapse
 
hdzcalmir profile image
Almir Hodzic

Absolutely agree! It's all about choosing the right tool for the job, and Aurelia delivers where it counts.

Collapse
 
juniourrau profile image
Ravin Rau

This is my first time hearing about Aurelia 2, thanks for writing about it.

Question, how does Aurelia 2 compare to Angular 2+ in these areas? Both frameworks use Dependency Injection and support event-driven patterns, but does Aurelia's approach offer specific advantages or a smoother developer experience?

Collapse
 
beggars profile image
Dwayne Charrington

In terms of learning curve, Aurelia beats Angular hands-down in every meaningful value. It's more intuitive IMO. You don't have to create modules or anything. For example, if you wanted to inject a service into a component:

@inject(MyService)
export class MyComponent {
    constructor(private readonly service: MyService) {
    }
}
Enter fullscreen mode Exit fullscreen mode

Or if you don't want to use decorators, you can use static inject on the class or the resolve function (which do the same thing):

// Using static inject
export class MyComponent {
    static inject = [MyService];

    constructor(private readonly service: MyService) {
    }
}

// Using resolve
export class MyComponent {
    private readonly service = resolve(MyService);
}
Enter fullscreen mode Exit fullscreen mode

It's also just as easy to leverage different forms of DI modes like lazy evaluation, singletons, transients and so on.

Collapse
 
hdzcalmir profile image
Almir Hodzic

Aurelia 2 offers a simpler, convention-based DI and intuitive event handling compared to Angular 2+'s more complex DI system and RxJS-based events. It reduces boilerplate, is easier to learn, and provides a more streamlined, flexible developer experience.

Collapse
 
abhirupa profile image
Abhirupa Mitra

Would love to dive deep here.

Collapse
 
hdzcalmir profile image
Almir Hodzic

Glad to hear that! It’s great to know my blog post inspired you.

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay