Auto Save of Model. With attempt to return focus to a specific field after Save.

  • 3
  • Question
  • Updated 5 years ago
  • Answered
  • (Edited)
I have implemented an auto-save feature for an end user (as per this post
https://community.skuidify.com/skuid/...), but the thing that is a bit of a showstopper at the moment is that after a save, the focus is removed from the field the user was in. This could be quite annoying :)

So I was wondering if there is a solution to set focus to a specific field, based on a variable, after the save - this is my non-working solution so far:


//Get the current field
var foc = $(document.activeElement);

//Do the save
...e.g. skuid.model.save

//Set the focus
$(foc).focus()
Photo of John Davies

John Davies

  • 164 Points 100 badge 2x thumb

Posted 5 years ago

  • 3
Photo of Zach McElrath

Zach McElrath, Employee

  • 48,984 Points 20k badge 2x thumb
Here is the code for an Inline JavaScript Resource that will perform autosave on a given Model after the user has stopped making changes, with a fixed delay to wait before performing the save. After the save completes, an attempt is made to refocus on the last field the user was typing immediately before the save, and also to set the cursor position to the last point the user was at before the save, using 2 jQuery UI extensions Skuid includes: "getCursorPosition" and "setCursorPosition".



(function(skuid){
var $ = skuid.$;
$(function(){

// The delay after which to perform a save
var DELAY = 5000;
// The name of the Model you wish to autosave.
var NAME_OF_MODEL_TO_AUTOSAVE = 'Account';

var editor = new skuid.ui.Editor();
var inputModel = skuid.model.getModel(NAME_OF_MODEL_TO_AUTOSAVE);

editor.registerModel(inputModel);

// Find all changes in the input model,
// and apply them to the record in our 'save' model.
var saveChanges = function(){

var activeEl = $(document.activeElement),
activeField = activeEl.closest('.nx-field'),
fieldId,
rowId,
cursorPosition,
editorEl;
if (activeField && activeField.length) {
var fieldObj = activeField.data('object');
if (fieldObj) {
fieldId = fieldObj.id;
rowId = fieldObj.row.Id;
cursorPosition = activeEl.getCursorPosition();
editorEl = fieldObj.editor.element;
}
}

inputModel.save({
callback:function(result){
if (result.totalsuccess) {
// All saves completed successfully.

// Go re-focus the selected field
if (editorEl) {
$.each(editorEl.data('object').list.renderedItems[rowId].fields,function(){
if (this.id === fieldId) {
var input = this.element.find(':input');
input.focus().setCursorPosition(cursorPosition);
}
});
}

} else {
// Something went wrong.
}

}
});

};
var timer;
editor.handleChange = function(){
// Reset our timer, if we're already scheduled to save changes
if (timer) {
clearTimeout(timer);
}
// Schedule a timeout to save changes
timer = setTimeout(saveChanges,DELAY);
};
// This will be called if our input model was manually saved/cancelled/refreshed by the user
editor.handleSave = editor.handleCancel = editor.handleDataRefresh = function() {
clearTimeout(timer);
};

});
})(skuid);

Photo of John Davies

John Davies

  • 164 Points 100 badge 2x thumb
That is awesome, thanks Zach.  I have adapted the code to save multiple models i.e. :
 
skuid.model.save([
                inputModel1,inputModel2,inputModel3],
                                    {callback:function(result)...
Also I found that there was a problem with email and checkbox fields with the setCursorPosition function so I made a change to test for those types before calling setCursorPosition.

Finally because my page is quite complex and has 2 related lists in edit mode which need to be autosaved, I had a problem with new rows failing autosave, because they don't have a 'proper' id yet.  My solution/hack is as follows, I expect there is a much better way to do this but it works:

firstly when identifying the 'active' row:

    //If rowId is not a Salesforce record Id, it is a 
    //new record                      
    if(rowId.length < 18){
      rowId = 0;
    }

and then when finding the control to focus back on:
    if(rowId !=0) {
        //Existing row
        fields = editorEl.data('object').list.renderedItems [rowId].fields;
    }
    else {
        //New row
        fields = editorEl.data('object').list.visibleItems[ 0].fields;
    }

Thanks again for the solution Zach, much appreciated.
(Edited)