Jump to content

Weekly update – 12 November 2021: Page Autosave + Live Preview


ryan
 Share

Recommended Posts

Work has continued on the Page Autosave module this week and several more improvements have been made, compiled into version 2 of the module (now posted in ProDrafts and ProDevTools). The PageAutosave portion has been improved in several ways. For instance, when the page changes elsewhere, autosave disables and displays a message letting the user know about the change. An issue with CKEditor inline fields was also fixed. 

But the biggest addition to the Page Autosave module was Live Preview. We've got a good discussion going on in last week's post and it helped move things forward with building the live preview feature. It's functional and working well, though it's a start and there's still more to do with it. 

It's far enough along to begin testing so I've posted it in the ProDevTools and ProDrafts boards. If you don't have access to either of those boards but do to one of the other VIP boards and want to test it out, let me know and I'll add it there too. Since I'm not yet certain whether this module will end up in the core, as a module, or as part of an existing Pro module package, I'm keeping it in the VIP boards at least until it's more production ready. If I find that it adds a lot of value to the core and I can fully support it there, then it'll end up in the core.

Here's how the live preview feature works:

When live preview is enabled (it is by default), the “View” tab in the top of the page editor supports a live preview option. To see it, hover the “View” tab and it should appear.

Screen Shot 2021-11-12 at 5.06.26 PM.png

When you click the live preview link it will open a new window. Drag and size the live preview window so that you can see it at the same time as your page editor, whether side-by-side, above or below, or on another screen. As you make changes in the page editor and they are automatically saved, the live preview window will also update automatically.

No development required

There is no development required to use live preview, you don't have to insert anything into your template files to start using it. The live preview window updates by re-rendering the page and replacing the entire <body> markup of the page. I found this works well in several places where I tested it and it has the benefit of looking pretty seamless and keeping your scroll position, etc. But it's not going to work everywhere, and it can also lose JS events (though I'm looking into how we can improve that). This might be okay since you are just previewing content and layout, but it's something to keep in mind. 

If you find the default <body> replacement doesn't work well for your instance, there's a setting in the module configuration where you can tell it to reload/refresh the window for live preview instead. This will pretty much always be reliable, but it also looks like a page refresh, which somewhat reduces the live preview effect. 

To control and improve performance of live preview

There's another option that enables you to have more control over how live preview works while also significantly improving the performance of it. The tradeoff is that you need to add class names in your markup for live preview to recognize and update. 

When you want live preview to just render and replace the value for the field being edited, you can edit your template file(s) and surround the field value output with a tag having the class name "pw-value-name" where "name" is the name of the field that has its value contained within. This can significantly increase performance as live preview can render and dynamically update just the changed field rather than the entire page.

For example, let’s say you had the following markup:

<h1><?= $page->title ?></h1>

To mark it up for fast live preview, you would add a new class:

<h1 class='pw-value-title'><?= $page->title ?></h1>

When an update is made to the “title” field in the page editor, live preview will notice the h1.pw-value-title in your markup and it can skip rendering the entire page and instead just render the value of the “title” field for the page, and replace it directly. A few things to note about this:

1. Live preview replaces the “inner HTML” of the element directly with the formatted field value. You should not have any other markup within there, unless it’s part of the field’s formatted value already.

2. The surrounding element (<h1> in the example above) can have any other class names or attributes that you want it to have. Live preview does not replace the tag, only the contents within it. So if you add the pw-value-* class to an existing element that already has other classes or attributes, that’s fine, it's flexible.

3. When you use this option, consider also using it in other places you output the same field value (if there is more than one), otherwise live preview won’t update the ones that aren't identified with the pw-value-* class. So if your $page->title appears elsewhere, like in breadcrumbs (for example) then that breadcrumb instance of the title won’t update, unless it is also marked up with the pw-value-title class. Maybe that is fine for breadcrumbs for it not to update, but it’s just something to be aware of.

4. This pw-value-* option is just for basic text, textarea (including CKEditor) and number fields. Don't use this stuff in repeater, file/image and other more complex fields, as those will always involve generated markup beyond just the field value.

For something like a blog post or other article type of page, if you want a 2 minute solution that'll give you the most return, just wrap your $page->body output and call it a day:

<div class="pw-value-body"><?= $page->body ?></div>

...that way any changes to your "body" field will update more quickly and without having to re-render the page. 

What's remaining

Like discussed in last week's thread, I'd still like to add support for "pw-field-*" classes. This will be similar to the "pw-value-*" classes, except that it'll enable replacing blocks of generated markup within the element having that class name, rather than just the field value. This'll be useful for people really getting into live preview. But I also kind of think that most people will want to use live preview without having to add anything in their markup, and it works quite well that way too. 

This is all I worked on this week so no core updates to report (though this may count as core updates if it ends up in the core). Thanks for reading and have a great weekend! 

  • Like 12
  • Thanks 5
Link to comment
Share on other sites

22 minutes ago, ryan said:

If you don't have access to either of those boards but do to one of the other VIP boards and want to test it out, let me know and I'll add it there too.

I'd really love to play around with this option to be honest. Therefore if possible... maybe you could add it to ProFields, ProCache, FormBuilder, LoginRegister Pro, Lister Pro, or ProMailer (in my case).

Or... maybe add a ProLivePreview Beta for 5 USD to the modules. I'd buy it!

 

  • Like 3
Link to comment
Share on other sites

7 minutes ago, ryan said:

those are the boards you are able to view?

Yes!

7 minutes ago, ryan said:

I've added it to the first one you mentioned (ProFields board).

Just downloaded it for tomorrow to play around with it.

8 minutes ago, ryan said:

Thanks. 

I have to thank you, @ryan!

  • Like 3
Link to comment
Share on other sites

First run.
Installation and setup are super nice!

First try.
One click and straight into the preview. No hassle in a minimal setup.

First feedback.
My custom HannaCode (which was there for a previous test) doesn't show up.

2021-11-13_00-44-fs8.thumb.png.ad9e79f867589e871a2aed2a80a43c77.png

 

Where should we collect issues, requests and bugs?

 

Update 2021-11-13
some tweaks in the Page Autosave module settings (switched to: Reload/refresh document) and everything works perfectly fine now. HannaCode, Repeater(Matrix) and various other content changes.

Link to comment
Share on other sites

@Jonathan Lahijani - speedy work on the video :)

I've been thinking of doing a very similar thing with RM and Uikit to create a lot more flexibility that what I have been currently offering. I do think that with a minimal amount of training some clients will be able to do great things, but for other clients I think I would stick with more strictly defined block types. I think it comes down to a combination of what works best for the site's layout requirements and the skills of the client.

Great to hear that you are planning on releasing what you've built here, because I know you've put a lot of thought into this and I am sure there's a fair bit of effort in getting everything configured so nicely!

  • Like 5
Link to comment
Share on other sites

I am honest here @Jonathan Lahijani... I'm more than impressed.

I know what ProcessWire can do but I didn't know what some could accomplish with it and this is such a BANGER!

I don't think my clients could easily work with it, maybe a few after some introduction, but that's another story.

The people from this community, I know a bit more, could probably easily maintain and build a site with this setup from scratch and customize it if needed - even though at least half of them don't really use UIKIT for websites or need and want to go a more custom route however.

Still shocked to witness something like this while I still try to figure out why my textarea doesn't always gets updated in the live preview. :D 

  • Like 3
Link to comment
Share on other sites

Quote

I am getting an ajax error: error and it seems to be coming from this exception:
TypeError: Cannot access offset of type string on string in /site/modules/PageAutosave/PageAutosaveLivePreview.php:180

@adrian So far I'm not seeing this one. I also backtracked through the code from that line and don't see anything obvious. Do you know what steps are necessary to reproduce it?

Also wanted to mention it may be necessary to disable Tracy on the front-end for the live-preview to work. I was running into JS errors from Tracy when trying to use it with live preview, which had the effect of preventing either from working. I think it's because both live preview and Tracy are adding code in the page, and live preview must be doing something that interferes with Tracy's JS. I think I'll be able to figure out a way around it but just wanted to mention it in case you or anyone else were observes it not working when the Tracy bar is active. 

Quote

Here's a video demonstrating a RepeaterMatrix full page builder using PageAutosave with live preview.  I really want to hear your feedback if you think "non-coders", but those with decent skills, could use such a builder successfully.

@Jonathan Lahijani This is fantastic! I'm blown away by it. Amazing work. How did you come up with this video so quickly? (and so well narrated too) 

Link to comment
Share on other sites

For anyone that is testing out the live preview feature in the PageAutosave module, if you have any trouble getting it to work, here's a few troubleshooting notes:

1. Try temporarily disabling Tracy debugger on the front-end (if using it). I found that I couldn't have Tracy debugger active on the front-end while using live preview, otherwise neither Tracy or live preview would work. I think the two conflict with each other in some way and I just haven't figured it out yet but will work with it more next week.

2. If using ProCache with HTML minify, try turning that feature off for logged-in users. You can do this in Setup > ProCache > Minify. The reason for this is that HTML minify removes a few technically unnecessary markup tags that live preview may need. I will also be able to find a workaround for this, but just haven't had a chance yet. 

3. Make sure your HTML document has <head>, </head>, <body> and </body> tags present in the output. 

4. If your page does a lot of its layout with Javascript and isn't working with live preview, go to the PageAutosave module settings and near the bottom are the Live Preview settings. For the "Default live preview update method" choose "Reload/refresh document". 

5. If still not working, try enabling the PageAutosave debug mode. You'll find this at the bottom of the PageAutosave module settings. This makes it report verbose javascript console.log messages. Open your javascript console when using live preview and please let me know what messages you get. 

  • Like 4
  • Thanks 1
Link to comment
Share on other sites

13 hours ago, Jonathan Lahijani said:

Here's a video demonstrating a RepeaterMatrix full page builder using PageAutosave with live preview.  I really want to hear your feedback if you think "non-coders", but those with decent skills, could use such a builder successfully.

Thanks for making this video @Jonathan Lahijani! There is a lot to study from it.

Let me ask one question right away. How did you changed the block colors? Is that already build in like Ryan said it would, or is it with custom css for now?

  • Like 1
Link to comment
Share on other sites

I forgot to mention earlier, but I haven't yet experimented with integrating htmx into this. Trying to start simple so right now it's all original PHP/JS. I do plan to experiment with htmx here to see what additional options it'll open up. The current version does however use server side events (SSE) like some of you guys (Ivan, Netcarver, Kongondo) suggested. 

@Ivan Gretsky Configurable repeater type colors I haven't yet added but will try to do that this coming week.

  • Like 3
Link to comment
Share on other sites

9 hours ago, ryan said:

@Jonathan Lahijani This is fantastic! I'm blown away by it. Amazing work. How did you come up with this video so quickly? (and so well narrated too) 

Thanks @ryan.  All the pieces were already in place.  I've been working on this concept for a while now and with live preview, everything has come together.

 

8 hours ago, Ivan Gretsky said:

Let me ask one question right away. How did you changed the block colors? Is that already build in like Ryan said it would, or is it with custom css for now?

@Ivan Gretsky That's just with custom CSS I'm loading in the admin.  Like this:

// assuming RM field is called 'builder' and matrix-type is called 'builder_column'
#wrap_Inputfield_builder [data-typename="builder_column"].InputfieldRepeaterItem > label.InputfieldHeader {
  outline: 1px solid #777;
  outline-color: #777;
  background-color: #777;
}

 

2 hours ago, bernhard said:

@Jonathan Lahijani how/where do you define the markup for all those elements? For example the image... Does it use some kind of $img->maxSize(x, y)->url or does it show the originally uploaded image, which might be several MB in filesize?

@bernhard The 'options' field (powered by Mystique) has a plethora of options for each matrix-type.  The 'Image' matrix-type has a 'width' and 'height' field that if set, will set it to the specified width (->width(width)), specified height (->height(height)) or size (->size(width, height)).  If no values are entered, it uses the original dimensions.

  • Like 4
Link to comment
Share on other sites

@ryan - Ok, I tracked that error down. Uninstalling the module is what triggered that. It turns out that during the uninstall, if I dump $change from inside this foreach:
 

foreach($changes as $key => $change) {

I get: 

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'page_autosave_changes' doesn't exist

 

But back to the more important issue of why the module doesn't actually work for me. Firstly, the module does work on my local dev environment, but on a live servers, it doesn't.

I still keep getting: "ajax error: error"

If I inspect the Network tab, I see a 500 error for: /page/edit/?id=8074&modified=1636913850&fields=body but there don't seem to be any PHP errors, so I went looking in the apache logs and I am seeing these:

[Sun Nov 14 09:54:27.525633 2021] [proxy_fcgi:error] [pid 24560:tid 139854520305408] (70007)The timeout specified has expired: [client 154.5.162.190:51384] AH01075: Error dispatching request to : (polling), referer: /?live_preview=1

[Sun Nov 14 09:54:34.477785 2021] [proxy_fcgi:error] [pid 24560:tid 139854579054336] [client 154.5.162.190:51384] malformed header from script 'index.php': Bad header: {"error":false,"message":"Ajax, referer: /page/edit/?id=8074

[Sun Nov 14 09:54:34.477904 2021] [proxy_fcgi:error] [pid 24560:tid 139854579054336] [client 154.5.162.190:51384] AH01070: Error parsing script headers, referer: /page/edit/?id=8074

[Sun Nov 14 09:54:34.477913 2021] [proxy_fcgi:error] [pid 24560:tid 139854579054336] (22)Invalid argument: [client 154.5.162.190:51384] AH01075: Error dispatching request to : , referer: /page/edit/?id=8074

The Network tab shows Response Headers: content-length: 0

I did a a little investigating to see if there are any known issues with SSE and these errors, but nothing useful so far.

 

 

  • Like 3
Link to comment
Share on other sites

@adrian Thanks for the help testing!

Quote

foreach($changes as $key => $change) {
I get: 
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'page_autosave_changes' doesn't exist

Just to make sure I understand, when this error appears, you are uninstalling the module while it is running in another window? I think I can fix that just by adding a try/catch in the appropriate location.

Quote

But back to the more important issue of why the module doesn't actually work for me. Firstly, the module does work on my local dev environment, but on a live servers, it doesn't.
I still keep getting: "ajax error: error"

Does it work for a few seconds, and then start getting errors? I am guessing that web hosts don't like scripts using SSE because it keeps an open connection and a PHP script that runs until the live preview window is closed. Perhaps web hosts see a non-terminating http request/PHP process and kill it automatically. Though the error messages in your log (after the first one) seem to indicate maybe there was some PHP error that preceded the response headers getting sent. It would be interesting to know if it's a 500 error from ProcessWire or an interpreted one from the browser. Do you see anything related in your Setup > Logs > errors ?

As for how to manage with web hosts that don't like long running processes, I'm thinking we'll add an auto-refresh setting that makes it refresh the live preview window after a certain number of seconds (like 30 seconds or whatever is configured). That way it can start over with the SSE at a regular interval and avoid long running processes. Another possibility is to skip SSE and use the ProDrafts strategy which is for the editor window to send a message to the preview window that it needs to get an update -- this might be a little slower but has been reliable. 

Link to comment
Share on other sites

2 hours ago, ryan said:

Just to make sure I understand, when this error appears, you are uninstalling the module while it is running in another window? I think I can fix that just by adding a try/catch in the appropriate location.

Yes - probably a strange thing to do, but the popup window was behind my main window so I didn't notice it. 

 

2 hours ago, ryan said:

Does it work for a few seconds, and then start getting errors?

It doesn't update at all - that error happens as soon as I stop typing in on of the fields - so as soon as it tries to trigger an update in the preview window.

My server is a VPS box so it shouldn't be any host specific limitations - or at least not any that I can't change. I have played around a little with some of the timeout settings, but no luck so far. No php errors that I can find either. I'll try to take a deeper look later.

Link to comment
Share on other sites

17 hours ago, ryan said:

Another possibility is to skip SSE and use the ProDrafts strategy which is for the editor window to send a message to the preview window that it needs to get an update -- this might be a little slower but has been reliable.

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.

  • Like 1
Link to comment
Share on other sites

Quote

It doesn't update at all - that error happens as soon as I stop typing in on of the fields - so as soon as it tries to trigger an update in the preview window.

@adrian Is the error in your preview window or the editor window? (sounds like preview window, but wanted to double check). 

wbmnfktr also had a live server where live preview wasn't working, and he set me up with access to test it there. I posted a new version (v3) that includes a non-SSE option and we found that this works on his live server where the SSE option didn't work. Though we found browser cache to be an issue, so had to open dev tools for cache to be disabled at least temporarily. Looks like I need to add a cache buster to the js file. 

To enable the option in the module settings, make sure you've got the latest posted version (v3) choose "Ajax" for "Enable live preview". Actually it may be pre-selected when you upgrade the module, as I think it's a better default. Make sure your browser cache is clear or that you've got dev tools open, at least if you find it doesn't work at first. 

Quote

My server is a VPS box so it shouldn't be any host specific limitations - or at least not any that I can't change. I have played around a little with some of the timeout settings, but no luck so far. No php errors that I can find either. I'll try to take a deeper look later.

I think any web server setup for hosting is likely to have some measures in place to prevent long running PHP scripts just by default. I'm starting to wonder if SSE is just not an ideal solution for a module like this that has to work everywhere. 

Quote

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.

@kongondo I was only making a guess, thinking that SSE has some native performance benefit. Now that I've implemented the communication between windows (The "Enable live preview > Ajax" option) I find it does seem like it's faster than when using SSE, though I've not benchmarked anything yet. 

 

 

  • Like 5
Link to comment
Share on other sites

The newest posted versions of PageAutosave (v3 and v4 in ProDevTools, ProDrafts, ProFields) adds the following:

  • Support for alternate event driven ajax live preview mode, which appears to provide improved performance and work in live environments where the streaming/SSE didn't. Since this seems to work better, it is the new default setting. The SSE option is still there but you have to manually select it in the module settings if you want it. 
     
  • Support for remembering live preview window position, plus automatically detecting and using an optimal preview window position. Meaning, it'll find the location on the screen where there is the most room for the preview window and then move and resize it there. Once the window is in position (or if you move or resize it) it'll remember your setting in a cookie so that you won't have to move and resize your live preview window every time you edit another page. 
     
  • Automatic insertion of pw-value-* classes in markup to support replacement of individual fields native to $page, without having to re-render the page. This means you no longer have to insert your own pw-value-* classes in markup for improved performance, as the module will do it for you when rendering a live preview request. If it causes any issues with your site, you can optionally turn off the feature in the module settings. 

 

 

  • Like 7
Link to comment
Share on other sites

@ryan - I think the biggest issue I am seeing is the actual autosave - that's what is causing the 500 error when making the AJAX call. If I the type in buildAjaxRequest() to GET, then I don't get the error any more. It's still not autosaving, but that's not surprising given that ProcessPageEdit is expect GET, but maybe it gives you a clue as to why I might be getting the error.

This is what debug is returning:

[
    "submit_autosave=1",
    "id=7689",
    "body=%3Cp%3Ehjhdh+dhjdk+dhjdkg+dfghdf%26nbsp%3B%3C%2Fp%3E%0D%0A",
    "TOKEN643048379X1637275316=pObPhEnWs6iGOEdu.4kZzL2ATScfNNl0"
]
{
    "type": "POST",
    "url": "./?id=7689&modified=1637275019&fields=body",
    "data": "submit_autosave=1&id=7689&body=%3Cp%3Ehjhdh+dhjdk+dhjdkg+dfghdf%26nbsp%3B%3C%2Fp%3E%0D%0A&TOKEN643048379X1637275316=pObPhEnWs6iGOEdu.4kZzL2ATScfNNl0",
    "dataType": "json"
}


Autosave error: ajax error: error (body)

 

  • Like 1
Link to comment
Share on other sites

Question to all of you... 

What about a setting that enables PageAutoSave O N L Y when in PreviewMode?

I play a lot with Page Autosave + Live Preview but sometimes I'm just tackling my daily tasks and the auto-save feature is (kind of) too much as I start writing but can't finish the text due to a phone call, "writer's block" or my empty coffee cup. Yet the content was entered, saved and is available on the frontend for page visitors. Luckily only within a side project at the moment but still.

I could enable the feature only for unpublished pages but most of the content that gets edited is already published and therefore live-content.

I'm almost missing a setting like "enable autosave only when livepreview-window is active".

What do you think?

  • Like 3
Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...