2

When having the need to pass settings or parameters from PHP to JavaScript, i always used to do so according to a blog post from Acquia - create a dependency to drupalSettings, attach the settings on the PHP side and use the drupalSettings variable on the JS side.

my_module.libraries.yml:

my_lib:
  version: 1.x
  js:
    js/my_javascript.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

my_module.module:

function my_module_page_attachments(&$attachments) {
    $attachments['#attached']['drupalSettings']['my_module']['my_property'] = 'abc';
}

Finally my_javascript.js:

(function ($,Drupal,drupalSettings) {
  Drupal.behaviors.test = {
    attach: function(context,settings) {
      console.log(drupalSettings.my_module.my_property); // would output 'abc';
    }
  }
})(jQuery,Drupal,drupalSettings);

However, I came to a sudden realization that the settings variable of my attach function already contains exactly the same variable:

(function ($,Drupal) {
  Drupal.behaviors.test = {
    attach: function(context,settings) {
      console.log(settings.my_module.my_property); // would also output 'abc';
    }
  }
})(jQuery,Drupal);

So why is there even a separate way to load drupalSettings and it's proposed in every guide I found, when in fact we already have a variable containing our settings, so we don't need to mention the global drupalSettings variable?

Thanks for your clarification!

Update 1

I found out in my test behavior that the variable settings is a reference to drupalSettings, so if you modify one of the two, the other also has the direct changes, and they are therefore equal.

Drupal.behaviors.anotherTest = {
  attach: function(context,settings) {
    console.log(settings == drupalSettings); // outputs true
    drupalSettings.testproperty = 'abc';
    console.log(settings == drupalSettings); // still true
    console.log(settings.testproperty); //  outputs 'abc'
    // works also the other way around
    settings.testproperty = 'def';
    console.log(drupalSettings.testproperty); // outputs 'def' now
  }
}

This applies to every call of my behavior. On my rather complex project, the behavior gets called 12 times, and while the context variable changes on every call, settings is always equal to drupalSettings.

  • 1
    so we don't need to mention the global drupalSettings variable? That would have been my guess too, but given the advice to wrap behaviours in a closure I’m not sure that makes sense - surely settings would be passed in through the outer wrapper of that was the concern. My experience is the same as yours BTW, that settings === drupalSettings – Clive Nov 5 '18 at 15:38
  • 1
    @Clive Could be bad memory on my part, or me misremembering when AJAX settings get merged with the global settings. However, using the version passed in from the function also has the advantage of being found in the scope directly, rather than chasing in the global scope. Passing in via a closure helps here, as well as with the accidental reassignments. Chasing to the global (ie, window.*) scope can be slow, in a relative sense. – mpdonadio Nov 5 '18 at 16:44
  • 1
    Yeah that’s true @mpdonadio. I think you can probably even ignore the performance stuff with this: you’re given a variable and told to use it, so that’s what you should do. The fact that it’s the same object as something else may be relevant, may not, but it’s not your concern. Interface not implementation! – Clive Nov 5 '18 at 17:20
1

The thing to remember is that behaviors can fire both on page load and on AJAX requests.

Upon page load, the global and the parameter passed to the behavior should be identical.

During the course of page interaction, and depending on the AJAX commands, the settings parameter may be different when it fires.

When writing behaviors in Javascript, always use both the jQuery context to scope selectors, and the settings variable that get passed in to be safe and future proof your code even if you aren't using AJAX.

  • So, you're saying that the settings parameter is also reduced to the given context? How can Drupal distinguish when I attach my settings to the page (using hook_page_attachments) which behavior context will use the given setting? – Florian Müller Nov 5 '18 at 13:44
  • see also my updated answer with further tests... – Florian Müller Nov 5 '18 at 14:02
  • 1
    You can use alternative settings in the AJAX SettingsCommand by keeping the default merge=FALSE. You wouldn't find many examples though in core, because normally the settings are collected from the rendered output and added to the response with merge=TRUE. – 4k4 Nov 5 '18 at 17:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.