digitallyinduced / ihp

🔥 The fastest way to build type safe web apps. IHP is a new batteries-included web framework optimized for longterm productivity and programmer happiness
https://ihp.digitallyinduced.com/
MIT License
4.92k stars 195 forks source link

redirectTo / redirectToPath / redirectToUrl mutes JS in destination page #451

Closed fegu closed 3 years ago

fegu commented 4 years ago

Morphdom issue

Whenever a page contains javascript added by the developer, this javascript will NOT be executed if a user comes to the page after a form submit or a delete action. The reason is that IHP gets the page content via an AJAX call and uses morphdom to render it. Morphdom does not execute SCRIPT-tags it finds in the new content to be rendered.

The consequence is that any use of your own JS or common JS GUI helpers such as select2 or datatable.js works fine when linked to, but fails if the page is the target after a form submit or delete.

To reproduce: add a select2 dropdown to any edit view. Have the edit view be rendered after an update (UpdateAction ends with render EditView or redirectTo EditAction ). Watch the GUI select control gracefully degrade from select2 to ordinary select after submit.

IHP uses some JS helper libs itself, such as flatpickr. Special workarounds has been implemented in helpers.js to avoid this problem. Clearly we can't add workarounds for any JS lib out there, we need a generic solution. But a step-by-step guide on how to add similar custom workarounds for new JS libraries would still be a great help.

Other users of morphdom have noticed the same issue (see https://github.com/patrick-steele-idem/morphdom/issues/178). I have tried the fix shown there, but it did not work.

Turbolinks issue

Turbolinks can easily get confused when clicking around between pages using javascript (for instance the common datatable.js grid gets duplicate GUI elements). I have therefore disabled turbolinks altogether.

No Workaround

While completely disabling turbolinks and morphdom is mentioned in the Guide as an option, this does not work. While turbolinks can be removed without any issues, morphdom cannot. Removing morphdom gives a runtime javascript error from helpers.js.

I would like to keep these features when they do work, and I would like to run as "standard" IHP as possible. Also, in the future, when this issue is fully resolved, I would like to have an easy upgrade path. However, at this time removing both morphdom and turbolinks is the only workaround that works (and we currently can't remove morphdom)

Suggested workaround

I have made a workaround to be used per controller that makes form submits (and delete) work by selectively bypassing morphdom on the rendering for selected events. Unless better suggestions appear, I will make a PR with this soon.

unhammer commented 3 years ago

I don't know if this helps with the form submit (guessing not), but I did find a method that works nicely for keeping datatables around while navigating with turbolinks: Remove datatables on the turbolinks:visit event, then add it again on turbolinks:load.

function initDataTables () {
    console.log("initDataTables");
    const options = {
        paging: false,
        info: false,
        stateSave: true, // don't clear search input on navigate
    };
    if($.fn.dataTable.isDataTable("#mydatatable")) {
        console.log("datatable already inited, not initing again");
    }
    else {
        console.log("datatable initing");
        $('#mydatatable').DataTable(options);
    }
}

function destroyDataTables () {
    console.log("datatable destroy");
    $('#mydatatable').DataTable().destroy();
}

// for dev server:
document.addEventListener('DOMContentLoaded', initDataTables);
// for prod server:
document.addEventListener('turbolinks:load',  initDataTables);
document.addEventListener('turbolinks:visit',  destroyDataTables);

(EDIT: changed before-visit to visit, since before-visit isn't called on browser back/forth history navigation)

mpscholten commented 3 years ago

I just added formForWithoutJavascript to allow disabling the javascript helpers for a specific form:

renderForm post = formForWithoutJavascript post [hsx|
|]

This sets a data-disable-javascript-submission attribute on the form. The helpers.js will pick this up and then use normal browser-based form submission.

The commit that introduces this function is https://github.com/digitallyinduced/ihp/commit/2422bbdfa6165231e89b44c2e7d1c68b65f6b3b4

Does that solve the problem?

mpscholten commented 3 years ago

Closing this as we have formForWithoutJavascript now (see prev comment) :)