Grid CRUD using Popup Demo

Grid crud, built using the Grid and PopupForm helpers.
There are three PopupForms for create, edit and delete, they are initialized via InitCrudPopupsForGrid custom helper, each PopupForm has Success js function assigned.
Create post action returns the grid model for the new Item so the js func will render and append it.
Edit post action returns the item's Id and the js func is using grid.api.update to update the row (api.update will call GridModelBuilder.GetItem).
Delete PopupForm has OnLoad js func used to animate the row that is about to be deleted, and the post action will delete the item and return the item's Id, the js func will use grid.api.select to select and remove the row, if there's no rows left grid.api.load will be called.
Shared/Demos/GridCrud.cshtml
@{
var gid = "DinnersGrid";
}
@Html.InitCrudPopupsForGrid(gid, "DinnersGridCrud")

<div class="bar">
<div style="float: right;">
@Html.Awe().TextBox("txtSearch").Placeholder("search...").CssClass("searchtxt")
</div>

@(Html.Awe().Button()
.Text("Create")
.OnClick(Html.Awe().OpenPopup("create" + gid)))
</div>

@(Html.Awe().Grid(gid)
.Mod(o => o.Main())
.Url(Url.Action("GridGetItems", "DinnersGridCrud"))
.Parent("txtSearch", "search")
.Attr("data-syncg", "dinner")
.Height(350)
.Columns(
new Column { Bind = "Id", Width = 75 },
new Column { Bind = "Name" }.Mod(o => o.Nohide()),
new Column { Bind = "Date" },
new Column { Bind = "Chef.FirstName,Chef.LastName", Prop = "ChefName", Header = "Chef" },
new Column { Prop = "Meals", Header = "Meals", Grow = 2, MinWidth = 200 },
new Column { Bind = "BonusMeal.Name", Prop = "BonusMeal", Header = "Bonus Meal"},
Html.EditColumnForGrid(gid),
Html.DeleteColumnForGrid(gid)))
Demos/Grid/DinnersGridCrudController.cs
/// </summary>
/// Edit and Delete post actions must return an object with property "Id" - in aweUtils.js itemEdited and itemDeleted functions expect it this way;
/// parameters for Edit, Delete controller Actions need to remain called "id", that's how they are set in GridUtils.cs ( "params:{{ id:" );
/// or in MapToGridModel additionally to o.MyId add another property Id = o.MyId
/// and for the action columns (edit, delete btns) you can either specify the MyId property (e.g. GridUtils.EditFormatForGrid("DinnersGrid", "MyId"));
/// change the Bind = "MyId" (if you have an Id column),
/// Note: If your Key Property is not "Id" (but "MyId"), you need to change the GridModelBuilder.Key = "MyId" in GridGetItems and in the view
/// <summary>
public class DinnersGridCrudController : Controller
{
private readonly MyContext mcx = new MyContext();// mock EF Db context

public DinnersGridCrudController()
{

}

private static object MapToGridModel(Dinner o)
{
return
new
{
o.Id,
o.Name,
Date = o.Date.ToShortDateString(),
ChefName = o.Chef.FullName,
Meals = string.Join(", ", o.Meals.Select(m => m.Name)),
BonusMeal = o.BonusMeal.Name
};
}

public async Task<IActionResult> GridGetItems(GridParams g, string search = "")
{
var query = mcx.Dinners
.Include(o => o.Chef)
.Include(o => o.Meals)
.Include(o => o.Country)
.Include(o => o.BonusMeal)
.Where(o => o.Name.Contains(search)).AsQueryable();

var model = new GridModelBuilder<Dinner>(query, g)
{
KeyProp = o => o.Id, // needed for api select, update, tree, nesting, EF
Map = MapToGridModel,
};

return Json(await model.EFBuildAsync());
}

public IActionResult Create()
{
// make sure to use "return PartialView" for PopupForm/Popup views
// this will ignore _viewstart.cshtml so that you don't use the _Layout.cshtml and reload all the scripts
return PartialView();
}

[HttpPost]
public async Task<IActionResult> Create(DinnerInput input)
{
return await save(input);
}

public async Task<IActionResult> Edit(int id)
{
var dinner = await mcx.Dinners
.Include(o => o.Chef)
.Include(o => o.Meals)
.Include(o => o.BonusMeal)
.SingleAsync(o => o.Id == id);

var input = new DinnerInput
{
Id = dinner.Id,
Name = dinner.Name,
ChefId = dinner.Chef.Id,
Date = dinner.Date,
MealsIds = dinner.Meals.Select(o => o.Id),
BonusMealId = dinner.BonusMeal.Id
};

return PartialView("Create", input);
}

[HttpPost]
public async Task<IActionResult> Edit(DinnerInput input)
{
return await save(input);
}

private async Task<IActionResult> save(DinnerInput input)
{
if (!ModelState.IsValid) return PartialView("Create", input);

var isCreate = !input.Id.HasValue;

var ent = isCreate ? new Dinner() :
await mcx.Dinners
.Include(o => o.Meals)
.FirstAsync(o => o.Id == input.Id);

ent.Name = input.Name;
ent.Date = input.Date.Value;
ent.Chef = await mcx.FindAsync<Chef>(input.ChefId);

// ToList req for EF
ent.Meals = await mcx.Meals
.Where(o => input.MealsIds.Contains(o.Id)).ToListAsync();

ent.BonusMeal = await mcx.FindAsync<Meal>(input.BonusMealId);
ent.Organic = input.Organic ?? false;

if (isCreate)
{
mcx.Add(ent);
}

await mcx.SaveChangesAsync();

if (isCreate)
{
// the create PopupForm's success function aweUtils.itemCreated expects the grid row model obj, to render and append the new row
return Json(MapToGridModel(ent));
}

// the edit PopupForm's success function aweUtils.itemEdited expects an obj with "Id" property
return Json(new { Id = ent.Id });
}

public async Task<IActionResult> Delete(int id)
{
var dinner = await mcx.FindAsync<Dinner>(id);

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

[HttpPost]
public async Task<IActionResult> Delete(DeleteConfirmInput input)
{
await mcx.DeleteAsync<Dinner>(input.Id);

// the delete PopupForm's success function aweUtils.itemDeleted expects an obj with "Id" property
return Json(new { Id = input.Id });
}
}
DinnersGridCrud/Create.cshtml
@model AweCoreDemo.ViewModels.Input.DinnerInput
@using (Html.BeginForm())
{
using (Html.Awe().BeginContext())
{
@Html.EditorFor(o => o.Name)
@Html.EditorFor(o => o.Date)
@Html.EditorFor(o => o.ChefId)
@Html.EditorFor(o => o.MealsIds)
@Html.EditorFor(o => o.BonusMealId)
}
}

Grid Crud Inline Popup

Crud popups are open inline, inside a div identified by id.
GridCrudDemo/Index.cshtml
@{
var gid1 = "GridInline1";
}
@Html.InitCrudPopupsForGrid(gid1, "DinnersGridCrud", inlineContId: "inlCont1")

<div class="bar">
<div style="float: right;">
@Html.Awe().TextBox("txtSearch1").Placeholder("search...").CssClass("searchtxt")
</div>

@(Html.Awe().Button()
.Text("Create")
.OnClick(Html.Awe().OpenPopup("create" + gid1)))
</div>

@(Html.Awe().Grid(gid1)
.Mod(o => o.Main())
.Url(Url.Action("GridGetItems", "DinnersGridCrud"))
.Parent("txtSearch1", "search")
.Attr("data-syncg", "dinner")
.Height(350)
.Columns(
new Column { Bind = "Id", Width = 75 },
new Column { Bind = "Name" }.Mod(o => o.Nohide()),
new Column { Bind = "Date" },
new Column { Bind = "Chef.FirstName,Chef.LastName", Prop = "ChefName", Header = "Chef" },
new Column { Prop = "Meals", Header = "Meals", Grow = 2, MinWidth = 200 },
new Column { Bind = "BonusMeal.Name", Prop = "BonusMeal", Header = "Bonus Meal"},
Html.EditColumnForGrid(gid1),
Html.DeleteColumnForGrid(gid1)
))

<script>
$(function () {
var inlc = $('#inlCont1');
inlc.on('aweopen', function () {
awe.flash(inlc);
});
});
</script>

<div id="inlCont1"></div>



Comments