Grid inline editing always in edit mode
Grid inline editing with Always edit mode and batch save.
StopLoad is set, so the grid won't load (change page, sort) if there are unsaved changes, and will scroll to first row with changes.
We could also handle the change event on the grid, and call grid api batchSave to save instantly on each change.
StopLoad is set, so the grid won't load (change page, sort) if there are unsaved changes, and will scroll to first row with changes.
We could also handle the change event on the grid, and call grid api batchSave to save instantly on each change.
Shared/Demos/GridAlwaysEdit.cshtml
@{
var gridId = "GridAlwaysEdit";
}
<div class="bar">
<button type="button" onclick="$('#@gridId').data('api').inlineCreate()" class="awe-btn">Create</button>
<button type="button" onclick="$('#@gridId').data('api').batchSave()" class="awe-btn alwEdStBtn">Save All</button>
<button type="button" onclick="$('#@gridId').data('api').inlineCancelAll()" class="awe-btn alwEdStBtn">Cancel All (Undo changes)</button>
</div>
@Html.InitDeletePopupForGrid(gridId, "GridInlineEditDemo")
@(Html.Awe().Grid(gridId)
.Mod(o => o.Main(false))
.Url(Url.Action("GridGetData", "GridInlineBatchEditing"))
.InlEdit(new InlEditOpt
{
SaveUrl = Url.Action("BatchSave", "GridInlineBatchEditing"),
Batch = true,
AlwaysEdit = true,
StopLoad = true
})
.CssClass("noalt") // no alt color rows
.Height(390)
.Resizable()
.Reorderable()
.Attr("data-syncg", "dinner")
.Columns(
new Column { Bind = "Id", Width = 75 }
.InlId(),
new Column { Bind = "Name" }
.InlString(),
new Column { Bind = "Date", Width = 160 }
.InlDate(),
new Column { Bind = "Chef.FirstName,Chef.LastName", Prop = "ChefName", Header = "Chef", MinWidth = 170 }
.InlDropdownList(new DropdownListOpt { Name = "ChefId", Url = Url.Action("GetChefs", "Data") }),
new Column { Prop = "Meals", Header = "Meals", MinWidth = 250, Grow = 2 }
.InlMultiselect(new MultiselectOpt { Name = "MealsIds", Url = Url.Action("GetMealsImg", "Data") }),
new Column { Bind = "BonusMeal.Name", Prop = "BonusMeal", Header = "Bonus Meal" }
.InlDropdownList(new DropdownListOpt
{
Name = "BonusMealId",
Url = Url.Action("GetMealsImg", "Data")
}
.ImgItem()),
new Column { Bind = "Organic", Width = 90, Prop = "DispOrganic" }
.Inl(Html.Awe().Toggle(new ToggleOpt { Name = "Organic" })),
Html.InlCancelColumn(),
Html.InlDeleteAlwaysColumn(gridId)
))
@*name of each inline helper/editor is matching the names of DinnerInput properties, and the names of row model properties set in MapToGridModel*@
@* certain helper data urls are configured to be cached globally (in site.js),
that's why when the grid loads all rows in edit mode you may not notice 10 xhr requests for Url.Action("GetChefs", "Data"), Url.Action("GetMealsImg", "Data"), etc. *@
<script>
// disable/enable save and cancel buttons when the grid has changed or new rows
$(function () {
var grid = $('#@gridId');
updateBtnState();
grid.on('change awerender awerowch', updateBtnState);
});
function updateBtnState() {
var grid = $('#@gridId');
$('.alwEdStBtn').prop('disabled', !grid.find('.o-hasch, .o-glnew').length);
}
</script>
@*optional grid client instant validation (for MVC5 must be placed in a cshtml with @model DinnerInput)*@
@(Html.BindOVldForId<DinnerInput>(gridId))
Demos/Grid/GridInlineEditDemoController.cs
public class GridInlineEditDemoController : Controller
{
private readonly MyContext mcx = new MyContext();// mock EF Db context
public GridInlineEditDemoController()
{
}
private 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,
o.Organic,
DispOrganic = o.Organic ? "Yes" : "No",
// below properties used for inline editing only
MealsIds = o.Meals.Select(m => m.Id).ToArray(),
ChefId = o.Chef.Id,
BonusMealId = o.BonusMeal.Id,
// for conditional demo
Editable = o.Meals.Count() > 1,
DateReadOnly = o.Date.Year < 2015
};
}
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());
// you can use .Build() for non EF datasource (like a List<Dinner> query)
// EFBuildAsync - custom extension for EF, you can copy it from demo source code
}
[HttpPost]
public async Task<IActionResult> Save(DinnerInput input)
{
// custom validation example
if (ModelState.IsValid && input.Name.Contains("asdf"))
{
ModelState.AddModelError("Name", "Name can't contain asdf");
}
if (ModelState.IsValid)
{
var isCreate = !input.Id.HasValue;
var ent = isCreate ? new Dinner() :
await mcx.Dinners
.Include(o => o.Meals)
.FirstOrDefaultAsync(o => o.Id == input.Id);
if (ent == null)
{
throw new Exception("Item doesn't exist anymore, 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();
return Json(new { Item = MapToGridModel(ent) });
}
return Json(ModelState.GetErrorsInline());
}
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)
{
mcx.Remove(await mcx.FindAsync<Dinner>(input.Id));
await mcx.SaveChangesAsync();
// the PopupForm's success function aweUtils.itemDeleted expects an obj with "Id" property
return Json(new { input.Id });
}
public IActionResult Index()
{
return View();
}
public IActionResult ConditionalDemo()
{
return View();
}
public IActionResult MultiEditorsDemo()
{
return View();
}
public IActionResult ClientSave()
{
return View();
}
public IActionResult AlwaysEdit()
{
return View();
}
}
Comments