DEV Community

Cover image for How to show (Angular) components in specific order according to a backend configuration
David Hermoso
David Hermoso

Posted on • Edited on

How to show (Angular) components in specific order according to a backend configuration

UPDATE: ⚠️ This is an awful idea for accessibility.⚠️ 🤭

It's a terrible solution for accessibility because things are being rendered in the screen in an absolutely different order as they're written in the HTML code.

So screen readers, tab navigation, etc. is different to what users may be seeing in the screen.

So it's wrong.

In any CMS you can configure your content to appear in a specific order,
so you don't need to code in order to change how it's rendered.
Last week, I faced that issue in an app I'm working on: I had some Angular components and I wanted to be able to sort them in a different way, according to a BE configuration.

Why you would do that?

Let's guess you are building a Learning platform. You have a results page with some filters.

So users can filter the courses to find the ones more interesting to them.

Basically, making the filters sortable is a business need:
Some specific reasons:

  • A/B testing
  • Same code, different brands, different kind of customers, and different configuration
  • Different sorting configuration according to courses search result
  • Highlight new filters at the top

The requirements

  • Configuration needs to be easy to understand just taking a look at it
  • If there's no configuration in the BE, components must appear how they currently are (written in the HTML).

The solution

If you want to avoid reading the whole post, that's the solution:
I used Flexbox order property.
The parent containing all the filters must be a flexbox element and flexbox direction must be column.
You can do that using the CSS properties:

display: flexbox;
flex-direction: column;
Enter fullscreen mode Exit fullscreen mode

I get a configuration object from the backend side, containing the order of every component.

sortableFilters: {
  price: 0,
  language: 2
  rating: 1,
  duration: 3,
  difficultLevel: 4
}
Enter fullscreen mode Exit fullscreen mode

I apply a style.order in the template to every component:
[style.order]="sortableElements.OrderId".

And that's it. But there's a bit more to tell, if you want to keep reading.
You can read more about Flexbox: https://css-tricks.com/snippets/css/a-guide-to-flexbox/

The configuration: It needs to be easy to understand

The configuration needs to be easy to understand for a content manager that does not need to know how the frontend is done (that by the way, can be changing).
And the configuration needs to work for a web client, but it needs to do so for any other client (Android, iOs, etc).

In Flexbox order property, the bigger the number, the lower the element appears.
Guess We have the following configuration in the backend (coming from a CMS or whatever) side:

{
  price: 0,
  language: 1
  rating: 2,
  duration: 3,
  difficultLevel: 4
}
Enter fullscreen mode Exit fullscreen mode

Price filter would appear in first place, Language filter in second place, and so on.
I don't think it's an intuitive configuration.
If you don't know how the front-end side is built, it seems to say:
"More number, more priority. So it will appear closer to the top".
So... Why the person setting up it needs to know how the Frontend is built?
I don't think it's the way it should work.

I reversed the data, not the content.

So my solution was to reverse every configuration number to its negative if it's greater than 0.
I reversed them in the frontend side because it's done because of the style. It's not something to be done in the backend.
In Angular, I'm doing it in the service, before the component gets the data.
The observable of the filter order configuration will emit with the numbers already in negative.
Note: In Angular, you should not manage data in components, but in services.

public getFiltersOrderConfiguration(): Observable<FiltersOrderConfiguration> {
    // In the real world this would be a HTTP call.
    return this.demoService.filtersOrderConfigurationBehaviorSubject
      .pipe(map(this.turnFiltersOrderConfigurationIntoNegative));
  }

private turnFiltersOrderConfigurationIntoNegative(filtersOrderConfiguration: FiltersOrderConfiguration): FiltersOrderConfiguration {
    for (const filterID in filtersOrderConfiguration) {
      if (filtersOrderConfiguration[filterID] > DemoService.DEFAULT_FILTER_ORDER_CONFIGURATION) {
        filtersOrderConfiguration[filterID] = filtersOrderConfiguration[filterID] * -1;
      }
    }

    return filtersOrderConfiguration;
  }
Enter fullscreen mode Exit fullscreen mode

So, this way, the configuration in the backend can be:

{
  price: 4, // More important / Closer to the top
  language: 3
  rating: 2,
  duration: 1,
  difficultLevel: 0 // Not configured, indeed.
}
Enter fullscreen mode Exit fullscreen mode

And the frontend will have

{
  price: -4, // More important / Closer to the top
  language: -3
  rating: -2,
  duration: -1,
  difficultLevel: 0 // Not configured.
}
Enter fullscreen mode Exit fullscreen mode

This way, the filter components will be displayed in the following order:

  1. Price
  2. Language
  3. Rating
  4. Duration
  5. Difficult

Why you turn into negative?

So every order number will be negative if it's been set up in the backend side.
I could avoid it to be turn into negative using flex-direction: reverse-column; instead of flex-direction: column.
It would show the filters reverse to how they are written in the HTML template.
Adding a component to the bottom of the template, would lead in a new component at the top.
And I think that's quite weird and not intuitive for the developers maintaining the code after me.
So that's basically why I turn every configuration number into negative.

Conclusion

I wasn't sure about this solution, but I did not find any other working for my context.
I was trying with Angular dynamic components.
And they could have work, but they're not there for this use case.
And every filter emits its own output event , and gets its own input properties, of different types.
So using dynamic components led in a mess of code impossible to maintain and close to modifications.
Read more about Angular dynamic components here: https://angular.io/guide/dynamic-component-loader

I built a simplified example:
Demo: http://demo-angular-sort-components.s3-website.eu-west-3.amazonaws.com
GitHub repo: https://github.com/jdavidhermoso/show-angular-components-sort-configuration

NOTE: That was my solution. If you think there's a better solution and you want to discuss it, you can ping me at:

Top comments (0)