-
Posts
7,473 -
Joined
-
Last visited
-
Days Won
144
Everything posted by kongondo
-
Weekly update – 12 November 2021: Page Autosave + Live Preview
kongondo replied to ryan's topic in News & Announcements
How much slower are we talking? In my mockup, the one with the htmx only I talked about earlier, the communication between the windows was quite fast. I don't know, however, how your implemented your communication. -
I am afraid I don't understand what this means. Does it mean Menu Builder is not applying those CSS classes to your menu's (MainLeft) markup?
-
@Frank Schneider, Please stick to this thread with all questions related to Menu Builder. I've merged the thread you started into this one. Thanks.
-
Welcome to the forums @Frank Schneider. Please have a look at Menu Builder's documentation.
-
I like date-fns.
-
Padloper 2: Alpha/Early Beta Testers Wanted
kongondo replied to kongondo's topic in Padloper Support
Forgot to add. I'll leave the form up for about 10 days, just to give everyone a chance to register. -
Padloper 2: Alpha/Early Beta Testers Wanted
kongondo replied to kongondo's topic in Padloper Support
Hi all, Added link to the Google Form in the first post. Please let me know if there are any glaring mistakes/ambiguities, thanks ?. -
I get you now. I've been dealing with this a lot, where custom Inputfields have been updated and InputfieldPageAutocomplete, CKEditor, Selectize, etc stop working. I have been using ProcessWire's Inputfields API to reload them ?, i.e. initFromInputfield, Inputfields.reload, .trigger('reloaded'), etc. htmx has this event htmx:afterSettle that is triggered after the DOM has settled, after a swap. I use that like this: htmx.on("htmx:afterSettle", function (event) { // RUN POST SETTLE OPS // this function will get the details of the trigger and target event // it will then call ProcessWire's Inputfield's methods or an Inputfield's own methods (e.g. InputfieldPageAutocomplete) // to reload the Inputfield that has been updated, e.g. adding a new autocomplete to the DOM via htmx runAfterSettleOperations(event) }) The only challenge I've had with this is Selectize (I reported the error in another post) but I managed to resolve that. If you are not currently using htmx, I imagine you could emit an event after your preview has been updated and reload your elements and their attached events. I'd probably use MutationObserver in that case, and have it watch a top-level wrapper element, e.g. something that wraps all the markup in the preview window. It can detect changes anywhere in the DOM. You might run into performance issues though, if you cast your net that wide, as opposed to watching, say, a number of ul lists.
-
Yes...this is the same link I posted here: Just pointing this out to avoid confusion ?.
-
Thanks for your thoughts @ryan. You raise some good points here. Setting the entry level as low as possible is desirable. Do you mean when coding in htmx or vanilla JavaScript/jQuery? Maybe have a look at htmx onLoad method? There is an example here for third-party integration, although I am not sure if it is applicable. Other than that, many of htmx attributes are inherited and can be placed on the parent element. htmx aside, with JavaScript event bubbling/delegation, even with vanilla JS, if you placed the events on the parent element, future events should be picked up. Another option is MutationObserver. It is a beast though! Or maybe I misunderstood the question?
-
Glad you got it sorted ?.
-
I have had a play and some of the implementation is literally, trivial ?. SSE SSE is so simple it is unbelievable at first. You only need two things: in our case PHP and a modern browser...batteries included! Example adapted from w3schools. More examples at MDN. server-side <?php namespace ProcessWire; header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); $time = date('r'); echo "data: The server time is: {$time}\n\n"; flush(); client if(typeof(EventSource) !== "undefined") { var source = new EventSource("/some-processwire/url/"); source.onmessage = function(event) { document.getElementById("result").innerHTML += event.data + "<br>"; }; } else { document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events..."; } I tested it and it works fine but was getting errors about wrong mime type if I set the headers later, but that is to be expected. Live Preview Refresh - Partial and Full - ajax - htmx I have this working nicely (will try post the demo later today). The premise is: A change happens. The editor client sends the data to the server to save the change (autosave - was just mimicking this part). Server saves and sends back a response including the name of the field that has been saved and the page ID back to the editor client. The editor client communicates with the preview client (a separate window). It sets the page ID and the name of the changed field name in its message to the preview window. The preview window client (htmx) receives the page id and the name of the field that has changed and sends a request to the server to fetch the rendered contents of that field for that page. The server sends back the html for htmx to swap in the DOM. In this simple communication, we have the field name match the html id of the element we are swapping. E.g. <h2 id='headline'>My Headline</h2> This is simple and straightforward and easily achieves partial refresh. However, it will be difficult to handle the more complex cases I mentioned in my previous post. It will also be somewhat dictating how the developer should name their html attributes. In addition, it still leaves the developer's templates with htmx attributes that will not be needed in production. This got me thinking. Live Preview Wrapper - idea #1 In this case, the live preview window would add a wrapper html element around the rendered template file markup. The wrapper html element would have the htmx attributes instead of the template file markup. This idea didn't go very far. It would inevitably lead to broken / incorrect markup in the preview window as the outermost markup of the template file would be <html> tags. In addition, while it solves the problem of unrequired htmx attributes in production, it doesn't solve the problem of selective rendering. It still wouldn't' know what parts of the markup in the preview window to swap. Live Preview Config - idea #2 What if instead of live preview / ProcessWire suggesting things like naming of elements, standard markup, etc. that are needed in a template file in order to get selective markup rendering, we implemented it the other way round? What if the developer instead, via config file(s), passed properties to live preview to implement the htmx attributes needed for a template file / page? The default would be full page refreshes. However, if live preview detected that a partial refresh config was available, it would implement those. This would offer the granularity needed for partial refreshes. If live preview did not receive / detect any configs, the default would kick in. Live preview would remain as agnostic as possible. It could be something like this (assumes use of htmx): A developer places their live preview partial refresh configs in /site/templates/live-previews/basic-page-live-preview.php (or basic-page-live-preview.json). ProcessWire detects that the template file basic-page has a partial refresh config in .../live-previews/basic-page-live-preview.php When ProcessWire loads the preview window, it injects the htmx attributes for each named element in the live preview configs in the corresponding elements in the preview window DOM. Injection is done by a ProcessWire live preview near-agnostic JavaScript script that only runs in the preview window. When the autosave happens in the editor window and the preview window gets informed, htmx kicks in, responding to an event and fires htmx.trigger(elem, event). The element in the markup with the corresponding event listener (hx-trigger='this-element-event-fired') will pick up the event fired by htmx.trigger() and query the server for the changed markup. The attributes set in #3 will ensure only the markup that has a change will fire. Server sends the response back and the element's hx-swap (or even hx-oob) picks it up and swaps the DOM. #6 is still not straightforward though. What will live preview / ProcessWire send back? Here too the config at #1 can help. E.g. it can tell ProcessWire to send <p>My Field Value</p> or just send the value of the field back, i.e. 'My Field Value'. The former is still complicated. Instead of telling ProcessWire I want <p>My Field Value</p>, how about tell it, via the configs at #1 that if requested field is field A, send me back the raw value (e.g. a plain text) but if the request is for field B, use this defined function in basic-page-live-preview.php to send me the rendered partial markup for field B? All ProcessWire would do is pass the value of field B to the said function. This value could be a Pageimages object. The function would iterate the images and send back the 'gallery' markup back to htmx to swap. example live preview config <?php namespace ProcessWire; $livePreviewConfigs = [ [ 'field' => 'headline', 'element_id' => 'my_headline_element_id', 'htmx-attributes' => ['hx-swap' => 'outerHTML', 'hx-trigger' => 'headlineevent'] ], [ 'field' => 'images', 'element_id' => 'gallery', 'handler' => 'renderGallery','htmx-attributes' => ['hx-swap' => 'innerHTML', 'hx-trigger' => 'galleryevent'] ], ]; function renderGallery(Pageimages $images) { // we only need the <li> since we are using innerHTML swap // the target element is <ul id='gallery'></ul> // -------------- $out = ""; foreach ($images as $image) { $thumb = $image->size(200, 200); $out .= "<li><img src='{$thumb->url}' alt='{$image->url}'></li>"; } // --------- // sent back to htmx to swap return $out; } When the preview window loads, ProcessWire sends $livePreviewConfigs to the browser, e.g. ProcessWire.config.LivePreview. The preview window JavaScript would consume it like this: client set htmx attributes @note: JavaScript written in the browser, not tested, could have errors. // get live preview configs const elementsConfigs = ProcessWire.config.LivePreview // loop through configs, find the corresponding element and set their htmx attributes for (const elementConfig of elementsConfigs) { const element = document.getElementById(elementConfig.element_id) if (element) { const htmxAttributes = elementConfig['htmx-attributes'] for (const [prop, value] of Object.entries(htmxAttributes)) { element.setAttribute(prop,value); } } } htmx-ready DOM changes via live preview injection (runtime only in live preview window): <h2 id="my_headline_element_id" hx-swap='outerHTML' hx-trigger='headlineevent' hx-vals='{"field":"headline"}'>My Headline</h2> <!-- gallery --> <ul id="gallery" hx-swap='innerHTML' hx-trigger='galleryevent' hx-vals='{"field":"images"}'> <li><img src='image-1-src' alt='image-1-alt'></li> <li><img src='image-2-src' alt='image-2-alt'></li> </ul> I have partly implement the above (just hard-coded configs for now) and it works nicely. Takeaways ProcessWire's live preview remains as agnostic as possible. Developer give instructions to live preview (via configs) and not the other way round. Live preview ships with minimal defaults, including full page refresh. It seems like a lot of work on the developer's part if they want partial refresh. However, this partly matches the complexity of their template files. Besides, some of the functionality (e.g. renderGallery()) could be something they already use in their template, hence reusable. Developer does not need to have additional markup in their template files in order to see live previews when editing. This means no cleaning up afterwards. Their templates remain clean. Live preview only alters the DOM (of the rendered template) at runtime. Developer can name their markup attributes as they wish. Developer can decide what needs live preview and what doesn't, even on the same page (e.g. hx-select). ProcessWire stays out of the developer's way, as usual.
- 39 replies
-
- 11
-
@Gideon So, Please correct your code. It is broken in several places (missing braces, etc.). For instance: Here you close </li> early, before its children (nested ul). I think you meant $pages->get(1) ?.
-
Unfortunately, we cannot get a way from a full page refresh no matter how desirable a partial refresh is. htmx deals with selectors, to find the element(s) to swap. It would be very onerous for ProcessWire to read through a template's code to find the element to swap. This means this: <div hx-get="/some-url" hx-trigger="some_event" hx-target="#body" > </div> OR this.. <div hx-get="/some-url" hx-trigger="some_event" hx-target=".body" > </div> What @ryanis suggesting is that the target for htmx can be read from the GET variable. Whilst this might work for simpler cases, and it is a nice idea, the fact is there are myriad ways for building templates. What about fields that are split into various fragments on the page, how do we update those? What about the concatenated ones? This also means developers need to code for the live preview and in a certain way. Having said that, htmx has hx-vals. <div hx-get="/example" hx-vals='{"myVal": "My Value"}'>Get Some HTML, Including A Value in the Request</div> OK, that might work. This would tell ProcessWire if this changes, give me the changed values for 'myVal'. All these lead nicely to the next headache. What happens when your site goes live? It means you have to delete all those hx-attributes as you only needed them for live preview. Just playing devil's advocate here ?. If live preview was about rendering an Inputfield's content, that's easy. We would let individual Inputfields' __render()s handle it. I am using this quite a lot with partial updates (i.e., only those bits that have changed). However, if it is about previewing the site, how it would appear when rendered on the frontend, then the many difficulties of adapting to a developer's code in their template files make partial updates quite difficult, if not impossible.
-
There's lots of use cases mainly related to module development as well as internal ProcessWire use. You can also use it (with caution) for housekeeping. Let's say you created a number of fields for testing purposes on a site or module you are working on. You gave your fields a specific prefix to easily identify them as test fields, for instance. You now want to delete them. They are not in use in any template. There are a lot of them. Instead of deleting them manually, you can use the API to do it for you. <?php namespace ProcessWire; // get your 'test' fields // their names start with 'test', e.g. 'test_some_field' $testFields = $fields->find("name^=test"); d($testFields,'TEST FIELDS'); foreach($testFields as $testField){ $fields->delete($testField); } Run that in Tracy console and you are done.
- 1 reply
-
- 3
-
Yes. Using Ajax. There are many way to send an Ajax request to the server: vanilla JavaScript, jQuery (?), Axios, htmx ?...etc. A workflow like this maybe: Create buttons of type 'button' - to avoid them submitting the form normally. Choose the client-side library (or pure JS) that will to talk to the server. Listen to the click events on the buttons. I would probably toggle the buttons. User clicks on I am here: this sends an ajax request to your server. Create a session (or if you wanted, a cookie) to 'register' the user's presence. Populate your log-page. Send back a response to client, toggling the I am here button to a I am out button. This would be trivial if using htmx. User leaves: delete their session in #6. Amend your log-page. If stream still continuing, show I am here button. Stream ends: clear all sessions from #6. Something along those lines, assuming I understood your question ?.
-
Hi @howsoonisnow90. Welcome to ProcessWire and the forums ?. Not directly related to your question, but you will want to rename your field. ProcessWire uses and prefers lowercase letters for field names. So, viaggi_gallery. Yes and yes. Your viaggi_gallery field is most likely an image field that accepts and outputs multiple images. So, when you do an echo $pages->get('your_field') you are actually telling ProcessWire to output a whole collection of images and it is asking you which one? There are a number of images in here. Secondly, you need to specify what property of the object you want it to out. Maybe an object->url, or object->description, etc. This is just a toString() method kicking in. <?php namespace ProcessWire; $out = ""; // --------- $out .= "<ul>"; foreach($page->viaggi_gallery as $image){ // foreach($page->get('viaggi_gallery') as $image){// would also work // most likely you want a thumb so create one // processwire will only create this if one is not already available // $thumb = $image->size(200,200); // if you want to specify height only and let width be automatically determined $thumb = $image->height(200); $out .= "<li><a href='{$image->url}'><img src="{$thumb->url}" alt="{$image->description}"></a></li>"; } // ----- $out .= "</ul>"; echo $out; Have a look the documentation and the getting started tutorials. https://processwire.com/api/ref/pageimages/ https://processwire.com/api/ref/pageimage/ https://processwire.com/docs/tutorials/ ps: written in browser; please check for errors.
-
One of the many reasons I like htmx. It reminds me of ProcessWire a lot. Out of the box all it does is give you the necessary tools then stays out of your way. Even the author, for some reason, reminds me of Ryan ?.
-
After releasing Padloper 2, I'd like to give this (and others) module some attention. Any ideas on how it could be improved (UI, etc), please note them here. Thanks.
-
Update: Blog 2.4.6 Changelog Fixed fatal error when installing Blog (thanks @Orodreth , @lele_ballack and @Sascha Nos for reporting. Various other minor fixes. Please test and let me know. Thanks. PS: Not sure when it will propagate in the modules directory. I couldn't log in to force a re-trigger as I seem to have forgotten my password for that area ?.
-
I am happy to help in this regard in any way I can.
-
Just cross-referencing this announcement/call for testers here:
-
In preparation for testing of the initial (alpha/early beta) release of Padloper 2, I would like to gather expressions of interest. In the past, some of you expressed a willingness to help with testing. It has been many days since and your position might have changed. In addition, I would like to do this in an organised manner so we cover as much ground as possible. The grounds I’d like to cover are usability and technical aspects with a bias towards the latter. Please also note that there are a number of planned features that will follow the initial release. Hence, we shouldn’t focus much on those. These and similar thoughts will be added to a planned features list (more on this below). The main focus of this testing is to make Padloper 2 production-ready. In order to properly organise this testing, I will need to gather some information from you. I will be doing this via Google Forms. The most important detail will be your email address. I will need this in order to inform you how to access Padloper 2 as well as for other necessary communication. I will not use your email address for any other purposes nor pass it to any third-party ?. Other information to be captured in the form would be what areas of testing you will you want to be involved in and your preference for planned features (since I will need to prioritise them). Forms are better than plain emails in this respect. Please note the following if you wish to be involved in the testing programme: Pricing and subscription will follow the model I have previously stated (similar to ProcessWire Pro Modules). However, for the testing programme, your subscription period will NOT start counting down until after the production-ready release. You will still also have VIP support (please note the nature and location of this may change). To be fair to other testers, anyone joining the programme needs to actually spend time testing the product. If you won’t have time to do this, please wait for the production release. This initial release is NOT a production release. Although it may work for some in that regard, it will not be tagged as production-ready (hence the alpha tag). Licences will be the usual three: (i) Basic/Single Site Licence, (ii) Developer Licence and (iii) Agency Licence. I can explain the different between these three if anyone needs clarity. The initial release will have the introductory prices of €150, €300, €900 for single, developer and agency licences respectively. Cooling period will be 14 days (within which a full refund can be requested, no questions asked). Please note that this time period may change for the production release. Here is the link to the Google Form to express your interest in the testing programme. The form will close in 10 days. Many thanks for your patience. Hope to see you soon in the testing programme. I trust you will enjoy Padloper 2 as much as I have had the pleasure (and honour) of developing it ?.
- 41 replies
-
- 20
-
I fixed some typos in my code, by the way, in case you didn't see them ?.
-
@horstbeat me to it ?. Two options: Option 1: substr() https://www.php.net/releases/8.0/en.php (+ trim() only if needed) edit. <?php namespace ProcessWire; $originalString = '01 - Category1'; // will remove the space after 01 - for you as well $str = substr($originalString, 5); // debug // d($str,'substr'); Option 2: array explode() + trim() <?php namespace ProcessWire; $originalString = '01 - Category1'; // explode the string on the hyphen ('-') // this creates an array $str2Array = explode("-",$originalString); // our string is in the second position // we get it and remove spaces on each side $str2 = trim($str2Array[1]); // debug // d($str2Array,'$str2Array'); // d($str2,'str2');