0

This is driving me nuts. I'm trying to edit a many-to-many relation, following this nice answer, but as I added a second relation, things started to get funky.

public ActionResult Edit(Style style)
{
    var accessoryIDs = style.SelectedAccessories.Select(a => a.ID);
    var fabricIDs = style.SelectedFabrics.Select(f => f.ID);

When loading style for editing in the first place, both lists are populated and all is good. So I make my edits and save.. style.SelectedAccesories is fine, style.SelectedFabrics is null! In my view, I have the editorfor accessories above the one for fabrics. If I swap the two, so that fabrics come before accessories, accessories is null where fabrics isn't.

I started out with a shared model for the two Selecteds and dublicated to make sure it didn't have something to with that (see how desperate I am?) which of course it didn't.

I'm thinking that I'm missing something ridiculous..

Feel free to ask for more code if you need it.

Edit: I realize that I've asked an off-topic question and am sorry but I was really frustrated and hoped someone could look at the outline and say "Oh, that, you just need to tweak that one there and it'll be all good". I guess it runs deeper than that, so here's code to add to make a proper question:

Controller actions:

    public ActionResult Edit(int? id)
    {
        var style = db.Styles.Include(s => s.Fabrics).Include(s => s.Accessories).FirstOrDefault(s => s.ID == id);
        style.SelectedAccessories = db.Accessories.Select(f => new SelectedAccessory { ID = f.ID, Text = f.Name }).ToList();
        style.SelectedFabrics = db.Fabrics.Select(f => new SelectedFabric { ID = f.ID, Text = f.Name }).ToList();
        return View(style); // As said, the two above aren't null at this point
    }

    [HttpPost]
    public ActionResult Edit(Style style)
    {
        var accessoryIDs = style.SelectedAccessories.Where(s => s.IsSelected).Select(s => s.ID);
        // This one (style.SelectedFabrics) is now null
        var fabricIDs = style.SelectedFabrics.Where(s => s.IsSelected).Select(s => s.ID);
        return View(style);
    }

Edit view:

@model Styleinfo.Models.Style
@using (Html.BeginForm())
{
    <div class="form-horizontal">
        @*swapping around makes the last of the two null in the controller*@
        @Html.EditorFor(model => model.SelectedAccessories)
        @Html.EditorFor(model => model.SelectedFabrics)
        <input type="submit" value="Save" class="btn btn-default" />
    </div>
}

Editor view (SelectedFabric for fabrics, they're otherwise identical as mentioned):

@model Styleinfo.Models.SelectedAccessory
@using (Html.BeginForm())
{
    @Html.HiddenFor(model => model.ID)
    @Html.LabelFor(model => model.IsSelected, Model.Text)
    @Html.EditorFor(model => model.IsSelected)
}

The models:

public partial class Style
{
    public IEnumerable<SelectedFabric> SelectedFabrics { get; set; }
    public IEnumerable<SelectedAccessory> SelectedAccessories { get; set; }
}

public class SelectedAccessory // Again, SelectedFabric, otherwise identical
{
    public int ID { get; set; }
    public string Text { get; set; }
    public bool IsSelected { get; set; }
}

T-SQL to create the simplified database and consequently the rest of Style:

-- Creating table 'Fabrics'
CREATE TABLE [dbo].[Fabrics] (
    [ID] int IDENTITY(1,1) NOT NULL,
    [Name] nvarchar(max)  NOT NULL,
);
GO

-- Creating table 'Styles'
CREATE TABLE [dbo].[Styles] (
    [ID] int IDENTITY(1,1) NOT NULL,
    [Name] nvarchar(max)  NOT NULL,
);
GO

-- Creating table 'Accessories'
CREATE TABLE [dbo].[Accessories] (
    [ID] int IDENTITY(1,1) NOT NULL,
    [Name] nvarchar(max)  NOT NULL,
);
GO

-- Creating table 'FabricStyle'
CREATE TABLE [dbo].[FabricStyle] (
    [Fabrics_ID] int  NOT NULL,
    [Styles_ID] int  NOT NULL
);
GO

-- Creating table 'AccessoryStyle'
CREATE TABLE [dbo].[AccessoryStyle] (
    [Accessories_ID] int  NOT NULL,
    [Styles_ID] int  NOT NULL
);
GO
Community
  • 1
  • 1
Heki
  • 926
  • 2
  • 15
  • 34

1 Answers1

2

So after much hair pulling I discovered several things.

1: The browser didn't post back the second editor contents at all. The values simply weren't there in the POST data.

So I figured I'd have a look at the model binding because I've seen other people have trouble with just that. Alas, to no avail.

2: I decided to look at the markup that was handed to the browser and I discovered this

<div class="form-group">
  <label class="control-label col-md-2" for="SelectedAccessories">Accessories</label>
  <div class="col-md-10">
    <form action="/Styles/Edit/1" method="post">
      <input data-val="true" data-val-number="The field ID must be a number." data-val-required="Feltet ID skal udfyldes." id="SelectedAccessories_0__ID" name="SelectedAccessories[0].ID" type="hidden" value="1" />
      <label for="SelectedAccessories_0__IsSelected">Accessory 1</label>
      <input class="form-control check-box" data-val="true" data-val-required="Feltet IsSelected skal udfyldes." id="SelectedAccessories_0__IsSelected" name="SelectedAccessories[0].IsSelected" type="checkbox" value="true" />
      <input name="SelectedAccessories[0].IsSelected" type="hidden" value="false" />
    </form>
    <span class="field-validation-valid text-danger" data-valmsg-for="SelectedAccessories" data-valmsg-replace="true"></span>
  </div>
</div>

So what's wrong with that picture? There's a form tag in there.

3: So back to the editor.

@model Styleinfo.Models.SelectedAccessory
@using (Html.BeginForm())
{
    @Html.HiddenFor(model => model.ID)
    @Html.LabelFor(model => model.IsSelected, Model.Text)
    @Html.EditorFor(model => model.IsSelected)
}

I removed the Html.BeginForm() thus

@model Styleinfo.Models.SelectedAccessory
@Html.HiddenFor(model => model.ID)
@Html.LabelFor(model => model.IsSelected, Model.Text)
@Html.EditorFor(model => model.IsSelected)

And then all my troubles vanished.

As I said in my question; "I'm thinking that I'm missing something ridiculous.." and I was.

Heki
  • 926
  • 2
  • 15
  • 34