1

Requirement: I have a referral form, where user can fill in the details to apply for a job. Now, As per my requirement, if a user wants to reapply for the same job, he can do so, multiple number of times (humor me on this). So, If I detect that user has already applied for the job before then I need to update the old referral request, if not then create a new request.

Current Behaviour: I have successfully written the logic to overwrite the existing value (if user had already applied before). But I want to show a pop up or some confirmation, if I detect that the user has already applied to this job, before updating resume and datetime and other fields. And once user says Yes, then only I want to overwrite the values.

Issue: I have no clue how to achieve above. Because once the request goes to backend controller action then only I can detect that this is something new or this is something which is already existing and needs to be overwritten and a POP UP needs to be shown.

View:

@model Bridge.ViewModels.ReferralViewModel
@using (Html.BeginForm())
{
    <div class="form-horizontal">
        <h4>Job Request</h4>
        <div class="form-group">
            @Html.LabelFor(model => model.CompanyId, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(m => m.CompanyId, Model.Companies, "Please Select", new { @class = "form-control", data_url = Url.Action("ListOfCoverLetterByCompanyId", "Referral") })
            </div>
        </div>
       @* SOME FIELDS REMOVED FOR BREVITY*@
        <div class="form-group">
            @Html.LabelFor(model => model.ResumeId, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(m => m.ResumeId, Model.Resumes, new { @class = "form-control" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.CoverLetterId, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(m => m.CoverLetterId, Model.CoverLetters, "Please select", new { @class = "form-control" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

Controller Action

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ReferralViewModel viewModel)
{
    var candidateId = User.Identity.GetUserId();
    var referral = _context.Referrals.Where(r => (r.CandidateId == candidateId) && (r.CompanyId == viewModel.CompanyId)).SingleOrDefault();
    if (referral != null)
    {
        // if already existing then show a confirmation/warning before updating
        referral.ResumeId = viewModel.ResumeId;
        referral.CoverLetterId = viewModel.CoverLetterId;
        referral.dateTime = DateTime.Now;
    }
    else
    {
      // If new then add the entry
    }
    _context.SaveChanges();

    return RedirectToAction("ReferralCenter");
}

EDIT

Attempt 1:

View:

@section scripts {
        <script>
            $('form').submit(function () {
                var data = $('form').serialize();
                var url = '@Url.Action("CheckForExistingReferral")';
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success(data)
                    {
                        debugger;
                        alert("hello");
                        // some logic
                    },
                    error()
                    {
                        debugger;
                    alert("error");
                     //some logic
                }
                });
            });     
        </script>
    }

AJAX Action:

[HttpPost]
public JsonResult CheckForExistingReferral(ReferralViewModel viewModel)
{
    bool hasPreviousRequest = false;
    var candidateId = User.Identity.GetUserId();
    var referral = _context.Referrals.Where(r => (r.CandidateId == candidateId) && (r.CompanyId == viewModel.CompanyId) && (r.SkillId == viewModel.SkillId) && (string.IsNullOrEmpty(r.ReferrerId))).SingleOrDefault();
    hasPreviousRequest = referral != null;
    return Json(new { hasPreviousRequest = hasPreviousRequest });

}

Issue:

Breakpoint at JSON Function gets hit, but before it can return the result, my Actual Form POST Method breakpoint gets hit. I want the Form POST controller action to wait for AJAX call return.

Attempt 2

View

@section scripts {
    <script>
            var canSubmit = false;
            debugger;
            $('form').submit(function (e) {
                if (!canSubmit)
                    e.preventDefault();
                var data = $('form').serialize();
                var url = '@Url.Action("CheckForExistingReferral")';
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success(response){
                        if (response.hasPreviousRequest)
                        {
                            if (confirm("You've already applied for this job. Apply again?")) {
                                canSubmit = true;
                                $('form').submit();
                            }
                        }
                        else {
                            canSubmit = true;
                            $('form').submit();
                        }
                    },
                    error(){
                       alert("error");
                    }
                }); 
            });
        </script>
    }

Issue: For single click, my Json Action gets hit atleast 3 times. It should be hit just once.

Attempt 3

Gave an Id to the form:

@using (Html.BeginForm("Create", "Referral", FormMethod.Post, new { id = "job-form" }))
{}

Script:

@section scripts {
    <script>
            var canSubmit = false;
            debugger;
            $('#job-form').submit(function (e) {
                if (!canSubmit)
                    e.preventDefault();
                var data = $('#job-form').serialize();
                var url = '@Url.Action("CheckForExistingReferral")';
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function (response) {
                        if (response.hasPreviousRequest)
                        {
                            if (confirm("You've already applied for this job. Apply again?")) {
                                canSubmit = true;
                                $('#job-form').submit();
                            }
                        }
                        else {
                            canSubmit = true;
                            $('#job-form').submit();
                        }
                    },
                    error: function (){ 
                       alert("error");
                    }
                }); 
            });
        </script>
    }

ISSUE: I keep on seeing the JS Pop up in a loop.

Unbreakable
  • 7,776
  • 24
  • 90
  • 171
  • 2
    You could include a property in your view model (and array of the Company ID's the user has previous applied for) and check if the selected option is in that array and use javascript to display a popup before submitting. Or you could use ajax to call a server method (passing the value of the selected option) to return a value indicating if the user has applied previously and then display a popup before submitting –  Sep 03 '17 at 22:18
  • Sir, actually, one duplicacy of one referral request, is dependent on many properties. such as "skillId", "companyId", "candidateId" and "a bool value that that referral request has already been served or not? (only allow if the requst is open). So, after checking all these fields only I decide that it is to be treated as existing one or a new one. – Unbreakable Sep 03 '17 at 22:22
  • Basically, it's same company for different skills set, I will treat it as new. So, I want to follow your ajax approach. So I cannot just go off of company lists. Sorry that I did not include that information in the question – Unbreakable Sep 03 '17 at 22:22
  • I have learned about making ajax call. So, on form submit I need to make an ajax call? And then return true or false? and then again submit the form? Can you kindly guide me a little more. – Unbreakable Sep 03 '17 at 22:25
  • 1
    Then handle the forms `.submit()` event, make an ajax call passing the relevant values from your form to a method that does you check, and return a `JsonResult` (say either `true` or `null`). Then check the response, and if `true`, display your confirm popup. If 'confirmed', continue with the submit, otherwise cancel it. –  Sep 03 '17 at 22:26
  • @StephenMuecke: I will try to implement it and will ask further doubts (if I get stuck). thanks alot. – Unbreakable Sep 03 '17 at 22:27
  • @StephenMuecke: Sir, In answer given below to my question, it's some `$.get` request. I am confused, I need to follow the `onsubmit` event or the below one. Both approach is going to be new to me. Can you kindly guide, which one is more suited, and simple to follow, so that I learn the optimized way. – Unbreakable Sep 03 '17 at 22:38
  • That code in the 2nd snippet just needs to be inside the handler for your forms submit event (and not `$.get()` is just a shortcut for `$.ajax()`). But you should asking Tieson T. if you need more clarification about their answer :) –  Sep 03 '17 at 22:43
  • Got it.. thanks. :) – Unbreakable Sep 03 '17 at 22:43

1 Answers1

1

You can basically repeat the check you use in the POST action currently, but return a simple JSON value instead:

public ActionResult CheckForExistingReferral(ReferralViewModel viewModel)
{
    bool hasPreviousRequest = false;

    var candidateId = User.Identity.GetUserId();
    var referral = _context.Referrals.Where(r => (r.CandidateId == candidateId) && (r.CompanyId == viewModel.CompanyId)).SingleOrDefault();

    hasPreviousRequest = referral != null;

    return Json(new { hasPreviousRequest = hasPreviousRequest }, JsonRequestBehavior.AllowGet);
}

In your view, you'd do something like:

var data = $('form').serialize();
var url = '@Url.Action("CheckForExistingReferral")';

$.get(url, data)
    .done(function(response, status, jqxhr){
        if(response.hasPreviousRequest){
            // show alert?
        }
        else{
            // post form?
        }
    });

This is using jQuery's $.get helper, which (mostly) just wraps their $.ajax function. serialize will URL-encode your form values into a format suitable for submitting through the Ajax request, or you can manually construct a JavaScript object, if you prefer.

The .done() function handles successful responses (anything with a 200 status code). If you want to handle bad/failed requests, you'd add a .fail() function, something like this:

$.get(url, data)
    .done(function(response, status, jqxhr){
    })
    .fail(function(jqxhr, status, error){
    });

error will contain an object that contains the error details returned from the server. jqxhr, in both instances, is the XMLHTTPRequest created by jQuery.

Of course, since you'd now be repeating a chunk of code, you should probably refactor this duplicate check into something like a helper method, and then replace the check in both actions.

Since you don't want to allow the form submission unless the user has confirmed the resubmission (or there wasn't a previous submission), you need to prevent the form submission. Here's an example:

// check variable - we'll update this in the handler below
var canSubmit = false;

$('form').on('submit', function(e){   
    // Have we checked for resubmissions yet, or has the user approved the resubmit?
    if(!canSubmit){
        e.preventDefault();

        var data = $('form').serialize();
        var url = '@Url.Action("CheckForExistingReferral")';

        $.get(url, data)
            .done(function(response, status, jqxhr){
                if(response.hasPreviousRequest){
                    // show alert?
                    if(confirm("You've already applied for this job. Apply again?")){
                        canSubmit = true;
                        // $('form').submit(); re-triggers the form submission. 
                        // Since the value is now true, the form submission occurs normally
                        $('form').submit();
                    }                       
                }
                else{
                    // post form?
                    canSubmit = true;
                    $('form').submit();
                }
            });
    }
});
Tieson T.
  • 20,774
  • 6
  • 77
  • 92
  • Doubt 1: Is it alright if I use `ajax post` request instead of `get`. Or it's a mistake on my part? – Unbreakable Sep 03 '17 at 22:53
  • No, you can use POST if you prefer. That would allow you to remove the JsonRequestBehavior from the JsonResult. I just tend to use GET if all I'm doing is fetching some value(s). You may find https://stackoverflow.com/questions/1077412/what-is-an-idempotent-operation interesting. – Tieson T. Sep 03 '17 at 22:58
  • Doubt 2: Sir, I am able to hit the JSON Action, but before it returns anything my actual form post action breakpoint gets hit. Am I doing something silly here. I have added my code in `attempt 1` section of my `original question`. – Unbreakable Sep 03 '17 at 23:29
  • Yes, Stephen already mentioned this: you need to prevent the form submission. Ajax calls are asynchronous and non-blocking, though, so you can't just wait on the response. – Tieson T. Sep 03 '17 at 23:32
  • Sir, do I need to add something called "prevent default" . I am new to this so I am stil learning, Because I only want the form submission once user says "Yes" in the confirmation box. – Unbreakable Sep 03 '17 at 23:34
  • Whole point to doing this is to prevent overwriting if user says "no" in confirmation box. But if anyways in the background form gets submitted then it beats the purpose. – Unbreakable Sep 03 '17 at 23:37
  • @Unbreakable I've edited the question to show one way of handling the form. There are other ways of doing roughly the same thing, but this is simple and works. – Tieson T. Sep 03 '17 at 23:40
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/153577/discussion-between-unbreakable-and-tieson-t). – Unbreakable Sep 03 '17 at 23:46
  • Sir, I cannot get it to work. With every form submit my JSON action gets hit alteast 3 times. I have added my Attempt2 code: in the original question. – Unbreakable Sep 04 '17 at 00:20
  • I've replied in chat. Please ask your questions there. – Tieson T. Sep 04 '17 at 00:25