Save on Escape?

A place for users and developers of the Xataface to discuss and receive support.

Postby Aoirthoir » Wed Jul 12, 2006 7:01 am

Ok in the current program they are running, if you type anything into any screen and then hit esc it does not ask if you want to save, and instead just closes out the screen without saving. Since a lot of the screens have lots of info this causes accidents to lose data a lot. Sometimes the person does not realize the data is lost and goes about their business.

Thus it was brought up to me by a member of the staff here, will the same happen with our new program (which will be in dataface). I realized, if you click to any link while on a form, most likely your data will not be saved. So I am wondering for a future update, how this could be handled? Would all links on an edit page have to be javascript code that would take note if something had been typed in a form before loading?

Just musing right now, I dont have many ideas on this since I had not considered it..

Ah..if you hit back would the data be in the form?
Aoirthoir
 
Posts: 420
Joined: Wed Dec 31, 1969 5:00 pm

Postby shannah » Wed Jul 12, 2006 7:30 am

Good point.. Dataface currently does not handle this .. you just have to be careful. The best and easiest way to remedy this will be to add a Javascript event handler to warn the user before he leaves the page if he has made changes to the form.

In 0.6 this won't be as intrusive as it would have been in 0.5.3 because 0.6 has a "View" tab so, presumably, the only reason someone would be in the "Edit" tab is if they intend to edit the record.
--
Steve Hannah
@shannah78 (on twitter)
sjhannah.com blog
shannah
 
Posts: 4457
Joined: Wed Dec 31, 1969 5:00 pm

Postby Aoirthoir » Thu Jul 13, 2006 6:47 am

Yes I thought probably a javascript would do it, and I would only have to do it on the links on the edit page. I would just have to set each link to run a script that firsts check to see if the data had been changed, and if not that calls the link. If so then a dialog box would be presented to warn the user. Also I could just set firefox somehow with an extension to display all links at our domain to pop up into a new window.

I agree with what you said about the view and edit tabs. Unfortunately we have a lot of temps and in the beginning they mess up stuff a lot. One that is no longer here and put down that the customer had ordered a bed, when they in fact ordered a wheelchair. It is insane sometimes:)

But with the regular staff I am going to just let them know PAY ATTENTION...maybe I could make some kinda HUGE color difference for an edit screen. And in any case it will still be better than what we have because they wont be able to just hit esc or another key and lose their changes.

Also I thought about this last night..And I verified in the morning... If someone is entering data in a dataface screen, and they click another link...if they hit BACK then the data they entered will be in the form..though not saved yet.
Aoirthoir
 
Posts: 420
Joined: Wed Dec 31, 1969 5:00 pm

Postby Aoirthoir » Thu Jul 13, 2006 7:18 am

Ok I talked with the fella who brought this issue up to me. So I came to a conclusion. I'll give you a little background...

As you know I am working on some MySQL code in order to create a history table. The way this will work is, each time a record is saved, it is saved to its table and a nearly identical copy is saved to a history table (with some added fields for maintenance, like date of change who made change and so on...) This starts with the very first insert of the record and follows through all updates and even deletes..the history stays intact even if the main record is deleted.

So we concluded maybe an AUTOSAVE would be good. Each time they hit enter after making a change to a field (only if a field is changed though) the record is saved. That way even if they close out of the browser, their changes are saved. However this would cause the history table to grow exponentially.

So the solution we concluded is, autosave goes to a temp table. This has very much the layout of the history table for any particular table. Temp tables do not get their own history. They are like a mirrored reflection of the original table. Then at the end of editing if they click save, the record in the temp table is deleted. If however they end up forgetting to save, the record remains intact. If there is thus ever a view or an edit made on that record in the future, the code would always check to see if that record existed also in the temp table. If it did it would display both side by side (or temp field contents below actual field contents). Thus the person could choose at that point to save the temp contents into the actual field contents. If they did then the same process above would occur. The temp record would be deleted and the history would be inserted (history is always an insert) and the original table would be updated. If they chose not to write out the temp info that record would at that time be simply deleted.

Now this isnt so much a feature request. I think this is more of a thing of adding an action or some code within my dataface configs myself. Also i told them this is a LONG time away. for now they are just going to have to be very very careful. Mostly this is going to have to be an AJAZ addition and I am not ready for that yet. So it might be months or longer before I tackle this thing.
Aoirthoir
 
Posts: 420
Joined: Wed Dec 31, 1969 5:00 pm

Postby shannah » Thu Jul 13, 2006 9:50 am

The javascript onbeforeunload event handler is how most systems implement this sort of thing.

e.g.:

Code: Select all
    function warnOnLeave(){
       return "There may be unsaved changes in the form.";
   }
   window.onbeforeunload = warnOnLeave;



This will produce a warning window asking the user if they are sure they want to navigate away from this page with an OK and Cancel button when they try to leave the page.

The only tricky part is making this only show up if unsaved changes are made to the form.

Plone contains some javascript code to handle this completely. If would have to be modified somewhat to suit Dataface but probably not that much. It's code goes like this:

Code: Select all
/* BeforeUnload form processing */
if (!window.beforeunload) (function() {

    /*  this function is designed to replace form onsubmit handlers
     *  and calls the original onsubmit handler that may have been
     *  overidden by third party scripts (like Epoz)
     */
    function plone_onsubmit(event) {
        var tool = window.onbeforeunload && window.onbeforeunload.tool;
        tool.submitting = true;
   if (this.plone_saved_onsubmit) {
            this.plone_saved_onsubmit(event)
        }
    }

    var BeforeUnloadHandler = function() {
        var self = this;

        this.message = window.form_modified_message ||
            "Your form has not been saved. All changes you have made will be lost.";

        this.forms = [];
        this.chkId = [];
        this.chkType = new this.CheckType();
        this.handlers = [this.isAnyFormChanged];
        this.submitting = false;

        this.execute = function(event) {
            if (self.submitting) return;
            if (!event) event = window.event;

            for (var i = 0; i < self.handlers.length; i++) {
                var fn = self.handlers[i];
                var message = message || fn.apply(self);
            }
            if (message===true) message = self.message;
            if (message===false) message = undefined;
            if (event && message) { event.returnValue = message; }
            return message;
        }
        this.execute.tool = this;
    }
    var Class = BeforeUnloadHandler.prototype;

    // form checking code
    Class.isAnyFormChanged = function() {
        for (var i=0; i < this.forms.length; i++) {
            var form = this.forms[i];
            if (this.isElementChanged(form)) {
                return true;
            }
        }
        return false;
    }
    Class.addHandler = function(fn) {
        this.handlers.push(fn);
    }
    Class.addForm = function(form) {
        for (var i = 0; i < this.forms.length; i++) {
            if (this.forms[i]==form) return;
        }
        this.forms.push(form);
        form.plone_saved_onsubmit = form.onsubmit
        form.onsubmit = plone_onsubmit;
        var elements = form.getElementsByTagName('input');
        for (var j = 0; j < elements.length; j++) {
            var ele = elements[j];
            if (ele.type=='hidden') {
                ele.setAttribute('originalValue', ele.defaultValue);
            }
        }
    }
    Class.addForms = function() {
        for (var i = 0; i < arguments.length; i++) {
            var element = arguments[i];
            if (!element) continue;
            if (element.tagName=='FORM') {
                this.addForm(element);
            }
            else {
                var forms = element.getElementsByTagName('form');
                for (var j = 0; j < forms.length; j++) {
                    this.addForm(forms[j]);
                }
            }
        }
    }
    Class.removeForms = function() {
        for (var i = 0; i < arguments.length; i++) {
            var element = arguments[i];
            if (!element) continue;
            if (element.tagName=='FORM') {
                for (var j = 0; j < arguments.length; j++) {
                    if (this.forms[j] == element) {
                        this.forms.splice(j--, 1);
                        element.onsubmit=null;
                    }
                }
            } else {
                var forms = element.getElementsByTagName('form');
                for (var j = 0; j < forms.length; j++) {
                    this.removeForms(forms[j]);
                }
            }
        }
    }

    Class.CheckType = function() {};
    var c = Class.CheckType.prototype;
    c.checkbox = c.radio = function(ele) {
        return ele.checked != ele.defaultChecked;
    }
    c.password = c.textarea = c.text = function(ele) {
        return ele.value != ele.defaultValue;
    }
    // hidden: cannot tell on Mozilla without special treatment
    c.hidden = function(ele) {
        var orig = ele.getAttribute("originalValue");
        return orig && (ele.value != orig);
    }

    c['select-one'] = function(ele) {
        for (var i=0 ; i < ele.length; i++) {
            var opt = ele.options[i];
            if ( opt.selected != opt.defaultSelected) {
                if (i===0 && opt.selected) continue; /* maybe no default */
                return true;
            }
        }
        return false;
    }

    c['select-multiple'] = function(ele) {
        for (var i=0 ; i < ele.length; i++) {
            var opt = ele.options[i];
            if ( opt.selected != opt.defaultSelected) {
                return true;
            }
        }
        return false;
    }

    Class.chk_form = function(form) {
        var elements = form.elements;
        for (var i=0; i < elements.length; i++ ) {
            var element = elements[i];
            if (this.isElementChanged(element)) {
                return true;
            }
        }
        return false;
    }

    Class.isElementChanged = function(ele) {
        var method = ele.id && this.chkId[ele.id];
        if (!method && ele.type && ele.name)
            method = this.chkType[ele.type];
        if (!method && ele.tagName)
            method = this['chk_'+ele.tagName.toLowerCase()];

        return method? method.apply(this, [ele]) : false;
    };

    window.onbeforeunload = new BeforeUnloadHandler().execute;
   
    registerPloneFunction(function() {
        // terminate if we hit a non-compliant DOM implementation
        if (!W3CDOM){return false};

        var tool = window.onbeforeunload && window.onbeforeunload.tool;
        var content = getContentArea();
        if (tool && content) {
            var forms = cssQuery('form.enableUnloadProtection');
            for (var i=0; i < forms.length; i++) {
                tool.addForm(forms[i]);
            }
        }
    });
})();


I'm not ready to add this in at this point, but it could be done with just a little tweaking.
--
Steve Hannah
@shannah78 (on twitter)
sjhannah.com blog
shannah
 
Posts: 4457
Joined: Wed Dec 31, 1969 5:00 pm

Postby Aoirthoir » Thu Jul 13, 2006 1:25 pm

Thanks Steve. Actually I think the one that you posted first might just be sufficient. It is short and simple. And would only have to be run on an edit form. And eventually I will add the stuff up there that I mentioned. They already know we are months away from that though.
Aoirthoir
 
Posts: 420
Joined: Wed Dec 31, 1969 5:00 pm


Return to Xataface Users

Who is online

Users browsing this forum: No registered users and 15 guests

cron
Powered by Dataface
© 2005-2007 Steve Hannah All rights reserved