elabx Posted November 4, 2022 Share Posted November 4, 2022 Hi! I am adding Alpine.js to work with an Inputfield I'm building: public function renderReady(Inputfield $parent = null, $renderValueMode = false){ $this->config->scripts->add("https://unpkg.com/alpinejs@3.10.2/dist/cdn.min.js"); parent::renderReady($parent, $renderValueMode); } But seems to be that it requires the "defer" attribute to work correctly, otherwise I get a lot of errors from Alpine. From what I understand, the way the AdminThemeUikit loads the scripts is done here (wire/modules/AdminTheme/AdminThemeUikit/_head.php): // line 55 foreach($scripts as $file) { echo "\n\t<script type='text/javascript' src='$file'></script>"; } I also tried hooking into the AdminThemeUikit::renderFile to basically edit the output after finding the line where the alpine.js is loaded, but it happens multiple times if I setup different fields of the same type in the page (and maybe too hackish anyway??) Does anyone think it's worth making a pull request to make this more configurable? Maybe a hookable AdminThemeUikit:renderScriptTag($filename, $options) method in AdminTheme? I feel it makes sense to be able to add this attributes, since defer is now sort of standardized from what I read around the internet. foreach($scripts as $file) { $adminTheme->renderScriptTag($file); } I tried looking into initializing Alpine components any other way but had no success figuring out how to, yet ? Thanks for further answers! Link to comment Share on other sites More sharing options...
kongondo Posted November 4, 2022 Share Posted November 4, 2022 Padloper 2 uses Alpine.js (and htmx) in lots of places, including Process modules and Inputfields. Without defer, as you point out, Alpine.js goes funky. My solution is to use inline scripts. ProcessWire does so itself, in several places. For Process modules, I have a method like below that I use to inject Alpine.js script where I need it: <?php namespace ProcessWire; private function getInlineScripts() { // @note: need to load alpine as 'defer' $url = $this->wire('config')->urls->$this; $alpinejs = "{$url}vendors/scripts/alpinejs/alpinejs.3.2.4.min.js"; $out = "<script src='{$alpinejs}'defer></script>\n"; return $out; } It might not look pretty for some people but your browser doesn't care and it doesn't bother me either . For Inputfields, I have this: <?php namespace ProcessWire; public function ___render() { // .....some code /** @var PageArray $value */ $value = $this->attr('value'); $form = $this->buildForm($value); $out = ''; //---------- // ...etc //---------- // MAIN render content $out .= $form->render(); // ------- /** @var str $preloadInlineAssets */ $preloadInlineAssets = $this->renderPreloadInlineAssets(); if (!empty($preloadInlineAssets)) { $out .= $preloadInlineAssets; } //---------- // APPEND any configs for this Inputfield that might be needed by JavaScript // E.G. translated strings. $out .= $this->renderJavaScriptConfigs(); //------------- return $out; } private function renderPreloadInlineAssets(){ // ...some code: conditions, etc // ---- $out = "<script src='{$source}'{$defer}></script>\n"; return $out; } 14 hours ago, elabx said: Does anyone think it's worth making a pull request to make this more configurable? Maybe a hookable AdminThemeUikit:renderScriptTag($filename, $options) method in AdminTheme? I feel it makes sense to be able to add this attributes, since defer is now sort of standardized from what I read around the internet. I've been meaning to make a request to have this handled by config->scripts->add(), e.g. <?php namespace ProcessWire; $config->scripts->add($alpinejs, ['defer' => true, 'integriy'='some_hash', type="module"]); 1 Link to comment Share on other sites More sharing options...
bernhard Posted November 4, 2022 Share Posted November 4, 2022 5 minutes ago, kongondo said: It might not look pretty for some people but your browser doesn't care and it doesn't bother me either . Love that ? RockFrontend uses this syntax which I think is simpler and better than array syntax, because you don't have to think about all the custom attributes one might need. It's simply a suffix as a plain string: $rockfrontend->scripts()->add("foo/bar.js", "defer"); // the last part is simply a string, so you can do anything you need ->add("https://code.jquery.com/jquery-3.6.1.min.js", 'defer integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"'); 1 Link to comment Share on other sites More sharing options...
elabx Posted November 4, 2022 Author Share Posted November 4, 2022 3 hours ago, kongondo said: $config->scripts->add($alpinejs, ['defer' => true, 'integriy'='some_hash', type="module"]); 1000% something like this would be really nice! 3 hours ago, kongondo said: My solution is to use inline scripts. ProcessWire does so itself, in several places. For Process modules, I have a method like below that I use to inject Alpine.js script where I need it: Do you keep track of where you have injected it somehow? I has previously solved loading it with the following hook on an Inputfield: $this->addHookAfter('AdminTheme::getExtraMarkup', function ($e) { $parts = $e->return; $parts['head'] .= "<script defer src='https://unpkg.com/alpinejs@3.10.2/dist/cdn.min.js'></script>"; $e->return = $parts; }); But then noticed Alpine got injected multiple times if I used the field in a repeater for example, within a page with the same field. Link to comment Share on other sites More sharing options...
elabx Posted November 4, 2022 Author Share Posted November 4, 2022 @kongondo just out of curiosity, do you know what it would take if we wanted to share the same AlpineJS source for our modules? Have a ModuleJS derived AlpineJS module? Link to comment Share on other sites More sharing options...
kongondo Posted November 4, 2022 Share Posted November 4, 2022 3 hours ago, elabx said: Do you keep track of where you have injected it somehow? I haven't had the need yet. 3 hours ago, elabx said: But then noticed Alpine got injected multiple times if I used the field in a repeater for example, within a page with the same field. Yeah, I see your point. 3 hours ago, elabx said: I has previously solved loading it with the following hook on an Inputfield: Would hooking into page edit form solve the multiple injections issue or does each repeater come with its own page edit form? Just wondering. 3 hours ago, elabx said: @kongondo just out of curiosity, do you know what it would take if we wanted to share the same AlpineJS source for our modules? Have a ModuleJS derived AlpineJS module? Gut feeling is that this could work. Would it be just for loading Alpine.js? Link to comment Share on other sites More sharing options...
bernhard Posted November 4, 2022 Share Posted November 4, 2022 4 hours ago, elabx said: But then noticed Alpine got injected multiple times if I used the field in a repeater for example, within a page with the same field. I think it should be enough to just remove the hook once it got triggered? It's definitely better to add the script in the renderReady method rather than hooking buildForm or something, because in buildForm() you don't know whether your alpine field will be rendered or not (or you'd have to check that yourself and don't forget about nested fields etc...). <?php $this->addHookAfter('AdminTheme::getExtraMarkup', function ($e) { $parts = $e->return; $parts['head'] .= "<script defer src='https://unpkg.com/alpinejs@3.10.2/dist/cdn.min.js'></script>"; $e->return = $parts; $e->removeHook(null); // add this line }); 1 1 Link to comment Share on other sites More sharing options...
kongondo Posted November 5, 2022 Share Posted November 5, 2022 11 hours ago, bernhard said: $e->removeHook(null); // add this line You are the man @bernhard! I don't recall ever seeing this API! 1 Link to comment Share on other sites More sharing options...
bernhard Posted November 5, 2022 Share Posted November 5, 2022 39 minutes ago, kongondo said: You are the man @bernhard! I don't recall ever seeing this API! Haha, I think I've never actually used that myself, but I read about it some day in the blog and it sounded really useful and I think it would have been really useful in some of my hooks where I solved it differently (more complicated I guess) ? 1 Link to comment Share on other sites More sharing options...
elabx Posted November 16, 2022 Author Share Posted November 16, 2022 Thank you both for your answers! I tried to remove the hook like you suggested but the script still gets appended twice in the presence of two of the same field ?. Also added the request here: https://github.com/processwire/processwire-requests/issues/468 Also made the edit here: https://github.com/elabx/processwire/commit/9a261c18a232845d92abe8e411fa48a381563dfd I also dig your version @bernhard , like, if I want defer without value, this kinda looks weird? array( [ 'defer' => ''] ) Link to comment Share on other sites More sharing options...
elabx Posted November 16, 2022 Author Share Posted November 16, 2022 @kongondo Also managed to load AlpineJS like this: https://github.com/elabx/AlpineJS Although only works with the edited core version files mentioned above lol Then on the Inputfield on init(), I can do: $this->modules->AlpineJS This was copied from: https://github.com/somatonic/JqueryDataTables Only adding the composer dependency since that's how I want to manage it through the Inputfield actually using Alpine 1 Link to comment Share on other sites More sharing options...
bernhard Posted November 16, 2022 Share Posted November 16, 2022 6 hours ago, elabx said: I also dig your version @bernhard , like, if I want defer without value, this kinda looks weird? array( [ 'defer' => ''] ) Yeah sometimes good old strings are just a whole lot easier/faster to write. $scripts->add("foo.js", "defer") vs. $scripts->add("foo.js", ["defer" => ""]); Why not add support for strings in your PR? if(is_string($suffix)) ... elseif(is_array($suffix)) ... else ... 1 Link to comment Share on other sites More sharing options...
elabx Posted November 16, 2022 Author Share Posted November 16, 2022 @bernhard updated it! For anyone that might come across adding something else on the admin theme's markup, here's a recommendation from Ryan: $src = "https://unpkg.com/alpinejs@3.10.2/dist/cdn.min.js"; $this->wire()->adminTheme->addExtraMarkup('head', "<script src='$src' defer></script>"); 4 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now