0

I have a viewModel that I want to display a Toastr notification in, but when I run the code, it just goes back to the Index page, and does not acknowledge the e.preventDefault() method.

When I click save, I want it to return the 'success' notification before re-directing to the index page?

Here is the code:

@model Rubyx.ViewModels.McnFormViewModel
@{
ViewBag.Title = "MCN Form";
 Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Bidston MCN</h2>


@using (Html.BeginForm("Save", "BidstonHwrc", new { id = "mcnForm"}))
{
 @*@Html.ValidationSummary(true, "Please fix the below errors")*@

@*<div class="form-group">
    @Html.LabelFor(w => w.BidstonHwrc.McnNumber)
    @Html.TextBoxFor(w => w.BidstonHwrc.McnNumber, new { @class = "form-
control", @readonly = "readonly" })
</div>*@

<div class="form-group">
    @Html.LabelFor(w => w.BidstonHwrc.Id)
    @Html.TextBoxFor(w => w.BidstonHwrc.Id, new { @class = "form-control", 
@readonly = "readonly" })
</div>




<div class="form-group">
    @Html.LabelFor(w => w.BidstonHwrc.DateEntered)
    @Html.TextBoxFor(w => w.BidstonHwrc.DateEntered, new { @class = "form-
control", @readonly = "readonly"})
</div>


<div class="form-group">
    @Html.LabelFor(w => w.BidstonHwrc.WasteTypeId)
    @Html.DropDownListFor(w => w.BidstonHwrc.WasteTypeId, new 
SelectList(Model.WasteType, "Id", "Name"), "Select Waste Type", new { @class 
= "form-control" })
    @*@Html.ValidationMessageFor(w => w.BidstonHwrc.WasteType)*@
</div>


<div class="form-group">
    @Html.LabelFor(w => w.BidstonHwrc.DestinationId)
    @Html.DropDownListFor(w => w.BidstonHwrc.DestinationId, new 
SelectList(Model.Destination, "Id", "Name"), "Select Destination", new { 
@class = "form-control" })
    @*@Html.ValidationMessageFor(w => w.BidstonHwrc.Destination)*@
</div>


<div class="form-group">
    @Html.LabelFor(w => w.BidstonHwrc.Registration)
    @Html.TextBoxFor(w => w.BidstonHwrc.Registration, new { @class = "form-
control" })
</div>


<div class="form-group">
    @Html.LabelFor(w => w.BidstonHwrc.StaffMemberId)
    @Html.DropDownListFor(w => w.BidstonHwrc.StaffMemberId, new 
SelectList(Model.StaffMember, "Id", "Name"), "Select User", new { @class = 
"form-control" })
    @*@Html.ValidationMessageFor(w => w.BidstonHwrc.StaffMember)*@
</div>


<button type="submit" class="btn btn-primary">Save</button>



@Html.HiddenFor(w => w.BidstonHwrc.Id)
@Html.AntiForgeryToken(); //a secret code and a cookie on the users computer
//back button that returns you to the index page
@Html.ActionLink("Back", "Index", "BidstonHwrc", null, new { @class = "btn 
btn-primary" })

}

@section scripts{
<script>
    $(document).ready(function () {

        var vm = {}; //blank object


        $("#mcnForm").submit(function (e) {
            e.preventDefault();

            $.ajax({
                url: "/api/BidstonHwrc",
                method: "post",
                data: vm
            })
            .done(function () {
                toastr.success("MCN succesfully recorded");
            })
            .fail(function () {
                toastr.error("Something went wrong!")
            });

        });


        });

</script>

@Scripts.Render("~/bundles/lib")
}

Here is my controller:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity.Validation;
using Rubyx.ViewModels;
using Rubyx.Models;
using System.Web.Mvc;

namespace Rubyx.Controllers
{
public class BidstonHwrcController : Controller
{
    private ApplicationDbContext _context; //this is our call to the DB

    public BidstonHwrcController() //initialise the DB call in a constructor
    {
        _context = new ApplicationDbContext();
    }

    protected override void Dispose(bool disposing) //disposable object
    {
        _context.Dispose();
    }


    // GET: BidstonHwrc
    public ViewResult Index()
    {   
        //var bidston = _context.BidstonHwrc.ToList(); //LAZY LOADING
        return View();
    }



    public ActionResult New()
    {
        var wastetype = _context.WasteTypes.ToList();
        var destination = _context.Destinations.ToList();
        var staffmember = _context.StaffMember.ToList();

        var viewModel = new McnFormViewModel
        {
            BidstonHwrc = new BidstonHwrc(),
            WasteType = wastetype,
            Destination = destination,
            StaffMember = staffmember
        };

        return View("McnForm", viewModel);
    }






    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Save(BidstonHwrc bidstonhwrc)
    {
        _context.BidstonHwrc.Add(bidstonhwrc);



        try
        {
            _context.SaveChanges(); //either all changes are made or none at 
     all
            //return Json(new { id = bidstonhwrc.Id });
        }


        catch (DbEntityValidationException e)
        {
            Console.WriteLine(e);
        }


        return RedirectToAction("Index", "BidstonHwrc", new {id = 
             bidstonhwrc.Id });


    }


    public ActionResult Edit(int id)
    {
        var bidstonhwrc = _context.BidstonHwrc
            .SingleOrDefault(w => w.Id == id); //if the customer exists in 
        the DB it will be returned, otherwise null

        if (bidstonhwrc == null)
            return HttpNotFound();

        var viewModel = new McnFormViewModel
        {
            BidstonHwrc = bidstonhwrc,
            WasteType = _context.WasteTypes.ToList(),
            Destination = _context.Destinations.ToList(),
            StaffMember = _context.StaffMember.ToList()
        };


        return View("McnForm", viewModel); 
    }




}
}

And here is my API controller:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
 using System.Data.Entity;
 using System.Web.Http;
using Rubyx.Models;
using Rubyx.Dtos;
 using AutoMapper;

 namespace Rubyx.Controllers.Api
 {
public class BidstonHwrcController : ApiController
{
    private ApplicationDbContext _context;

    public BidstonHwrcController()
    {
        _context = new ApplicationDbContext();
    }


    //GET /api/BidstonHwrc
    public IHttpActionResult GetBidstonHwrcs()
    {
        var bidstonhwrcDtos = _context.BidstonHwrc
                .Include(b => b.WasteType)
                .Include(b => b.Destination)
                .Include(b => b.StaffMember)
                .ToList()
                .Select(Mapper.Map<BidstonHwrc, BidstonHwrcDto>);


        return Ok(bidstonhwrcDtos);
            //we are calling a delegate here not the method
            //this maps the objects to eachother using a generic method 
  (Map)

    }



    //GET /api/BidstonHwrc/1
    public IHttpActionResult GetBidstonHwrc(int id)
    {
        var bidstonhwrc = _context.BidstonHwrc.SingleOrDefault(b => b.Id == 
 id);

        if (bidstonhwrc == null)
            return NotFound();  //takes an enumeration that specifies the 
 kind of error
                                                                       //if 
 the given resource is not found, we return the above exception
        return Ok(Mapper.Map<BidstonHwrc, BidstonHwrcDto>(bidstonhwrc)); 
//Ok helper method used here
    }


    //POST /api/BidstonHwrc  this action will only be called if we send an 
 http post request
    [HttpPost]
    public IHttpActionResult CreateBidstonHwrc(BidstonHwrcDto 
 bidstonhwrcDto) //changed the return type to Dto
    {
        //validate the object first
        if (!ModelState.IsValid)
            return BadRequest();


        //need to map the Dto back to our object

        var bidstonhwrc = Mapper.Map<BidstonHwrcDto, BidstonHwrc>
 (bidstonhwrcDto);


        _context.BidstonHwrc.Add(bidstonhwrc); //add it to our context
        _context.SaveChanges();

        //we want to add the ID to our dto and return it to the client

        bidstonhwrcDto.Id = bidstonhwrc.Id;

        return Created(new Uri(Request.RequestUri + "/" + bidstonhwrc.Id), 
 bidstonhwrcDto); //method for mapping single customer to the Dto
    }


    //PUT /api/BidstonHwrc/1
    [HttpPut]
    public void UpdateBidstonHwrc(int id, BidstonHwrcDto bidstonhwrcDto)
    {
        if (!ModelState.IsValid)
            throw new HttpResponseException(HttpStatusCode.BadRequest);

        var bidstonhwrcInDb = _context.BidstonHwrc.SingleOrDefault(b => b.Id 
 == id);
        //we need to check for the existence of this object in the DB

        if (bidstonhwrcInDb == null)
            throw new HttpResponseException(HttpStatusCode.NotFound);

        //now we need to update the MCN


        Mapper.Map(bidstonhwrcDto, bidstonhwrcInDb);

        _context.SaveChanges();

    }



    //DELETE /api/BidstonHwrc/1
    [HttpDelete]
    public void DeleteBidstonHwrc(int id)
    {
        //first we need to check that this id is present in the DB
        var bidstonhwrcInDb = _context.BidstonHwrc.SingleOrDefault(b => b.Id 
 == id);

        if (bidstonhwrcInDb == null)
            throw new HttpResponseException(HttpStatusCode.NotFound);

        _context.BidstonHwrc.Remove(bidstonhwrcInDb); //object will be 
 removed in memory
        _context.SaveChanges();

    }



}
}
Kehoe
  • 5
  • 4
  • Save & then after ajax success call , reload the page after the toast message. – Asif Raza May 31 '17 at 12:05
  • can you try by binding event to `$('form')` instead of `$('#idofform')` – FosterZ May 31 '17 at 12:35
  • I think the issue might be that I'm using the 'Save' action from the controller, rather than the 'CreateBidstonHwrc' action from my api controller? – Kehoe May 31 '17 at 14:17
  • @Kehoe Which function saves your data of the form? Did you check my answer? – User3250 May 31 '17 at 14:21
  • @User3250 I tried your answer, but the form would not save. I have edited the answer to include the controller and apicontroller, as I know the API is populated on save, but the form itself is linked to the 'save' action on my controller. Sorry for being vague, but I am following a tutorial, and new to MVC and Web API. – Kehoe May 31 '17 at 14:29
  • Ok. Can you check console on browser to see if there is some error logged. Debug your done and fail methods in ajax. – User3250 May 31 '17 at 14:31
  • Thanks @User3250, will check them now, appreciate your time! :) – Kehoe May 31 '17 at 14:34
  • Happy to help. Also, if you get save to work you might need to setTimeout for few secs before redirecting to another page. Otherwise you might not see toastr msg. – User3250 May 31 '17 at 14:37
  • I've just tried again with your solution below @User3250, and I am getting a status 500 error - "parameters dictionary contains a null entry parameter 'id' of nullable type... – Kehoe May 31 '17 at 14:52
  • The method it is quoting in this error is GetBidstonHwrc(Int32) – Kehoe May 31 '17 at 14:53
  • Error is self descriptive. You must pass a parameter with name id to the function. – User3250 May 31 '17 at 14:59
  • Would it be here that I would need to pass the parameter: var vm = {}; ? I'm not sure how to populate this, as in the tutorial he uses an array for a different function – Kehoe May 31 '17 at 15:03
  • GetBidstonHwrc is to get the data not top post(save to db). With form you need to post the data to save data to create new object and save to DB may be. IMHO you should try using CreateBidstonHwrc. In script data:$(this).serialize() not vm. See network tab in browser to check data being sent. – User3250 May 31 '17 at 15:15
  • Hi @User3250, have checked everything again and it is the page re-direct that is causing the error. I've tried return false; and e.preventDefault, but the page still re-directs? Do you know of any other method that will work? Can't seem to find anything else on here that works? – Kehoe Jun 01 '17 at 13:23
  • @Kehoe did you replace your Html.BeginForm with that in my answer? – User3250 Jun 01 '17 at 13:37
  • @User3250 yes, I am using: using (Html.BeginForm("Save", "home",FormMethod.Post,new { id = "mcnForm" })) – Kehoe Jun 01 '17 at 13:41
  • Ok. Try this. 1. Comment out your js code. 2. Insted of "home" use "BidstonHwrc". Understand that BidstonHwrc is your controller name not "home". 3. then put debugger in your BidstonHwrc.Save function. – User3250 Jun 01 '17 at 13:44
  • @User3250, sorry, I had changed to BidstonHwrc, had just re-copied the below. So the error in the debugger is DbUpdateException – Kehoe Jun 01 '17 at 13:55
  • Ok. So your save function is getting called. Your DB operation isn't working. You need to check the error using try catch block. – User3250 Jun 01 '17 at 14:01
  • Sorry @User3250, just noticed I had commented out some other code when I was testing, the Save actually works fine without the JS. However, even though the save has worked, in the debugger it says there was a 302 response code regarding the POST – Kehoe Jun 01 '17 at 14:06
  • Great! You figured it out. Happy coding. – User3250 Jun 01 '17 at 14:09
  • Yes @User3250 thanks for your help :) – Kehoe Jun 01 '17 at 14:14
  • @User3250 - I've figured out what is going on. There are 2 x post requests that are fired, one which is succesful, and then another which fails, which I'm guessing is the JS. Even though the JS fails, it still posts the data to the API as well. In this case, should I be using a different JS function to call the Toastr notification as the POST is already happening, but I'm sending the data twice? Toastr works as it bring up the fail notification, but then the form is saved still – Kehoe Jun 01 '17 at 15:09
  • @Kehoe You should be having only JS function to save. Use url:'BidstonHwrc/Save' to save the data. Don't forget to use e.preventDefault() in first line of your JS function to avoid posting data twice. – User3250 Jun 02 '17 at 06:18

3 Answers3

0

Instead of e.preventDefault(), use return false as follow:

$("#mcnForm").submit(function (e) {
    //e.preventDefault();

    $.ajax({
    url: "/api/BidstonHwrc",
    method: "post",
    data: vm
    })
    .done(function () {
    toastr.success("MCN succesfully recorded");
    })
    .fail(function () {
    toastr.error("Something went wrong!")
    });

   return false;
});

For a better explanation about e.preventDefault please read event.preventDefault() vs. return false

Kiran Beladiya
  • 441
  • 1
  • 5
  • 11
0

you need both e.prevenDefault() and e.stopPropagation() to prevent default event from occuring and restrict the bubbling up the events, if you are using Jquery then return false does both for you.

FosterZ
  • 3,863
  • 6
  • 38
  • 62
  • Hi @FosterZ, have tried both ways now and still no luck, still get a re-direct to the index page? – Kehoe May 31 '17 at 10:17
0

You are using an overload of Html.BeginForm not correct for your case. Try below:

@using (Html.BeginForm("Save", "home",FormMethod.Post,new { id = "mcnForm" }))

4th param is for HtmlAttributes.

User3250
  • 2,961
  • 5
  • 29
  • 61