mozilla / nunjucks

A powerful templating engine with inheritance, asynchronous control, and more (jinja2 inspired)
https://mozilla.github.io/nunjucks/
BSD 2-Clause "Simplified" License
8.48k stars 634 forks source link

Feature request: Add a `merge` filter similar to Twig #1455

Open querkmachine opened 6 months ago

querkmachine commented 6 months ago

One of Nunjucks' stated goals is to maintain feature parity with Jinja and Twig, where there isn't conflicts or language-specific features.

Twig has a merge filter that can be used to combine either multiple arrays into a single array, or multiple objects into a single object.

Having this sort of functionality in Nunjucks would be incredibly useful, particularly when working with Nunjucks macros. It's incredibly common for us to want to combine multiple objects together.

For example, we use macros as components. Rather than having lots of default values scattered around the macro that are each selectively overridden by the macro's parameters, we could have a default configuration object set at the top of a macro, and merge a user's configuration object with that, producing a single unified configuration.

To contrive an example, instead of something like this, where the default values are interdependent but scattered around different parts of the macro (or otherwise become a long list of set tags):

{% macro myComponent(params) %}
  {% set foodPlural = params.food.plural | default("cookies") %}

  I once ate {{ params.quantity | default(5) }} {%- if params.adjective %} {{ params.adjective }}{% endif %} {{ (params.food.singular | default("cookie")) if params.quantity == 1 else foodPlural }}.
  I love {{ foodPlural }}!
{% endmacro %}

We could have everything defined in a single object at the top, leaving the body to only have the actual rendering logic.

{% macro myComponent(params) %}
  {% set v = { quantity: 5, food: { singular: "cookie", plural: "cookies" } } | merge(params) %}

  I once ate {{ v.quantity }} {%- if v.adjective %} {{ v.adjective }}{% endif %} {{ v.food.singular if v.quantity == 1 else v.food.plural }}.
  I love {{ v.food.plural }}!
{% endmacro %}

This is only one possible use case and one I encounter basically every day, but there are probably many other uses for merging arrays and objects in this manner.

In the past I've implemented a custom merge filter, but that isn't always practical, especially in cases where these macros are shared across projects and we cannot guarantee that they all share the same Nunjucks configuration or that the custom filters all work in the same underlying way.

Basically I think this'd be a great addition to the templating language (which isn't short of filters that manipulate arrays and objects) and helps meet Nunjucks' goal of parity with other, similar templating languages.

edwardhorsford commented 5 months ago

This would be very useful indeed - I end up merging objects all the time.

A related or alternative feature would be to support the javascript spread (...) operator within objects.

You could then do:

{% set defaultOptions = {
  quantity: 5,
  price: "£4.5"
} %}

{% set options = {...defaultOptions, params} %}