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.Awex().Grid<Dinner>(gridId)
.Main()
.ColumnsSelector(false)
.Mod(o => o.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
},
type: typeof(DinnerInput))
.Url(Url.Action("GridGetData", "GridInlineBatchEditing"))
.Height(350)
.Groupable(false)
.Resizable()
.Attr("data-syncg", "dinner")
.Columns(b => {
b.Add(o => o.Id).Width(75)
.InlHiddenId()
.InlVldSummary();

b.Add(o => o.Name)
.InlString(new TextBoxOpt { ClearBtn = true });

b.Add(o => o.Date).Width(160)
.InlDate();

b.Add(o => new {o.Chef.FirstName, o.Chef.LastName}).MinWidth(170)
.InlDropdownList(new DropdownListOpt { Name = "ChefId", Url = Url.Action("GetChefs", "Data") });

b.Add(o => o.Meals.Select(m => m.Name)).MinWidth(250).Grow(2)
.InlMultiselect(new MultiselectOpt
{
Name = "MealsIds",
Url = Url.Action("GetMealsImg", "Data")
}
.ImgItem());

b.Add(o => o.BonusMeal.Name).Header("Bonus Meal")
.InlDropdownList(new DropdownListOpt
{
Name = "BonusMealId",
Url = Url.Action("GetMealsImg", "Data")
}.ImgItem());

b.Add(o => o.Organic).Width(90)
.Inl(Html.Awe().Toggle(new ToggleOpt{ Name = "Organic"}));

b.Add(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();
}

public IActionResult GridGetData(GridParams g, string search)
{
var gmb = new GridModelBuilder<Dinner>(Db.Dinners.AsQueryable(), g)
{
KeyProp = o => o.Id, // needed for api select, update, tree, nesting, EF
};

gmb.FilterContainsStr(o => o.Name, search);

gmb.Prop(o => o.Meals.Select(m => m.Name), "Meals");
gmb.Prop(o => o.Organic, "DispOrganic", o => o ? "Yes" : "No");

// props used for inline editing controls values
gmb.PropVal(o => o.Meals.Select(m => m.Id), "MealsIds", o => o);
gmb.PropVal(o => o.Chef.Id, "ChefId");
gmb.PropVal(o => o.BonusMeal.Id, "BonusMealId");

return this.AweJson(gmb.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 = nameof(Dinner).ToLower(),
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