0

First Time posting here, I won't use it as an excuse not to be helpful but this is driving me nuts. I am a complete noob, this isn't hw but this is my first (at home/for fun) type of project with asp.net mvc.

So here we go..

My Main menu looks as such.

@{
    ViewBag.Title = "Index";
}

<ul class="nav nav-tabs">
    <li class="active"><a data-toggle="tab" href="#firstTab">View All 
Vendors</a></li>
    <li><a data-toggle="tab" href="#secondTab">Add New</a></li>
    <li><a data-toggle="tab" href="#thirdTab">Remove</a></li>
</ul>
<div class="tab-content">
    <div id="firstTab" class="tab-pane fade in active">@Html.Action("ViewAll")</div>
    <div id="secondTab" class="tab-pane fade in">@Html.Action("AddOrEdit")</div>
    <div id="thirdTab" class="tab-pane fade in">tab 3 content</div>
</div>
@section scripts
{
    @Scripts.Render("~/bundles/jqueryval")
}

If I run my AddOrEdit.cshtml directly I can 'POST' just fine.

@model VendorApp.Models.VENDOR

@{
    ViewBag.Title = "AddOrEdit";
}

@using (Html.BeginForm("AddOrEdit","Vendor",FormMethod.Post, new { enctype = "multipart/form-data", onSubmit = "return jQueryAjaxPost(this);" }))
{
    @Html.AntiForgeryToken()
    <div class="row">
        <div class="form-horizontal">
            <h4>Add Vendor</h4>
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            @Html.HiddenFor(model => model.VENDOR_NUM)

            <div class="col-md-6">
            <div class="form-group">
                @Html.LabelFor(model => model.VENDOR_NAME, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.VENDOR_NAME, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.VENDOR_NAME, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.STREET, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.STREET, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.STREET, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.CITY, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.EditorFor(model => model.CITY, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.CITY, "", new { @class = "text-danger" })
                </div>
            </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.STATE, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.STATE, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.STATE, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-md-offset-2 col-md-10">
                        <input type="submit" value="Update" class="btn btn-default" />
                    </div>
                </div>
            </div>  
            <div class="col-md-6">
                <div class="form-group">
                    @Html.LabelFor(model => model.ZIP, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.ZIP, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.ZIP, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.PHONE, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.PHONE, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.PHONE, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.EMAIL, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.EMAIL, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.EMAIL, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.SERVICE, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.SERVICE, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.SERVICE, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.NOTE, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.NOTE, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.NOTE, "", new { @class = "text-danger" })
                    </div>
                </div>
            </div>    
        </div>
    </div>
}

And here is my Controller

using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using VendorApp.Models;

namespace VendorApp.Controllers
{
    public class VendorController : Controller
    {
        // GET: Vendor
        public ActionResult Index()
        {
            return View();
        }

        //get all Vendors
        public ActionResult ViewAll()
        {
            return View(GetAllVendor());
        }

        //numerate the vendors

        IEnumerable<VENDOR>GetAllVendor()
        {
            using (Pref_VendorEntities db = new Pref_VendorEntities())
            {
                return db.VENDORs.ToList<VENDOR>();
            }
        }

        /// <summary>
        /// HTTP GET by DEFAULT
        /// </summary>
        /// <returns></returns>
        public ActionResult AddOrEdit()
        {
            VENDOR vendor = new VENDOR();
            return View(vendor);
        }

        /// <summary>
        /// HTTP Post
        /// </summary>
        /// <returns></returns>

        [HttpPost]
        public ActionResult AddOrEdit(VENDOR vend)
        {
            using (Pref_VendorEntities db = new Pref_VendorEntities())
            {
                db.VENDORs.Add(vend);
                db.SaveChanges();
            }

            return RedirectToAction("ViewAll");
        }
    }
}

If I Debug or Load directly from my index.cshtml I get This

But then if I go to click on my 'Add New' tab it looks like this.. Add New which is fine, but then it doesn't actually 'POST' the data.

If I directly load from this page the page displays as such

But it will actually let me 'POST' the data.. I hope I included enough information, but not too much to be overkill. I've tried looking around on here, and I don't know exactly what I'm doing wrong. Any help would be extremely appreciated and I'll be more than happy to elaborate on whatever else is required.

Thank you kindly!

dhodk92
  • 1
  • 1
  • FWIW `public ActionResult Index() { return View(); }` will look for index.cshtml file by default. Not sure its content as that is not posted. – Mark Schultheiss Nov 20 '18 at 18:51
  • What is the issue you are facing ? Are you getting an error ? – Shyju Nov 20 '18 at 18:52
  • When you said it _does not actually post data_, does it mean it is not hitting the post action for AddOrEdit? On [this](https://i.stack.imgur.com/ygxS3.png) page, in the HTML source code, the code `
    ` should be there. This line of code will tell the browser that upon submitting the form, it will post the form data on that URL.
    – Kristianne Nerona Nov 20 '18 at 19:19
  • 1
    @KristianneNerona I think you mean `action="Vendor\AddOrEdit"` perhaps? – Mark Schultheiss Nov 20 '18 at 19:36
  • @MarkSchultheiss right. – Kristianne Nerona Nov 20 '18 at 19:37
  • @KristianneNerona Thank you all for your replies. This seems to be the right train of thought. What I don't understand though, is where exactly I put that? Like I said what I don't get is it will post (hit the post action) from http://localhost:59104/Vendor/AddOrEdit, but if I load the AddOrEdit page asynchonously as you stated below, then it won't? That's what I don't understand is how it works one way and not the other. Forgive my ignorance and my inability to better explain. I sincerely appreciate your help I hope that makes sense. – dhodk92 Nov 20 '18 at 20:10
  • You have `@Html.AntiForgeryToken()` in your view but you do not have the `[ValidateAntiForgeryToken]` on your POST method (not sure how you can claim it works if you post from the page) –  Nov 20 '18 at 22:07
  • For that AntiForgeryToken refer to https://stackoverflow.com/q/4074199/125981 to handle that. – Mark Schultheiss Nov 20 '18 at 23:27
  • The `@Html.BeginForm("AddOrEdit", "Vendor")` creates the `
    `. If you don't see the form tag with the right action, maybe try rebuilding your project. I made a quick project copying parts of your work, and it looks like mine hits the post action on both mentioned pages. Remove the fourth parameter `, new {`. I only add `enctype=multipart/form-data` if I am uploading a file, and I don't use ajax if I am just posting something this simple. Also, @MarkShultheiss has good improvement points below.
    – Kristianne Nerona Nov 20 '18 at 23:44
  • Ahh thank you! The enctype=multipart/form-data was included b/c a tutorial off youtube showcased uploading an image file, but I honestly have no purpose for it for what I need. Thank you guys so much for the help! I'll dig into this more tomorrow morning. I'm currently working 40 hours a week on top of doing hw for all my programming courses, and this is just something I wanted to get ahead on! I'm honestly getting addicted to coding, it's amazing. For my first time posting this community seems awesome! – dhodk92 Nov 21 '18 at 03:51

1 Answers1

0

Here is the thing. Your are combining jQuery and MVC here and posting via AJAX. This is fine BUT you have to wire that up (the jQuery part).

See your code in the form onSubmit = "return jQueryAjaxPost(this);" (remove this)

Using Ajax is great BUT it has to be done properly and then you have to deal with the serialization and all that part properly on both ends. Easy enough but it does add a small bit of complexity to forms - not using post back and such.

NOTE: You are doing both Update and Create actions. Update needs an ID perhaps, so here is ONE way to do that - as a data attribute.

So, create a pubic method in your controller. Lets call it SaveVendor - default is normally "Save" but let's be specific for your "not default" situation.

Create two models (probably a base) one with an ID vendorId and one not, and we can use that with an overload.

I use your VENDOR class but suggest rename to Vendor instead.

public ActionResult SaveVendor(VENDOR vendor)

Add the proper attribute: (just like you have!)

[HttpPost]
public ActionResult SaveVendor(VENDOR vendor)

Now this one has a vendor id so:(for the update vs add)

[HttpPost]
public ActionResult SaveVendor(VendorWithId vendor)

Put some ajax around it: NOTE When you render the form for update, put the id in the data-vendorid

So this has two buttons, one for update, one for create in the form. Lots of ways to do this with one button, changing the text etc. but for this one I choose to create two with a class on each and then handle that, and show how to detect that, add extra data for the vendor id when you render it, get that and so forth.

I leave the exercise of how to handle display of which button to you as an exercise but you could add a class (hidden) or do it with code $('.update').toggle(false); etc.

// attach click handler to form, then each button has one
$('#AddOrEdit').on('click', '.create .update', function(event) {
  $(this).preventDefault(); // prevent submit because of button type
  let myform = $(event.delegateTarget);
  let mybuttonType = $(this).hasClass('create') ? "create" : "update";
  //add extra data so we know which
  myform.trigger("submit", ["submitbutton", mybuttonType]);
});
// I assume form ID AddOrEdit here:
$('#AddOrEdit').on('submit', function(event, param1, param2) {
  let myform = $(this);
  let myaction = myform.attr('action');
  let myData = myform.serializeArray();
  if (param2 === "update") {
    let $id = myform.data("vendorid");
    myData.push({
      name: "vendorId",
      value: $id
    });
  }
  $.ajax({
    type: "POST",
    url: myaction,
    data: $.param(myData);
  }).done(function(data) {
    alert(data);
  }).fail(function() {
    // ajax failed, do something
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="AddOrEdit" action="SaveVendor" data-vendorid="23223">
  <button type="submit" class="update btn btn-default">Update</button>
  <button type="submit" class="create btn btn-default">Create</button>
  <div>all my other form elements here:</div>
</form>

Note: I have "submitbutton" for param1, I did this just to show how to pass parameters with the trigger on the event, feel free to re purpose or revise, this is just an example.

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100