0

my model looks like this:

Bank           Other          LawFirm 
 |              |                |   
BankContact   OtherContact    LawFirmContact
 |______________|________________|
                |
                |
              Contact 

So when creating a contact I am trying to pass the information to create entry for the specific associated entity. At the moment I pass through a RelationId and ContactId and then try and create a the new entity in the controller but my "Save button on the form is not firing. I think it could be because I am not binding the information properly. I'm also posting this to see if this is the correct way to deal with a situation like this

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Save(ContactFormViewModel viewModel)
    {
        if (!ModelState.IsValid)
        {
            return View("ContactForm", viewModel);
        }
        if (viewModel.Contact.Id == 0)
        {
            _context.Contacts.Add(viewModel.Contact);
            if (viewModel.IsBank)
            {
                var bankContact = new BankContact()
                {
                    BankId = viewModel.RelationId,
                    Bank = _context.Banks.Single(b => b.Id == viewModel.RelationId),
                    ContactId = viewModel.ContactId,
                    Contact = viewModel.Contact
                };
                _context.BankContacts.Add(bankContact);
            }
            else if (viewModel.IsLawFirm)
            {
                var lawFirmContact = new LawFirmContact()
                {
                    LawFirmId = viewModel.RelationId,
                    LawFirm = _context.LawFirms.Single(l => l.Id == viewModel.RelationId),
                    ContactId = viewModel.ContactId,
                    Contact = viewModel.Contact,

                };
                _context.LawFirmContacts.Add(lawFirmContact);
            }
            else if (viewModel.IsOther)
            {
                var standaloneContact = new StandaloneContact()
                {
                    StandAloneId = viewModel.RelationId,
                    Standalone = _context.Standalones.Single(s => s.Id == viewModel.RelationId),
                    Contact = viewModel.Contact,
                    ContactId = viewModel.ContactId
                };
                _context.StandaloneContacts.Add(standaloneContact);
            }
        }

        else
        {
            var contactInDb = _context.Contacts.Single(c => c.Id == viewModel.Contact.Id);

            contactInDb.Firstname = viewModel.Contact.Firstname;
            contactInDb.Surname = viewModel.Contact.Surname;
            contactInDb.Email = viewModel.Contact.Email;
            contactInDb.ContactNo = viewModel.Contact.ContactNo;

            if (!String.IsNullOrWhiteSpace(viewModel.Contact.AltContactNo))
                contactInDb.AltContactNo = viewModel.Contact.AltContactNo;            
        }

        _context.SaveChanges();
        return RedirectToAction("Index", "Contacts");

    }

<div class="card">
    <div class="card-body">
        @using (Html.BeginForm("Save", "Contacts"))
        {

        <div class="form-row">
            <div class="col-3">
                <div class="form-group">
                    @Html.LabelFor(c => c.Contact.Firstname, new { @class = "form-control-label" })
                    @Html.TextBoxFor(c => c.Contact.Firstname, new { @class = "form-control" })
                    @Html.ValidationMessageFor(c => c.Contact.Firstname)
                </div>
            </div>

            <div class="col-3">
                <div class="form-group">
                    @Html.LabelFor(c => c.Contact.Surname, new { @class = "form-control-label" })
                    @Html.TextBoxFor(c => c.Contact.Surname, new { @class = "form-control" })
                    @Html.ValidationMessageFor(c => c.Contact.Surname)
                </div>
            </div>
        </div>

            <div class="form-row">

            <div class="col-3">
                <div class="form-group">
                    @Html.LabelFor(c => c.Contact.Email, new { @class = "form-control-label" })
                    @Html.TextBoxFor(c => c.Contact.Email, new { @class = "form-control" })
                    @Html.ValidationMessageFor(c => c.Contact.Email)
                </div>
            </div>
            <div class="col-3">
                <div class="form-group">
                    @Html.LabelFor(c => c.Contact.Birthday, new { @class = "form-control-label" })
                    @Html.TextBoxFor(c => c.Contact.Birthday, new { @class = "form-control datepicker-here", @data_language = "en", @autocomplete = "off" })
                </div>
            </div>
        </div>
            <div class="form-row">
                <div class="col-3">
                    <div class="form-group">
                        @Html.LabelFor(c => c.Contact.ContactNo, new { @class = "form-control-label" })
                        @Html.TextBoxFor(c => c.Contact.ContactNo, new { @class = "form-control" })
                        @Html.ValidationMessageFor(c => c.Contact.ContactNo)
                    </div>
                </div>

                <div class="col-3">
                <div class="form-group">
                    @Html.LabelFor(c => c.Contact.AltContactNo, new { @class = "form-control-label" })
                    @Html.TextBoxFor(c => c.Contact.AltContactNo, new { @class = "form-control" })
                </div>
                    </div>

            </div>

            <div class="form-row">
                <div class="col-4">
                    <div class="form-group">
                        <input type="radio" name="choice-contact-type" id="choice-contact-type-bank" required>
                        <label for="choice-contact-type-bank">Bank</label>

                        <div class="reveal-if-active">

                            @Html.DropDownListFor(b => b.BankContact.BankId, new SelectList(Model.Banks, "Id", "Name "), "", new { @class = "form-control" })
                            @{Model.IsBank = true;}
                        </div>
                    </div>
                </div>

                <div class="col-4">
                    <div class="form-group">
                        <input type="radio" name="choice-contact-type" id="choice-contact-type-lawFirm">
                        <label for="choice-contact-type-lawFirm">Law Firm</label>

                        <div class="reveal-if-active">

                            @Html.DropDownListFor(l => l.LawFirmContact.Id, new SelectList(Model.LawFirms, "Id", "Name"), "", new { @class = "form-control" })
                            @{Model.IsLawFirm = true;}
                        </div>
                    </div>
                </div>

                <div class="col-4">
                    <div class="form-group">
                        <input type="radio" name="choice-contact-type" id="choice-contact-type-standalone">
                        <label for="choice-contact-type-standalone">Other</label>

                        <div class="reveal-if-active">

                            @Html.DropDownListFor(s => s.StandaloneContact.Id, new SelectList(Model.Standalones, "Id", "Name"), "", new { @class = "form-control" })
                            @{Model.IsOther = true;}
                        </div>
                    </div>
                </div>
            </div>

            @Html.AntiForgeryToken()
            <button type="submit" class="btn btn-primary">Save</button>

        }
    </div>
</div>

Edit

The hidden dropdown fields are required and therefore validation is not allowing it to go through and because they were hidden I could not see that! The field is required because under my StandaloneContact Id is required. It is always required, so how would I get around this? setting it to a default value?

I'd still like to know if this is the best way to link a model to 3 different models with a many-to-many relationship. Is there another way to do this in MVC?

1 Answers1

1

Try adding this to the form

@using (Html.BeginForm("Save", "Contacts", FormMethod.Post))

Also in regards to the correct way to send data from the view to the controller, it's mostly just what you need at the given time. If you simply want to send data in a form, this is probably the best way. However, if you, for example, what to send this data and then refresh a certain aspect of the page such as a partial view, I would suggest using ajax.

EDIT: Based on the edit to the question, by making the Standalone Contact ID a nullable, this should resolve the issue of the id always being required

JamesS
  • 2,167
  • 1
  • 11
  • 29
  • 1
    in addition to @JamesS answer i did not se model declaration to your html so i woudl add `@model ContactFormViewModel` at the top of html and the use it instead of c in lambda expressions just to make sure – markorial Jan 17 '19 at 14:07
  • @markorial Sorry I should have included that part, I have the
    @model ContactFormViewModel
    at the top
    – Matt Richard Towers Jan 17 '19 at 14:14
  • @JamesS Okay cool thanks. I think What I was trying to ask is if my conceptual model on connecting bank and lawfirms to contact is okay. And if the way I'm testing to see which one is sent across is okay – Matt Richard Towers Jan 17 '19 at 14:16
  • After adding the above my form is unfortunately still not sending the info through – Matt Richard Towers Jan 17 '19 at 14:19
  • @MattRichardTowers The above code should reach that method depending on whether you are sending the correct model and the controller is called 'Contacts'. – JamesS Jan 17 '19 at 14:23
  • Have a look at my edit, I found the underlying issue but not sure how to get around it. – Matt Richard Towers Jan 17 '19 at 14:26
  • @MattRichardTowers So if I understand, sometimes you won't have a Standalone Contact ID in the controller? Is that correct? – JamesS Jan 17 '19 at 14:29
  • Yeah. So I'm trying to create a new associative entity based on whether the contact will be linked to either a bank, lawfirm, or standalone. I take the Id of the whichever is chosen from the dropdown and then set a boolean value true so that I can create the new entity using that Id with the contacts Id – Matt Richard Towers Jan 17 '19 at 14:33
  • @MattRichardTowers I have updated my answer and based off what you have said, i believe this should fix your issue. You may have to do some casting in cetain area's, depending on how this Id is used – JamesS Jan 17 '19 at 14:35
  • @JamesS okay awesome I'll give it a bash. Thanks for your help! In that regard, would I make LawFirmContact Id and BankContact Id nullable as well? – Matt Richard Towers Jan 17 '19 at 14:36
  • @MattRichardTowers Depends. If in the database both are primary keys, I would suggets against it. If they are foreign keys, it should be fine. – JamesS Jan 17 '19 at 14:43
  • @JamesS they are all the primary key for the associative table. the associative tables are made up of the Id, ContactId, BankId. Id, ContactId, LawFirmId, and Id, ContactId, and StandaloneId. – Matt Richard Towers Jan 17 '19 at 14:45
  • @JamesS Is the best solution to create a separate form for each type of Contact? – Matt Richard Towers Jan 17 '19 at 14:46
  • @MattRichardTowers It depends really. If every contact is also going to be one of the others then no this is fine. Otherwise I'd say keep them separate. Keeping them separate would make it more easy to understand too as you will be able to pinpoint where the issue is faster. – JamesS Jan 17 '19 at 14:49
  • @JamesS okay cool thanks :) one last question, the way that I'm setting the Boolean value of the ViewModel in the Razor view, is not changing the value when it gets saved. So the model is not getting saved to the db. What is the best way to do that? – Matt Richard Towers Jan 17 '19 at 14:57
  • @MattRichardTowers That's because you're not actually passing it to the controller to pass it I would suggest setting it the way you are doing here and then you can pass it as a hidden value. Like @Html.HiddenFor(m => m.//The bool) – JamesS Jan 17 '19 at 15:03