Master Detail CRUD Demo using Grid and PopupForm

This is a demo for master detail CRUD using the Grid.
For master-detail grid see Master Detail Grid, or Hierarchy (Nested Grids)
MasterDetailCrudDemo/Index.cshtml
@{ var grid1 = "RestaurantGrid"; }
@Html.InitCrudPopupsForGrid(grid1, "MasterDetailCrudDemo", 470, 1000)

<div class="bar">
@Html.CreateButtonForGrid(grid1)
</div>

@(Html.Awe().Grid(grid1)
.Height(350)
.Attr("data-syncg", "rest") // crud sync using signalr in site.js
.Url(Url.Action("RestaurantGridGetItems"))
.Groupable(false)
.Columns(
new Column { ClientFormat = ".(Id)", Header = "Id", Width = 70 },
new Column { Bind = "Name" },
new Column { ClientFormat = GridUtils.EditFormatForGrid(grid1), Width = 55 },
new Column { ClientFormat = GridUtils.DeleteFormatForGrid(grid1), Width = 55 }))
MasterDetailCrudDemo/Create.cshtml
@model AweCoreDemo.ViewModels.Input.RestaurantInput
@using (Html.Awe().BeginContext())
{
var gridId = "AddressesGrid";
using (Html.BeginForm())
{
@Html.EditorFor(o => o.Id)
@Html.EditorFor(o => o.Name)
}

@Html.InitCrudPopupsForGrid(gridId, "AddressesGridCrud", 230)

<div class="bar">
@Html.CreateButtonForGrid(gridId, new { restaurantId = Model.Id}, "Add address")
</div>
<div>
@(Html.Awe().Grid(gridId).Url(Url.Action("GridGetItems", "AddressesGridCrud"))
.Parameter("restaurantId", Model.Id)
.Attr("data-syncg","addr")
.Height(230)
.Groupable(false)
.Columns(
new Column { Bind = "Line1,Line2", ClientFormat = ".(Line1) .(Line2)", Header = "Address" },
new Column { Bind = "Chef.FirstName,Chef.LastName", ClientFormat = ".(ChefName)", Header = "Chef" },
new Column { ClientFormat = Html.EditFormatForGrid(gridId), Width = 52 },
new Column { ClientFormat = Html.DeleteFormatForGrid(gridId), Width = 52 }))
</div>
}
AddressesGridCrud/Create.cshtml
@model AweCoreDemo.ViewModels.Input.RestaurantAddressInput

@using (Html.BeginForm())
{
@Html.EditorFor(o => o.RestaurantId)
@Html.EditorFor(o => o.Line1)
@Html.EditorFor(o => o.Line2)
@Html.EditorFor(o => o.ChefId)
}
Demos/Grid/MasterDetailCrud/MasterDetailCrudDemoController.cs
public class MasterDetailCrudDemoController : Controller
{
public IActionResult Index()
{
return View();
}

public IActionResult RestaurantGridGetItems(GridParams g)
{
var model = new GridModelBuilder<Restaurant>(Db.Restaurants.Where(o => o.IsCreated).AsQueryable(), g)
{
Key = "Id",
GetItem = () => Db.Get<Restaurant>(Convert.ToInt32(g.Key))
}.Build();
return Json(model);
}

public IActionResult Create()
{
// needed so we could add addresses even before the restaurant is created/saved
var rest = Db.Insert(new Restaurant());

return PartialView(new RestaurantInput { Id = rest.Id });
}

[HttpPost]
public IActionResult Create(RestaurantInput input)
{
if (!ModelState.IsValid)
{
return PartialView(input);
}

var restaurant = Db.Get<Restaurant>(input.Id);
restaurant.Name = input.Name;
restaurant.IsCreated = true;

return Json(restaurant); // use MapToGridModel like in Grid Crud Demo when grid uses Map
}

public IActionResult Edit(int id)
{
var rest = Db.Get<Restaurant>(id);
return PartialView("Create", new RestaurantInput { Id = id, Name = rest.Name });
}

[HttpPost]
public IActionResult Edit(RestaurantInput input)
{
if (!ModelState.IsValid)
{
return PartialView("Create", input);
}

var rest = Db.Get<Restaurant>(Convert.ToInt32(input.Id));

rest.Name = input.Name;

return Json(new { rest.Id });
}

public IActionResult Delete(int id, string gridId)
{
var restaurant = Db.Get<Restaurant>(id);

return PartialView(new DeleteConfirmInput
{
Id = id,
Type = "restaurant",
Name = restaurant.Name
});
}

[HttpPost]
public IActionResult Delete(DeleteConfirmInput input)
{
Db.Delete<Restaurant>(input.Id);
return Json(new { input.Id });
}
}
Demos/Grid/MasterDetailCrud/AddressesGridCrudController.cs
public class AddressesGridCrudController : Controller
{
private object mapToGridModel(RestaurantAddress o)
{
return new
{
o.Id,
o.Line1,
o.Line2,
ChefName = o.Chef.FirstName + " " + o.Chef.LastName,

ChefId = o.Chef.Id // for inline editing, gives value to the Chef inline dropdown
};
}

public IActionResult GridGetItems(GridParams g, int restaurantId)
{
var items = Db.RestaurantAddresses.Where(o => o.RestaurantId == restaurantId).AsQueryable();
var model = new GridModelBuilder<RestaurantAddress>(items, g)
{
KeyProp = o => o.Id,
Map = mapToGridModel,
GetItem = () => Db.Get<RestaurantAddress>(Convert.ToInt32(g.Key))
}.Build();
return Json(model);
}

public IActionResult Create(int restaurantId)
{
return PartialView(new RestaurantAddressInput { RestaurantId = restaurantId });
}

[HttpPost]
public IActionResult Create(RestaurantAddressInput input)
{
if (!ModelState.IsValid)
{
return PartialView(input);
}

var address = Db.Insert(new RestaurantAddress
{
Line1 = input.Line1,
Line2 = input.Line2,
RestaurantId = input.RestaurantId,
Chef = Db.Get<Chef>(input.ChefId)
});

return Json(mapToGridModel(address));
}

public IActionResult Edit(int id)
{
var address = Db.Get<RestaurantAddress>(id);

return PartialView(
"Create",
new RestaurantAddressInput
{
Line1 = address.Line1,
Line2 = address.Line2,
ChefId = address.Chef.Id,
RestaurantId = address.RestaurantId
});
}

[HttpPost]
public IActionResult Edit(RestaurantAddressInput input)
{
if (!ModelState.IsValid)
{
return PartialView("Create", input);
}

var address = Db.Get<RestaurantAddress>(input.Id);
address.Line1 = input.Line1;
address.Line2 = input.Line2;
address.Chef = Db.Get<Chef>(input.ChefId);

return Json(new { input.Id });
}

public IActionResult Delete(int id)
{
var address = Db.Get<RestaurantAddress>(id);

return PartialView(new DeleteConfirmInput
{
Id = id,
Type = "restaurant address",
Name = address.Line1 + " "+ address.Line2
});
}

[HttpPost]
public IActionResult Delete(DeleteConfirmInput input)
{
Db.Delete<RestaurantAddress>(input.Id);
return Json(new { input.Id });
}

#region for inline editing
[HttpPost]
public IActionResult CreateInline(RestaurantAddressInput input)
{
if (!ModelState.IsValid)
{
return Json(ModelState.GetErrorsInline());
}

var ent = Db.Insert(new RestaurantAddress
{
RestaurantId = input.RestaurantId,
Line1 = input.Line1,
Line2 = input.Line2,
Chef = Db.Get<Chef>(input.ChefId)
});

return Json(new { Item = mapToGridModel(ent) });

}

[HttpPost]
public IActionResult EditInline(RestaurantAddressInput input)
{
if (!ModelState.IsValid)
{
return Json(ModelState.GetErrorsInline());
}

var ent = Db.Get<RestaurantAddress>(input.Id);
ent.Line1 = input.Line1;
ent.Line2 = input.Line2;
ent.Chef = Db.Get<Chef>(input.ChefId);

Db.Update(ent);

return Json(new { });
}
#endregion
}

Inline editing inside details popup

MasterDetailCrudDemo/Index.cshtml
@{ var gridIE = "RestaurantGridIE1"; }
@Html.InitCrudPopupsForGrid(gridIE, "MasterDetailInline", 470, 1000)

<div class="bar">
@Html.CreateButtonForGrid(gridIE)
</div>

@(Html.Awe().Grid(gridIE)
.Height(350)
.Attr("data-syncg", "rest") // crud sync using signalr in site.js
.Url(Url.Action("RestaurantGridGetItems"))
.Groupable(false)
.Columns(
new Column { ClientFormat = ".(Id)", Header = "Id", Width = 70 },
new Column { Bind = "Name" },
new Column { ClientFormat = GridUtils.EditFormatForGrid(gridIE), Width = 55 },
new Column { ClientFormat = GridUtils.DeleteFormatForGrid(gridIE), Width = 55 }))
MasterDetailInline/Create.cshtml
@model AweCoreDemo.ViewModels.Input.RestaurantInput
@using (Html.Awe().BeginContext())
{
var gridId = "AddressesGrid";
using (Html.BeginForm())
{
@Html.EditorFor(o => o.Id)
@Html.EditorFor(o => o.Name)
}

<div class="bar">
@Html.InlineCreateButtonForGrid(gridId, new { restaurantId = Model.Id }, "Add address")
</div>
<div>
@(Html.Awe().Grid(gridId)
.Url(Url.Action("AddressGrid", "MasterDetailInline"))
.Mod(o => o.InlineEdit(Url.Action("CreateAddr"), Url.Action("EditAddr"), rowClickEdit: true))
.Parameter("restaurantId", Model.Id)
.Attr("data-syncg", "addr")
.Height(230)
.Groupable(false)
.Columns(
new Column { Bind = "Id", Width = 75 }
.Mod(o => o.InlineId()),

new Column { Bind = "Line1,Line2", ClientFormat = ".(Line1) .(Line2)", Header = "Address" }
.Mod(o => o.Inline(Html.Awe().TextBox("Line1"))
.Inline(Html.Awe().TextBox("Line2"))),

new Column { Bind = "Chef.FirstName,Chef.LastName", ClientFormat = ".(ChefName)", Header = "Chef" }
.Mod(o => o.Inline(Html.Awe().AjaxRadioList("ChefId")
.Url(Url.Action("GetChefs", "Data"))
.Odropdown())),

new Column { ClientFormat = GridUtils.InlineEditFormat(), Width = 80 },
new Column { ClientFormat = Html.InlineDeleteFormatForGrid(gridId), Width = 85 }))
</div>
}

Master Detail CRUD using in nest editing

This demo is similar to the first one which uses PopupForms, except here the Restaurant PopupForms (create/edit/delete) are opened inside grid nests
MasterDetailCrudDemo/Index.cshtml
@{ var grid2 = "RestaurantGrid2"; }
@Html.InitCrudForGridNest(grid2, "MasterDetailCrudDemo")

<div class="bar">
<button type="button" onclick="utils.nestCreate('@grid2', 'create@(grid2)')" class="awe-btn mbtn">Create</button>
</div>

@(Html.Awe().Grid(grid2)
.Url(Url.Action("RestaurantGridGetItems"))
.PageSize(10)
.Attr("data-syncg", "rest")
.Nests(
new Nest { Name = "editnst", GetFunc = "utils.loadNestPopup('edit" + grid2 + "')" },
new Nest { Name = "delnst", GetFunc = "utils.loadNestPopup('delete" + grid2 + "')" })
.Columns(
new Column { ClientFormat = ".Id", Header = "Id", Width = 70 },
new Column { Bind = "Name" },
new Column { ClientFormat = GridUtils.EditGridNestFormat(), Width = 55 },
new Column { ClientFormat = GridUtils.DeleteGridNestFormat(), Width = 55 }))

Master Detail CRUD using Inline Editing and nesting

MasterDetailCrudDemo/Index.cshtml
@{
var grid3 = "RestaurantGridInline";
}

@Html.InitDeletePopupForGrid(grid3)

<div class="bar">
@Html.Awe().Button().Text("Create").OnClick("$('#" + grid3 + "').data('api').inlineCreate()").CssClass("mbtn")
</div>

@(Html.Awe().Grid(grid3)
.Attr("data-syncg", "rest")
.Url(Url.Action("RestaurantGridGetItems"))
.Mod(o => o.InlineEdit(Url.Action("Create", "RestInl"), Url.Action("Edit", "RestInl"), rowClickEdit: true))
.Groupable(false)
.Nests(new Nest { Name = "detailnst", Url = Url.Action("Addresses", "RestInl"), LoadOnce = true })
.Columns(
new Column { ClientFormat = ".(Id)", Header = "Id", Width = 70 }
.Mod(o => o.InlineId()),

new Column { Bind = "Name" }
.Mod(o => o.Inline(Html.Awe().TextBox("Name"))),

new Column
{
ClientFormat = "<button type='button' class='awe-btn o-pad detailnst'>details <i class='caretc'><i class='o-caret'></i></i></button>",
Width = 105
},
new Column { ClientFormat = GridUtils.InlineEditFormat(), Width = 80 },
new Column { ClientFormat = Html.InlineDeleteFormatForGrid(grid3), Width = 90 }))
<style>
/* hide addresses button for new rows */
.o-glnew .detailnst {
display: none;
}

.caretc {
position: relative;
padding: .4em .5em;
display: inline-block;
}

.caretc .o-caret {
transform: rotate(-90deg);
zoom: 1.1;
}

.detailnst-on .caretc .o-caret {
transform: rotate(0);
}
</style>



Comments