Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I made a few functions to handle an Ajax post request for a form inside a bootstrap modal, and then update the form errors / display a success message disappearing with a timer countdown upon valid form saving.

I don't have a lot of experience with jQuery and feel like this could be cleaned up some more.

I've include my Django template of the modal form so you can see how the HTML is structured for reference. And the view for the response data if these are needed.

Scripts/functions:

function removeErrors(form){

    $(form).find('.form-group').each(function(){
        if($(this).hasClass('has-error')){
            $(this).removeClass('has-error');
        }
        // Gets the <ul> of errors after the field rendered in template below
        $(this).next().empty();
    });
}

function addErrors(form, errors){

    for(var field in errors){
        $("#div_id_"+field, form).addClass('has-error');
        $.each(errors[field], function(k,v){
            // More html / styling here but minimized this.
            $('#'+field+"_errors", form).append("<li style='color:red;'>"+v+"</li>");
        }); 
    }
}

function updateForm(data, form){

    if(data.form_saved){
        $(form).get(0).reset();
        removeErrors(form);
        $(form).closest('.modal').modal('hide');
    }
    else{
        removeErrors(form);
        addErrors(form, data.form_errors);
    }
}

function showModalTimer(form, parent){

    parent.empty();
    var span = "<span id='timer'></span>";
    if($(form)[0].hasAttribute('name')){
        parent.append('Added a '+$(form).attr('name')+'! '+span)                
    }
    else{
        parent.append('Successfully added! '+span);
    }
    parent.fadeIn();
    var i = 10;
    window.setInterval(function(){
        $("#timer").text('Disappearing in '+i+'.');
        if (i == 0){
            clearInterval($(this));
            parent.fadeOut();
        }
        i--;
    }, 1000);
}

$("form").submit(function(e){
    e.preventDefault();
    var form_data = new FormData($(this).get(0));
    var form = this
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });

    $.ajax({
        type: $(this).attr('method'),
        url: $(this).attr('action'),
        data: form_data,
        cache: false,
        processData: false,
        contentType: false,
        success: function(data){
            updateForm(data, form);
            if (data.form_saved){
                showModalTimer(form, $("#modal-success"));
            }
        },
        error: function(error){
            console.log(error);
        },
    });
    return false;
});

One note about the below template. The enctype. I have forms that have ImageFields and hence the enctype. However, I'd like to still use this for fields without file fields. Will setting the enctype in this case to a blank string cause any issues?

Modal template:

{% load crispy_forms_tags %}
<div id="{{ modal_form_id }}" class="modal fade" ariahidden="True">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <a class="close" data-dismiss="modal">X</a>
        <h1 class="text-center">{{modal_form_title}}</h1>
      </div>
      <div class="modal-body">
        <form method="{{modal_form_method|default:'POST'}}" enctype="{{modal_form_enctype|default:''}}" action="{{modal_form_action}}" name="{{modal_form_name}}">
          {% csrf_token %}
          {{form.non_field.errors}}
          {% for field in form %}   
            {{field|as_crispy_field}}
            <ul id="{{field.name}}_errors"></ul>
          {% endfor %}
          <input class="btn btn-primary" type="submit" value="Create"/>
        </form>
      </div>
      <div class="modal-footer">
        <a href="#" class="btn btn-danger" data-dismiss="modal">Cancel</a>
      </div>
    </div>
  </div>
</div>

Example usage / include:

{% include "modal_form.html" with modal_form_id='new-group' modal_form_title="Create a New Group" modal_form_action="/home/" modal_form_name='group' form=form %}

One of my views:

def directory(request):

    if request.is_ajax():
        form = ArticleGroupForm(data=request.POST, files=request.FILES)
        if form.is_valid():
            instance = form.save()
            instance.save()
            response_data = {'form_saved':True, 'form_errors':None}
        else:
            response_data = {'form_saved':False,'form_errors':form.errors}
        return HttpResponse(json.dumps(response_data), content_type='application/json')
    else:
        form = ArticleGroupForm()
    groups = ArticleGroup.objects.all()
    recent = Article.objects.all().order_by('-created')[:10]
    return render(request, "directory.html"{'groups':groups,'recent':recent,'form':form})
share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.