Some hotfixes to previous commit. Client-side file validation added (except file type validation). Some refactoring.

This commit is contained in:
Matej Zrimšek 2016-07-18 13:16:41 +02:00
parent e2f94caa96
commit 01aad1764c
9 changed files with 172 additions and 167 deletions

View file

@ -47,7 +47,6 @@
data.push("file_size=" + file.size);
data.push(csrfParam + "=" + encodeURIComponent(csrfToken));
if (origId) {
data.push("asset_id=" + origId);
}
@ -110,9 +109,7 @@
if (!isValid) {
cbErr();
return;
}
} else {
fetchUploadSignature(file, origId, signUrl, function (data) {
function processPost(error) {
@ -124,10 +121,12 @@
errObj[errKey] = [error];
cbErr(errObj);
isValid = false;
return;
}
if (!postData) {
cb(data.asset_id);
isValid = false;
return;
}
@ -153,6 +152,7 @@
if (!data || data.status === 'error') {
cbErr(data && data.errors);
isValid = false;
return;
}
@ -161,6 +161,9 @@
processPost();
});
}
return isValid;
};
}(this));

View file

@ -311,10 +311,10 @@ function startFileUpload(ev, btn) {
var origAssetId = assetInput ? assetInput.value : null;
var url = '/asset_signature.json';
animateSpinner();
$form.clear_form_errors();
animateSpinner();
directUpload(form, origAssetId, url, function (assetId) {
var noErrors = directUpload(form, origAssetId, url, function (assetId) {
// edit mode - input field has to be removed
if (assetInput) {
assetInput.value = assetId;
@ -334,7 +334,11 @@ function startFileUpload(ev, btn) {
showResultFormErrors($form, errors);
});
if(!noErrors) {
animateSpinner(null, false);
}
ev.preventDefault();
return noErrors;
}
// This checks if the ctarget param exist in the

View file

@ -162,11 +162,6 @@ function formCallback($form) {
}
});
// Add asset validation
$form.add_upload_file_size_check(function() {
tabsPropagateErrorClass($form);
});
// Add hidden fields for tables
$form.submit(function(){
$(this).find("[data-role='editable-table']").each(function() {
@ -589,7 +584,7 @@ $("[data-action='new-step']").on("ajax:success", function(e, data) {
});
function nameValidator(event) {
var form = $(event.target.form);
var $form = $(event.target.form);
var nameTooShort = $( "#step_name" ).val().length === 0;
var nameTooLong = $( "#step_name" ).val().length > 50;
var errMsg;
@ -602,20 +597,19 @@ function nameValidator(event) {
var hasErrors = !_.isUndefined(errMsg);
if (hasErrors) {
renderError($("#step_name"), errMsg, form);
renderError($("#step_name"), errMsg);
}
return !hasErrors;
}
function checklistsValidator(event, editMode) {
var form = event.target.form;
$(form).clear_form_errors();
var $form = $(event.target.form);
var noErrors = true;
// For every visible (i.e. not removed) checklist
$(form).find(".nested_step_checklists[style!='display: none']").each(function() {
var checklistNameInput = $(this).find(".checklist_name");
var checklistNameEmpty = !checklistNameInput.val();
$form.find(".nested_step_checklists[style!='display: none']").each(function() {
var $checklistNameInput = $(this).find(".checklist_name");
var checklistNameEmpty = !$checklistNameInput.val();
anyChecklistItemFilled = false;
// For every ckecklist item input
@ -632,7 +626,7 @@ function checklistsValidator(event, editMode) {
if(anyChecklistItemFilled || editMode) {
// In edit mode, name can't be blank
var errMsg = I18n.t("devise.names.not_blank");
renderError(checklistNameInput, errMsg, $(form));
renderError($checklistNameInput, errMsg);
noErrors = false;
} else {
// Hide empty checklist
@ -648,28 +642,30 @@ function checklistsValidator(event, editMode) {
// Needed because server-side validation failure clears locations of
// files to be uploaded and checklist's items etc. Also user
// experience is improved
function localStepValidator(event, editMode) {
function stepValidator(event, editMode, forS3) {
var $form = $(event.target.form);
$form.clear_form_errors();
if(!editMode) {
// Most td's disappear when editing step and not pressing on
// edit tab, so we can't use this function
clearBlankTables(event.target.form)
// tables tab, so we can't use this function
clearBlankTables($form)
}
clearBlankFileForms(event.target.form);
clearBlankFileForms($form);
// TODO File type check
var fileSizeValid = uploadFileSizeCheck(event);
var checklistsValid = checklistsValidator(event, editMode);
var nameValid = nameValidator(event);
var noErrors = checklistsValid && nameValid;
if(noErrors) {
// Validations passed, so animate spinner for possible file
// uploading
if(fileSizeValid && checklistsValid && nameValid) {
if(forS3) {
// Needed to redirect uploaded files to S3
startFileUpload(event, event.target);
} else {
// Files are saved locally
// Validations passed, so animate spinner for possible file uploading
// (startFileUpload already calls it)
animateSpinner();
}
return noErrors;
}
function S3StepValidator(event, editMode) {
if(localStepValidator(event, editMode)) {
startFileUpload(event, event.target);
}
}
@ -731,9 +727,9 @@ function startFileUpload(ev, btn) {
var inputPos = 0;
var inputPointer = 0;
animateSpinner();
$form.clear_form_errors();
clearBlankFileForms(form);
animateSpinner();
function processFile () {
var fileInput = fileInputs.get(inputPos);
@ -743,17 +739,16 @@ function startFileUpload(ev, btn) {
if (!fileInput) {
btn.onclick = null;
$(btn).click();
return;
return false;
}
directUpload(form, null, url, function (assetId) {
return directUpload(form, null, url, function (assetId) {
fileInput.type = "hidden";
fileInput.name = fileInput.name.replace("[file]", "[id]");
fileInput.value = assetId;
inputPointer -= 1;
processFile();
}, function (errors) {
var assetError;
@ -768,16 +763,19 @@ function startFileUpload(ev, btn) {
var $el = $(el);
$form.clear_form_errors();
$el.closest(".form-group").addClass("has-error");
$el.parent().append("<span class='help-block'>" + assetError + "</span>");
renderError($el, assetError);
} else {
tabsPropagateErrorClass($form);
}
});
}
processFile();
var noErrors = processFile();
if(!noErrors) {
animateSpinner(null, false);
}
ev.preventDefault();
return noErrors;
}
// Remove empty file forms in step

View file

@ -19,10 +19,7 @@ function checkFilesValidity(fileInputs) {
input.closest('.form-group').removeClass('has-error');
input.parent().find("[data-error='file-size']").remove();
if (assetError) {
input.closest('.form-group').addClass('has-error');
input.parent().append(
"<span class='help-block' data-error='file-size'>" + assetError + "</span>"
);
renderError(input, assetError, "data-error='file-size'");
isValid = false;
}
});

View file

@ -14,7 +14,6 @@ $.fn.render_form_errors_input_group = function(model_name, errors) {
$.fn.render_form_errors_no_clear = function(model_name, errors, input_group) {
var form = $(this);
var firstErr = true;
$.each(errors, function(field, messages) {
input = $(_.filter(form.find('input, select, textarea'), function(el) {
var name = $(el).attr('name');
@ -34,17 +33,6 @@ $.fn.render_form_errors_no_clear = function(model_name, errors, input_group) {
} else {
input.parent().append(error_text);
}
if(firstErr) {
// Focus and scroll to the first error
input.focus();
firstErr = false;
$('html, body').animate({
scrollTop: input.closest(".form-group").offset().top
- ($(".navbar-fixed-top").outerHeight(true)
+ $(".navbar-secondary").outerHeight(true))
}, 2000);
}
});
};
@ -68,13 +56,19 @@ $.fn.clear_form_fields = function() {
// Callback function can be provided to be called
// any time at least one file size is too large
$.fn.add_upload_file_size_check = function(callback) {
var form = $(this);
var $form = $(this);
if (form.length && form.length > 0) {
form.submit(function (ev) {
var fileInputs = $(this).find("input[type='file']");
if (fileInputs.length && fileInputs.length > 0) {
var isValid = checkFilesValidity(fileInputs);
if ($form.length && $form.length > 0) {
$form.submit(function (ev) {
uploadFileSizeCheck(ev, callback);
});
}
};
function uploadFileSizeCheck(ev, callback) {
var $fileInputs = $(ev.target.form).find("input[type='file']");
if ($fileInputs.length && $fileInputs.length > 0) {
var isValid = checkFilesValidity($fileInputs);
if (!isValid) {
// Don't submit form
@ -88,47 +82,49 @@ $.fn.add_upload_file_size_check = function(callback) {
return false;
}
}
});
}
};
return true;
}
// Show error message and mark error element and, if present, mark
// and show the tab where the error occured.
// NOTE: Similar to $.fn.render_form_errors, except here we process
// one error at a time, which is not read from the form but is
// specified manually.
function renderError(nameInput, errMsg, form) {
var errMsgSpan = nameInput.next(".help-block");
if(!errMsgSpan.length) {
nameInput.after("<span class='help-block'>" + errMsg + "</span>");
nameInput.closest(".form-group").addClass("has-error");
function renderError(nameInput, errMsg, errAttributes) {
var $errMsgSpan = $(nameInput).next(".help-block");
if(!$errMsgSpan.length) {
errAttributes = (_.isUndefined(errAttributes)) ? "" : " " + errAttributes;
$(nameInput).after("<span class='help-block'" + errAttributes + ">" + errMsg + "</span>");
$(nameInput).closest(".form-group").addClass("has-error");
} else {
errMsgSpan.html(errMsg);
$errMsgSpan.html(errMsg);
}
$form = $(nameInput).closest("form");
$tab = $(nameInput).closest(".tab-pane");
if($tab.length) {
tabsPropagateErrorClass($form);
$parent = $tab;
} else {
$parent = $form;
}
tabsPropagateErrorClass($(form));
// Focus and scroll to the error if it is the first (most upper) one
if($(form).find(".form-group.has-error").length === 1) {
nameInput.focus();
$('html, body').animate({
scrollTop: nameInput.closest(".form-group").offset().top
- ($(".navbar-fixed-top").outerHeight(true)
+ $(".navbar-secondary").outerHeight(true))
}, 2000);
if($parent.find(".form-group.has-error").length === 1) {
goToFormElement(nameInput);
}
event.preventDefault();
}
// If any of tabs has errors, add has-error class to
// parent tab navigation link
// If any of tabs (if exist) has errors, add has-error class to
// parent tab navigation link and show the tab (if not already)
function tabsPropagateErrorClass(parent) {
var contents = parent.find("div.tab-pane");
if(contents.length) {
_.each(contents, function(tab) {
var $contents = parent.find("div.tab-pane");
_.each($contents, function(tab) {
var $tab = $(tab);
var errorFields = $tab.find(".has-error");
if (errorFields.length > 0) {
var $errorFields = $tab.find(".has-error");
if ($errorFields.length > 0) {
var id = $tab.attr("id");
var navLink = parent.find("a[href='#" + id + "'][data-toggle='tab']");
if (navLink.parent().length > 0) {
@ -136,6 +132,5 @@ function tabsPropagateErrorClass(parent) {
}
}
});
$(".nav-tabs .has-error:first > a", parent).tab("show");
}
$(".nav-tabs .has-error:first:not(.active) > a", parent).tab("show");
}

View file

@ -0,0 +1,13 @@
// Scroll to and focus on element
function goToFormElement(el) {
$("html, body").animate(
{
scrollTop: $(el).closest(".form-group").offset().top
- ($(".navbar-fixed-top").outerHeight(true)
+ $(".navbar-secondary").outerHeight(true)
+ $(".alert-dismissable").outerHeight(true))
},
"slow",
function() { $(el).focus(); }
);
}

View file

@ -75,7 +75,7 @@ function startFileUpload(ev, btn) {
$form.clear_form_errors();
animateSpinner($form);
directUpload(form, null, url, function (assetId) {
var noErrors = directUpload(form, null, url, function (assetId) {
var file = fileInput.files[0];
fileInput.type = "hidden";
fileInput.name = fileInput.name.replace("[avatar]", "[avatar_file_name]");
@ -103,12 +103,15 @@ function startFileUpload(ev, btn) {
var $el = $form.find("input[type=file]");
$form.clear_form_errors();
$el.closest(".form-group").addClass("has-error");
$el.parent().append("<span class='help-block'>" + avatarError + "</span>");
renderError($el, avatarError);
}
}, "avatar");
if(!noErrors) {
animateSpinner(null, false);
}
ev.preventDefault();
return noErrors;
}

View file

@ -4,11 +4,7 @@
<hr>
<%= render partial: "empty_step.html.erb", locals: {step: @step, f: f} %>
<hr>
<% if direct_upload %>
<%= f.submit t("protocols.steps.edit.edit_step"), class: 'btn btn-primary', onclick: 'S3StepValidator(event, true);' %>
<% else %>
<%= f.submit t("protocols.steps.edit.edit_step"), class: 'btn btn-primary', onclick: 'localStepValidator(event, true);' %>
<% end %>
<%= f.submit t("protocols.steps.edit.edit_step"), class: 'btn btn-primary', onclick: "stepValidator(event, true, #{direct_upload});" %>
<a type="button" data-action="cancel-edit" class="btn btn-default" href="<%= step_path(id: @step, format: :json) %>" data-remote="true">
<%= t("general.cancel")%>
</a>

View file

@ -4,11 +4,7 @@
<hr>
<%= render partial: "empty_step.html.erb", locals: {step: @step, f: f} %>
<hr>
<% if direct_upload %>
<%= f.submit t("protocols.steps.new.add_step"), class: 'btn btn-primary', onclick: 'S3StepValidator(event, false);' %>
<% else %>
<%= f.submit t("protocols.steps.new.add_step"), id: "create-step", class: 'btn btn-primary', onclick: 'localStepValidator(event, false);' %>
<% end %>
<%= f.submit t("protocols.steps.new.add_step"), class: 'btn btn-primary', onclick: "stepValidator(event, false, #{direct_upload});" %>
<button type="button" data-action="cancel-new" class="btn btn-default">
<%= t("general.cancel")%>
</button>