Hi Guys, I'm struggling a bit right now to incorporate a google map inside a page.

I'm creating a custom module that renders some content and right now what I'm trying to do is what is shown at this tutorial:
https://developers.google.com/maps/documentation/javascript/examples/dra...

This part is my battle right now:
Adding an external library inside my custommodule.libraries.yml, which right now looks like this:

google.maps:
  version: 1.x
  js:
    js/zipcoderate.maps.js: {minified: false}
    https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap: {external, defer: true, async: true}
  dependencies:
    - core/jquery

Problem is, the the tags "defer" or "async" doesn't show up in the tag, now, I didn't see any place where it was being added like the way I tried to do, inside the parameters next to "external", it was just a guess.

I tested as well trying to put it inline inside my content block as #markup type, but it just was stripped out!

Do anyone knows how to add an External Javascript with the tags "defer" and "async" in a proper way for Drupal 8?

Thanks,

Comments

joshi.rohit100’s picture

RainbowArray’s picture

If you want attributes on a script tag, you need to add an attributes key after a script. The key will be the attribute on the element, the value will be attribute value. If the JSON value is set to true, the attribute will appear on its own without a value on the element.

 https://maps.googleapis.com/maps/api/js?key=myownapikey&signed_in=true&libraries=drawing&callback=initMap: {type: external, attributes: { defer: true, async: true} }

Hope that helps.

fgm’s picture

Hi @mrdrummond : I'm trying to sort out how this works, but I can't seem to find what's being done with script attributes in core source. Do you have the name of a few methods to look at in order to understand this ?

For instance, the recent D8Theming book recommends use of this fragment, of which I can't entirely make sense yet (especially the asynch bit):

add-this:
  version: VERSION
  js:
    //s7.addthis.com/js/300/addthis_widget.js#pubid=someid: { type: external, asynch: asynch } 

Closest I could find is in LibaryDiscoveryParser::parseLibraryInfo() mentioning use of the defer attribute in the phpdoc, and JsCollectionRenderer::render() apparently passing any attribute on a non-aggregate asset in bulk to the element, and we know external scripts are not aggregated because JsCollectionOptimizer::optimize() excludes assets marked as external, external, or inline from optimization.

RainbowArray’s picture

Here is the documentation for how to do this, which admittedly, I just added:

https://www.drupal.org/theme-guide/8/assets#attributes

Also added that here:

https://www.drupal.org/developing/api/8/assets#attributes

fgm’s picture

Thanks for the doc ! So there is no "asynch" anyway, just "defer" and "async", it seems.

tamsoftware’s picture

Thank you very much for this documentation.
I have hit a blocker though which I can't seem to solve.

I am writing a module which needs to include a javascript external library in the page header, with parameters (like GoogleAnalytics).

Here is my mymodule.libraries.yml:

mymodule_js:
  version: 1.x
  header: true
  js:
    https://external.provider.com: { external: true, attributes: { async: true, data-my-id: d9ac4df4 } }
  dependencies:
      - core/drupalSettings

This works well as long as data-my-id is hard coded.

I have created a form in my module to be able to enter the value for "data-my-id", and it's now present in config and I'm attaching the config as such:
mymodule.module:

function mymodule_page_attachments(array &$attachments) {
    $config = \Drupal::config('mymodule.settings');

    $attachments['#attached']['library'][] = 'mymodule/mymodule_js';
    $attachments['#attached']['drupalSettings']['mymodule']['mymodule_js']['data-my-id'] = $config->get('my_id');
}

What I can't find is how in libraries.yml to replace the d9ac4df4 hardcoded value with a reference to my drupalSettings variable?
Can I put something like
https://external.provider.com: { external: true, attributes: { async: true, data-my-id: Drupal.settings.mymodule.mymodule_js.data-my-id } }?

What would the syntax be?

I've tried looking into the Google Analytics module but from what I see so far, the parameters are passed to a local js script which then computes an inline javascript and attaches it. I'm trying to avoid any inline javascript, as per the recommendations.

Thank you very much!

Franck Horlaville
TAM Software

Jaypan’s picture

YAML doesn't allow for dynamic attributes (as far as I can tell). You can maybe do this in hook_js_alter().

RainbowArray’s picture

You can use the hook library_info_build in order to add a library where some part of the definition can vary:

https://www.drupal.org/node/2374649

tamsoftware’s picture

Thanks !
I'm making progress:

function mymodule_library_info_build() {
    $config = \Drupal::config('mymodule.settings');
    $site_id = $config->get('site_id');

    $libraries = [];

    $libraries['mymodule_js'] = [
        'js' => [
            'https://my.external.library.com/' => [
                'type' => 'external',
                'minified' => TRUE,
                'scope' => 'header',
                'header' => true,
                'attributes' => [
                    'async' => TRUE,
                    'data-my-id' => "$site_id",
                ]
            ],
        ],
    ];

    return $libraries;
}

Unfortunately this is what I now receive, in the page footer:

<script src="https://load.sumome.com/" async data-my-id=""></script>

So my tries to send it to the header are currently vain and it isn't retrieving my site_id from config yet ...
I'm so close!

Any suggestions? Thanks !!

RainbowArray’s picture

'data-my-id' => "' . $site_id . '",

So in case that isn't clear, you need to have a single quote after your double quote, then a space, a period, a space, then your variable, a space, a period, a space, a single quote, a double quote.

Something like that at least. If that doesn't work, mess around with it a bit, but basically you need to properly concatenate the strings with the PHP variable.

tamsoftware’s picture

I got the data part correct now, thanks. I was missing a level in my settings,yml and I also needed to uninstall/reinstall the module to make the default config working.
That is now done and dusted.

Any idea on how I can now get my script in the header?

I've tried both

                 'scope' => 'header',
                'header' => true,

without success?
Thanks again

tamsoftware’s picture

Got it!

I was setting it one level too low, as a property of the js object where in fact it's at the top-level of the library object. Here is the correct working code:

/**
 * Implements hook_library_info_build().
 */
function my_module_library_info_build() {
    $config = \Drupal::config('my_module.settings');
    $site_id = $config->get('my_module.site_id');

    $libraries = [];

    $libraries['my_module_js'] = [
        'version' => '1.x',
        'header' => true,
        'js' => [
            'https://my.external.com/' => [
                'type' => 'external',
                'minified' => true,
                'attributes' => [
                    'data-site-id' => "$site_id",
                    'async' => "async",
                ],
            ],
        ],
    ];

    return $libraries;
}

Now I need to find out why when I write the site_id with my module's form it saves it as blank, but that's out of the scope of this discussion.

Thanks a zillion, @mrdrummond

capellic’s picture

This thread is fairly old and now you can add attributes via *.libraries.yml

https://www.drupal.org/docs/develop/theming-drupal/adding-assets-css-js-...