CRUD server controller
namespace: MvcControlsToolkit.Controllers
The ServerCrudController<VMD, VMS, D>
assists server controls in their
ajax edit/detail-edit/detail-show/add/add-detail/delete operations. Operations with the "detail prefix" takes place
in a detail modal, while all other operations takes place in-line, in the control itself.
ServerCrudController
operates both on server controls
based on IEnumerables of ViewModels of type VMS
and on single item
controls. VMD
is the ViewModel type to be used in all detail operations,
while VMS
isthe shorter in-line operations ViewModel. Finally, D
is the type of the principal key of both VMS
and VMD
.
All ajax operations are taken into account automatically by ServerCrudController
, so the developer typically inherits from it
and adds all other needed action methods, such as, for instance, the action method that populates the initial page with all controls.
All html is generated with the same templates used by the controls handled by the controller. For more details on how to associate
a controller to a control, and how a controller is able to select the right template for all controls it takes care of, please refer to
the documentation on RowType.
The inheriting control must have a constructor with some DI injected parameters that it will pass to the base controller constructor, and with a Repository that will be used by all ajax operations. This repository must be an implementation of the ICRUDRepository interface, such as, any subclass of the interface default implementation:
public class CGridsController : ServerCrudController<ProductViewModelDetail, ProductViewModel, int?> { public CGridsController(ProductRepository repository, IStringLocalizerFactory factory, IHttpContextAccessor accessor) : base(factory, accessor) { Repository = repository; }
As shown in the example above the repository once injected in the constructor must be saved in the
Repository
property inherited by the ServerCrudController
. IStringLocalizerFactory
is needed for various localization stuffs, while IHttpContextAccessor
might be needed to instantiate in-line templates.
Both ServerCrudController
and IHttpContextAccessor
are obligatory.
Another setting that might be provided is the name of VMD key property. If a property is named Id
as a default it is assumed to be the key,
if there is no Id
property or if you want to override this setting you must provide explicitely the key name by overriding:
public virtual string DeatailKeyName { get { return null; } }
There is no need to provide VMS
key since this information is automatically extracted by RowTypes
of the controls handled by the controller.
Below a complete controller for a View containing a paged immediate update grid:
public class ProductsController : ServerCrudController<SimpleProductViewModelDetail, SimpleProductViewModel, int?> { public ProductsController(ProductRepository repository, IStringLocalizerFactory factory, IHttpContextAccessor accessor) : base(factory, accessor) { Repository = repository; } public async Task<IActionResult> Index(int? page) { int pg = page.HasValue ? page.Value : 1; if (pg < 1) pg = 1; var model = new SimpleProductlistViewModel { Products = await Repository .GetPage<SimpleProductViewModel>( null, q => q.OrderBy(m => m.Name), pg, 5) }; return View(model); } }
As you can see, just the Index
method that displays a page of data has been added: all grid operations are automatically
handled by ServerCrudController
. For more examples see the Grid section
of the live examples.
While the operations shown in the examples above are all what is needed to have all controls associated to the controller work properly,
the behavior of the ServerCrudController
may be customized in several ways.
Controlling user permissions
No authorize attribute may be placed on all inherited ajax calls/handling action methods, so user authorization may be
handled either with an unique controller level Authorize attribute that allows/disallows all controller operations,
or, if one needs a more fine grained control, by overriding the function:
protected virtual Functionalities Permissions(IPrincipal user)
Default implementation always returns Functionalities.All
where Functionalities
ias a Flag
enumeration.
Customizing error messages
TheServerCrudController
may return one of the messages below to the client side (all controls take care of dispatching these messages properly):
- error 0: "wrong call", when the ajax call whose done with wrong parameters(which may happens only when it has been created by an human, instead of a control)
- error 1: "internal server error", an unspecified error took place during business processing
- error 2: "item not found, item to be shown in a detail window was not found
- error 3: "unauthorized", current logged user is not authorized to perform the operation. One should never have this error since UI elements needed to start the operation are not shown to unhauthorized users.
- error 4: "unable to delete item (maybe already deleted)", self-explanatory
- error 5: "unable to update item (maybe it has been deleted)", self-explanatory
- error 6: "operation aborted", this error takes place when the operation was aborted by a custom OnOperation...ing, event handling function (see below).
The above messages may be customized in two ways:
-
overriding
protected virtual string ErrorMessage(int i)
-
localizing the the default messages by simply creating a resource file associated to the type
ServerCrudController
. See Asp.net core Mvc localization for more infos.
Customizing detail modal
The easiest way to customize the modal detail is by furnishing a custom title, by overriding:
public virtual string DetailTitle { get { return "Item detail"; } }
Each detail modal is created by using default settings and default templates. If this is not acceptable one may provide the name of a partial View containig custom RowType definitions as in the example below:
<export-settings> <column asp-for="TypeId"> <external-key-remote display-property="TypeName" items-value-property="Value" items-display-property="Display" items-url="@(Url.Action("GetTypes", "DetailTest", new { search = "_zzz_" }))" dataset-name="product-types" url-token="_zzz_" max-results="20" /> </column> <row-type asp-for="@Model .SubInfo<ProductMaintenanceViewModelDetail>().Model" from-row="0"> <column asp-for="Price" detail-widths="new decimal[] {30, 15 }" /> <column asp-for="@((Model as ProductMaintenanceViewModelDetail) .MaintenanceYearlyRate)" /> </row-type> </export-settings>
The View must have a model of type VMD
.
The export-settings
TagHelper allows the content of the view be imported in a calling
template.
The name of the View containing all RoType definitions may be specified by overriding:
public virtual string DetailColumnAdjustView { get { return null; } }
Finally, one may provide a completely custom modal by overriding:
public virtual string DetailView { get { return "DefaultServerItemDetail"; } }
Advice: before trying a completely custom inplementation please read
about detail forms
Events
TheServerCrudController
behaviour may be customized also by "attaching" custom code to the various ajax operations.
This can be done thanks to ...ed and ...ing function called respectively after and before each single operation.
If a ...ing function returns CrudOperationSwitch.Abort
the operation is aborted.
Below the list of all such operations the inheriting controller may override:
public virtual async Task
OnOperationExecuted(CrudOperation op, VMS item)
{
return;
}
public virtual async Task
OnDetailOperationExecuted(CrudOperation op, VMD item)
{
return;
}
public virtual async Task OnDeleteExecuted(D key)
{
return;
}
public virtual async Task<CrudOperationSwitch>
OnOperationExecuting(CrudOperation op, VMS item)
{
return CrudOperationSwitch.Go;
}
public virtual async Task<CrudOperationSwitch>
OnDetailOperationExecuting(CrudOperation op, VMD item)
{
return CrudOperationSwitch.Go;
}
public virtual async Task<CrudOperationSwitch>
OnDeleteExecuting(D key)
{
return CrudOperationSwitch.Go;
}