Search code examples
objecttwiginterpolationtemplatingtemplating-engine

Interpolate Twig variable/attribute inside value of another Twig attribute definition


Take the following example structure:

{% set paths = {
                   ...
                   'js': {
                           ...
                           'jquery': {
                                      'version': '1.7.2',
                                      'cdn': 'https://ajax.googleapis.com/ajax/libs/jquery/{{paths.js.jquery.version}}/jquery.min.js',
                                      'fallback': ....
                                      }
                          }
               }
%}

To access, I would normally use something like:

 <script src="{{paths.js.jquery.cdn}}"></script>

The problem:

The interpolated variable isn't recognized, you will get something like ...libs/jquery/%7B%7B%paths.js/jquery.version7D%7D/jquery.min.js....

I've tried:

  • 'a': 'text{{b}}text',
  • 'a': {{ ('text' ~ b ~ 'text') }},
  • 'a': "{{ ('text' ~ b ~ 'text') }}",
  • 'a': "{{ (['text', b, 'text'] | join }}",
  • 'a': "{{ (['text', {{b}}, 'text'] | join }}"
  • And more I've forgotten

I'm aware of attribute()

There's not a lot of documentation on it, but from what I see, it would have to be something like this:

attribute(paths, attribute(js, attribute(jquery, cdn)))

It would be fine for one level, but not an arbitrary level of depth. Please correct me if I'm misunderstanding attribute().


Solution

  • 'a': ('text' ~ b ~ 'text') is actually correct, but there cannot be curly braces around the expression (because we're already inside an expression, a variable definition).

    The correct way:

    {% set paths = {
                       ...
                       'js': {
                               ...
                               'jquery': {
                                          'version': '1.7.2',
                                          'cdn': ('https://ajax.googleapis.com/ajax/libs/jquery/' ~ paths.js.jquery.version ~ '/jquery.min.js'),
                                          'fallback': ....
                                          }
                              }
                   }
    %}
    

    But there is simple design mistake here; when the renderer/parser comes to the cdn attribute definition, paths isn't set yet. One possible way to get around this is to declare another variable first;

    {% set params=  {
                       'jqueryversion': '1.7.2'
                    }
    %}
    
    {% set paths = {
                       ...
                       'js': {
                               ...
                               'jquery': {
                                          'cdn': ('https://ajax.googleapis.com/ajax/libs/jquery/' ~ params.jqueryversion ~ '/jquery.min.js'),
                                          'fallback': ....
                                          }
                              }
                   }
    %}
    

    Obviously if paths.js.jquery.cdn is only going to be used once then just hardcode the parent variable's value where you need it and interpolate the child variable:

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/{{params.jqueryversion}}/jquery.min.js"></script>