Client validation for grid inline editing


In addition to the validation rules defined in the ViewModel (server-side), this demo also includes client-side validation rules:
* The Name field cannot contain 'asdf'.
* If Organic is set to true, the Name field must be at least 5 characters long.
GridInlineClientValidation/Index.cshtml
@{
var gridId = "DinnersGrid2";
var initObj = new
{
Name = DemoUtils.RndName(),
Date = DateTime.Now.ToShortDateString(),
ChefId = Db.Chefs.First().Id,
MealsIds = Db.Meals.Take(2).Select(o => o.Id).ToArray()
};
}
<div class="bar">
<button type="button" onclick="$('#@gridId').data('api').inlineCreate()" class="awe-btn">Create</button>
@Html.InlineCreateButtonForGrid(gridId, initObj, "Create with predefined values")
<button type="button" onclick="$('#@gridId').data('api').batchSave()" class="awe-btn">Save All</button>
<button type="button" onclick="$('#@gridId').data('api').inlineCancelAll()" class="awe-btn">Cancel All</button>
</div>

@Html.InitDeletePopupForGrid(gridId, "GridInlineEditDemo")

@(Html.Awe().Grid(gridId)
.Mod(o => o.Main(false)
.BatchEdit(Url.Action("BatchSave", "GridInlineBatchEditing")))
.Validation(b =>
{
b.Prop("Name", "noAsdf", "Name can't contain asdf");
b.Prop("Name", "organicNameMinLen", "Name length can't be less than 5 when Organic = yes");
b.Relate("Organic", "Name"); // when checking Organic Name will also be updated
})
.Url(Url.Action("GridGetData", "GridInlineBatchEditing"))
.Height(350)
.Groupable(false)
.Resizable()
.Attr("data-syncg", "dinner")
.Columns(
new Column { Bind = "Id", Width = 75 }
.InlHiddenId()
.InlVldSummary(),

new Column { Bind = "Name" }
.InlString(new TextBoxOpt{ ClearBtn = true}),

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")
}
.ImgItem()),

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.InlEditDelColumn(gridId)
))

<script>
function noAsdf(o) {
return o.v.indexOf('asdf') !== -1;
}

function organicNameMinLen(o) {
var row = $(o.input).closest('.awe-row');
var org = row.find('[name="Organic"]');
var name = row.find('[name="Name"]');

if (org.val() === 'true') {
return (name.val().length < 5);
}
}
</script>
Demos/Grid/GridInlineBatchEditingController.cs
public class GridInlineBatchEditingController : Controller
{
public IActionResult Index()
{
return View();
}

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.ToString()).ToArray(), // value for meals multiselect
ChefId = o.Chef.Id, // value for chef dropdown
BonusMealId = o.BonusMeal.Id // value for bonus meal dropdown
};
}

public IActionResult GridGetData(GridParams g, string search = "")
{
var query = Db.Dinners.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,
GetItem = () => Db.Get<Dinner>(Convert.ToInt32(g.Key)), // used when calling api.update (signalrSync.js)
};

return Json(model.Build());
}

[HttpPost]
public IActionResult BatchSave(DinnerInput[] inputs)
{
var res = new List<object>();

foreach (var input in inputs)
{
var vldState = ModelUtil.Validate(input);

// custom validation example
if (input.Name != null && input.Name.Contains("aaa"))
{
vldState.Add("Name", "Name can't contain aaa");
}

if (vldState.IsValid())
{
try
{
var isCreate = !input.Id.HasValue;

var ent = isCreate ? new Dinner() :
Db.Dinners.First(o => o.Id == input.Id);

ent.Name = input.Name;
ent.Date = input.Date.Value;
ent.Chef = Db.Find<Chef>(input.ChefId);

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

ent.BonusMeal = Db.Find<Meal>(input.BonusMealId);
ent.Organic = input.Organic ?? false;

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

res.Add(new { });
}
catch (Exception ex)
{
vldState.Add("Name", ex.Message);
}
}

if (!vldState.IsValid())
{
res.Add(vldState.ToInlineErrors());
}
}


return Json(res);
}

public IActionResult Delete(int id)
{
var dinner = Db.Find<Dinner>(id);

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

[HttpPost]
public IActionResult Delete(DeleteConfirmInput input)
{
Db.Remove(Db.Find<Dinner>(input.Id));

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



Comments