Grid CRUD

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
@Html.InitCrudPopupsForGrid("DinnersGrid", "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("createDinnersGrid")))
</div>

@(Html.Awe().Grid("DinnersGrid")
.Mod(o => o.ColumnsSelector().ColumnsAutohide().PageInfo())
.Url(Url.Action("GridGetItems", "DinnersGridCrud"))
.Parent("txtSearch", "search")
.Attr("data-syncg", "dinner")
.Height(350)
.Columns(
new Column { Bind = "Id", Width = 55 },
new Column { Bind = "Name" }.Mod(o => o.NoAutohide()),
new Column { Bind = "Date" },
new Column { Bind = "Chef.FirstName,Chef.LastName", ClientFormat = ".ChefName", Header = "Chef" },
new Column { ClientFormat = ".Meals", Header = "Meals" },
new Column { ClientFormat = Html.EditFormatForGrid("DinnersGrid"), Width = 55 }.Mod(o => o.Nohide()),
new Column { ClientFormat = Html.DeleteFormatForGrid("DinnersGrid"), Width = 55 }.Mod(o => o.Nohide())))
Demos/Grid/DinnersGridCrudController.cs
/// <summary>
/// Note: If your Key Property is not "Id" (but "MyId"), you need to change the Key = "MyId" in GridGetItems and it in the view
/// change the Bind = "MyId" (if you're showing the id column),
/// and for the action columns you can either specify the MyId property (e.g. GridUtils.EditFormatForGrid("DinnersGrid", "MyId"));
/// or in MapToGridModel additionally to o.MyId add another property Id = o.MyId
/// parameters for Edit, Delete controller Actions need to remain called "id", that's how they are set in GridUtils.cs (params:{{ id: );
/// Edit and Delete post actions must return an object with property "Id" - in utils.js itemEdited and itemDeleted funcs expect it this way;
/// </summary>
public class DinnersGridCrudController : Controller
{
private static object MapToGridModel(Dinner o)
{
return
new
{
o.Id,
o.Name,
Date = o.Date.ToShortDateString(),
ChefName = o.Chef.FirstName + " " + o.Chef.LastName,
Meals = string.Join(", ", o.Meals.Select(m => m.Name))
};
}

public ActionResult GridGetItems(GridParams g, string search)
{
search = (search ?? "").ToLower();
var items = Db.Dinners.Where(o => o.Name.ToLower().Contains(search)).AsQueryable();

return Json(new GridModelBuilder<Dinner>(items, g)
{
Key = "Id", // needed for api select, update, tree, nesting, EF
GetItem = () => Db.Get<Dinner>(Convert.ToInt32(g.Key)), // called by the grid.api.update ( edit popupform success js func )
Map = MapToGridModel
}.Build());
}

public ActionResult 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 ActionResult Create(DinnerInput input)
{
if (!ModelState.IsValid) return PartialView(input);

var dinner = Db.Insert(new Dinner
{
Name = input.Name,
Date = input.Date.Value,
Chef = Db.Get<Chef>(input.Chef),
Meals = input.Meals.Select(mid => Db.Get<Meal>(mid)),
BonusMeal = Db.Get<Meal>(input.BonusMealId)
});

return Json(MapToGridModel(dinner)); // returning grid model, used in grid.api.renderRow
}

public ActionResult Edit(int id)
{
var dinner = Db.Get<Dinner>(id);

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

return PartialView("Create", input);
}

[HttpPost]
public ActionResult Edit(DinnerInput input)
{
if (!ModelState.IsValid) return PartialView("Create", input);
var dinner = Db.Get<Dinner>(input.Id);

dinner.Name = input.Name;
dinner.Date = input.Date.Value;
dinner.Chef = Db.Get<Chef>(input.Chef);
dinner.Meals = input.Meals.Select(mid => Db.Get<Meal>(mid));
dinner.BonusMeal = Db.Get<Meal>(input.BonusMealId);
Db.Update(dinner);

// returning the key to call grid.api.update
return Json(new { Id = dinner.Id });
}

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

return PartialView(new DeleteConfirmInput
{
Id = id,
Message = string.Format("Are you sure you want to delete dinner <b>{0}</b> ?", dinner.Name)
});
}

[HttpPost]
public ActionResult Delete(DeleteConfirmInput input)
{
Db.Delete<Dinner>(input.Id);
return Json(new { Id = input.Id });
}
}
DinnersGridCrud/Create.cshtml
@model AwesomeMvcDemo.ViewModels.Input.DinnerInput
@using (Html.BeginForm())
{
using (Html.Awe().BeginContext())
{
@Html.EditorFor(o => o.Name)
@Html.EditorFor(o => o.Date)
@Html.EditorFor(o => o.Chef)
@Html.EditorFor(o => o.Meals)
@Html.EditorFor(o => o.BonusMealId)
}
}
Helpers/Awesome/CrudHelpers.cs
public static IHtmlString InitCrudPopupsForGrid<T>(
this HtmlHelper<T> html,
string gridId,
string crudController,
int createPopupHeight = 430,
int maxWidth = 0)
{
var url = GetUrlHelper(html);

gridId = html.Awe().GetContextPrefix() + gridId;

var result =
html.Awe()
.InitPopupForm()
.Name("create" + gridId)
.Group(gridId)
.Height(createPopupHeight)
.MaxWidth(maxWidth)
.Url(url.Action("Create", crudController))
.Title("Create item")
.Modal()
.Success("utils.itemCreated('" + gridId + "')")
.ToString()

+ html.Awe()
.InitPopupForm()
.Name("edit" + gridId)
.Group(gridId)
.Height(createPopupHeight)
.MaxWidth(maxWidth)
.Url(url.Action("Edit", crudController))
.Title("Edit item")
.Modal()
.Success("utils.itemEdited('" + gridId + "')")

+ html.Awe()
.InitPopupForm()
.Name("delete" + gridId)
.Group(gridId)
.Url(url.Action("Delete", crudController))
.Success("utils.itemDeleted('" + gridId + "')")
.OnLoad("utils.delConfirmLoad('" + gridId + "')") // calls grid.api.select and animates the row
.Height(200)
.Modal();

return new MvcHtmlString(result);
}
Utils/GridUtils.cs
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using Omu.AwesomeMvc;

namespace AwesomeMvcDemo.Utils
{
public static class GridUtils
{
private static string GetPopupName<T>(this HtmlHelper<T> html, string action, string gridId)
{
return action + html.Awe().GetContextPrefix() + gridId;
}

public static string InlineDeleteFormatForGrid<T>(this HtmlHelper<T> html, string gridId, string key = "Id", bool nofocus = false, string text = "Cancel")
{
var popupName = html.GetPopupName("delete", gridId);

return DeleteFormat(popupName, key, btnClass: "o-glh", nofocus: nofocus)
+ html.Awe()
.Button()
.Text(text)
.CssClass("o-glcanb awe-nonselect o-gl o-pad");
}

public static IHtmlString InlineCreateButtonForGrid<T>(this HtmlHelper<T> html, string gridId, object parameters = null, string text = "Create")
{
gridId = html.Awe().GetContextPrefix() + gridId;
var parms = new JavaScriptSerializer().Serialize(parameters);

return html.Awe().Button()
.Text(text)
.CssClass("mbtn")
.OnClick(string.Format("$('#{0}').data('api').inlineCreate({1})", gridId, parms));
}

public static IHtmlString CreateButtonForGrid<T>(this HtmlHelper<T> html, string gridId, object parameters = null, string text = "Create")
{
return html.Awe().Button()
.Text(text)
.CssClass("mbtn")
.OnClick(html.Awe().OpenPopup(html.GetPopupName("create", gridId)).Params(parameters));
}

public static string EditFormatForGrid<T>(this HtmlHelper<T> html, string gridId, string key = "Id", bool setId = false, bool nofocus = false, int? height = null)
{
var popupName = html.GetPopupName("edit", gridId);

var click = html.Awe().OpenPopup(popupName).Params(new { id = "." + key });
if (height.HasValue)
{
click.Height(height.Value);
}

var button = html.Awe().Button()
.CssClass("awe-nonselect editbtn")
.Text("<span class='ico-crud ico-edit'></span>")
.OnClick(click);

var attrdict = new Dictionary<string, object>();

if (setId)
{
attrdict.Add("id", $"gbtn{popupName}.{key}");
}

if (nofocus)
{
attrdict.Add("tabindex", "-1");
}

button.HtmlAttributes(attrdict);

return button.ToString();
}

public static string DeleteFormatForGrid<T>(this HtmlHelper<T> html, string gridId, string key = "Id", bool nofocus = false)
{
gridId = html.Awe().GetContextPrefix() + gridId;

return DeleteFormatForGrid(gridId, key, nofocus);
}

public static string EditFormat(string popupName, string key = "Id", bool setId = false, bool nofocus = false)
{
var idattr = "";
if (setId)
{
idattr = $"id = 'gbtn{popupName}.{key}'";
}

var tabindex = nofocus ? "tabindex = \"-1\"" : string.Empty;

return string.Format("<button type=\"button\" class=\"awe-btn awe-nonselect editbtn\" {3} {2} onclick=\"awe.open('{0}', {{ params:{{ id: '.{1}' }} }}, event)\"><span class='ico-crud ico-edit'></span></button>",
popupName, key, idattr, tabindex);
}

public static string DeleteFormat(string popupName, string key = "Id", string deleteContent = null, string btnClass = null, bool nofocus = false)
{
if (deleteContent == null)
{
deleteContent = "<span class='ico-crud ico-del'></span>";
}

var tabindex = nofocus ? "tabindex = \"-1\"" : string.Empty;

return string.Format("<button type=\"button\" class=\"awe-btn awe-nonselect delbtn {3}\" {4} onclick=\"awe.open('{0}', {{ params:{{ id: '.{1}' }} }}, event)\">{2}</button>",
popupName, key, deleteContent, btnClass, tabindex);
}

public static string InlineEditFormat(bool nofocus = false)
{
var tabindex = nofocus ? "tabindex = \"-1\"" : string.Empty;
return string.Format("<button type=\"button\" class=\"awe-btn o-gledtb awe-nonselect o-glh o-pad\" {0} >Edit</button><button type=\"button\" class=\"awe-btn o-glsvb awe-nonselect o-gl o-pad\">Save</button>", tabindex);
}

public static string EditFormatForGrid(string gridId, string key = "Id", bool setId = false, bool nofocus = false)
{
return EditFormat("edit" + gridId, key, setId, nofocus);
}

public static string DeleteFormatForGrid(string gridId, string key = "Id", bool nofocus = false)
{
return DeleteFormat("delete" + gridId, key, null, null, nofocus);
}

public static string EditGridNestFormat()
{
return "<button type='button' class='awe-btn editnst'><span class='ico-crud ico-edit'></span></button>";
}

public static string DeleteGridNestFormat()
{
return "<button type='button' class='awe-btn delnst'><span class='ico-crud ico-del'></span></button>";
}

public static string AddChildFormat()
{
return "<button type='button' class='awe-btn awe-nonselect o-pad' onclick=\"awe.open('createNode', { params:{ parentId: '.Id' } })\">add child</button>";
}

public static string MoveBtn()
{
return "<button type=\"button\" class=\"awe-movebtn awe-btn\" tabindex=\"-1\"><i class=\"awe-icon\"></i></button>";
}
}
}



Comments