1 Star 0 Fork 0

QianXun-Studio / Grid.Blazor

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
Crud.md 18.24 KB
一键复制 编辑 原始数据 按行查看 历史

Blazor client-side with OData back-end

CRUD

Index

GridBlazor supports CRUD forms to add, edit, view and delete items for Blazor client-side projects.

These are the supported features:

  • Full screen forms
  • Auto-generated forms with field type detection based on column definition
  • Lists for drop-drown fields
  • Custom forms
  • Support of grid models including 1:N relationships
  • Support of entities with multiple foreign keys
  • Direct URLs

Auto-generated forms

You can enable CRUD using the ODataCrud(bool enabled) method of the GridODataClient object:

    var client = new GridODataClient<Order>(HttpClient, url, query, false, "ordersGrid", 
        c => columns(c, NavigationManager.BaseUri), 10, locale, new List<string> { "Employee", "Shipper" })
            .ODataCrud(true)

You can also enable CRUD depending on a condition for each row using the ODataCrud(bool createEnabled, Func<T, bool> enabled) method:

    var client = new GridODataClient<Order>(HttpClient, url, query, false, "ordersGrid", 
        c => columns(c, NavigationManager.BaseUri), 10, locale, new List<string> { "Employee", "Shipper" })
            .ODataCrud(true, r => r.Customer.IsVip)

The create form can only be enabled using a bool parameter. But the read, update and delete forms can be enabled using a function that returns a bool.

Note: All 4 crud forms can be enabled at the same time with the former methods, but you can enable one by one using ODataCrud(bool create, bool read, bool update, bool delete) or ODataCrud(bool createEnabled, Func<T, bool> readEnabled, Func<T, bool> updateEnabled, Func<T, bool> deleteEnabled) methods.

You must create actions in the ODataController for all 4 CRUD operations. This is an example of controller including those 4 actions:

    public class OrdersController : ODataController
    {
        ...
        [EnableQuery]
        public async Task<IActionResult> Get(int key)
        {
            var repository = new OrdersRepository(_context);
            Order order = await repository.GetById(key);
            return Ok(order);
        }

        public async Task<IActionResult> Post([FromBody]Order order)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }         

            if (order == null)
            {
                return BadRequest();
            }

            var repository = new OrdersRepository(_context);
            try
            {
                await repository.Insert(order);
                repository.Save();

                return Created(order);
            }
            catch (Exception e)
            {
                return BadRequest(new
                {
                    message = e.Message.Replace('{', '(').Replace('}', ')')
                });
            }
        }

        public async Task<IActionResult> Put([FromODataUri]int key, [FromBody] Order order)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            if (key != order.OrderID)
            {
                return BadRequest();
            }

            var repository = new OrdersRepository(_context);
            await repository.Update(order);
            try
            {              
                repository.Save();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!OrderExists(key))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
            return Updated(order);
        }

        public async Task<ActionResult> Delete([FromODataUri] int key)
        {
            var repository = new OrdersRepository(_context);
            Order order = await repository.GetById(key);
            if (order == null)
            {
                return NotFound();
            }
            repository.Delete(order);
            repository.Save();
            return StatusCode((int)HttpStatusCode.NoContent);
        }

        private bool OrderExists(int key)
        {
            var repository = new OrdersRepository(_context);
            return repository.GetAll().Any(x => x.OrderID == key);
        }
        ...
    }

Column definition

The column definition must include the primary keys:

  • using the SetPrimaryKey(true) method for columns with auto-generated keys, or
  • using the SetPrimaryKey(true, false) method for columns with manually generated keys, or
  • using the SetPrimaryKey(true).SetSelectField(...) method for columns with keys selected from a list

If the grid model includes foreign keys, the column definition should include them using the SetSelectField in order to get the options for the <select> element.

The SetSelectField method has 3 required parameters:

Parameter Description
enabled boolean to configure if the field is shown as a <select> html element
expression function to get the selected value for update and delete forms (it must return an string value)
selectItemExprAsync async function to get the values and titles to be shown in the drop-down of create and update forms (it must return an Task<IEnumerable<SelectItem>>)

The type of fields currently supported as foreign keys are:

  • string
  • DateTime
  • DateTimeOffset
  • TimeSpan
  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64
  • Byte
  • Single
  • Double
  • Decimal
  • bool
  • Guid

This is an example of function to get values and title for a drop-down:

    Func<string, Task<IEnumerable<SelectItem>>> GetAllEmployees = async (baseUri) =>
    {
        string url = baseUri + $"odata/Employees?$select=EmployeeID,FirstName,LastName";
        ODataDTO<Employee> response = await new HttpClient().GetFromJsonAsync<ODataDTO<Employee>>(url);
        if (response == null || response.Value == null)
        {
            return new List<SelectItem>();
        }
        else
        {
            return response.Value
                .Select(r => new SelectItem(r.EmployeeID.ToString(), r.EmployeeID.ToString() + " - " + r.FirstName + " " + r.LastName))
                .ToList();
        }
    };

Other fields that you want to be shown as dropdowns with a closed list can also be configured with the SetSelectField method.

All fields to be included in the CRUD forms but not in the grid as columns should be configured as hidden (e.g. Add(o => o.RequiredDate, true)).

All columns required to be included in the Update form as read only should be configured using the SetReadOnlyOnUpdate(true) method.

If a column is a date that has to be shown as date, time, week, month or datetime-local in the CRUD forms, the column definition should use the SetInputType method in order to get the correct format.

If a column is a string that has to be shown as <textarea> in the CRUD forms, the column definition should use the SetInputType method in order to get the correct html element.

The SetInputType method has 1 required parameter:

Parameter Description
inputType InputType enum. Its value can be InputType.TextArea, InputType.Date, InputType.Time, InputType.Month, InputType.Week or InputType.DateTimeLocal

You can also add components on the CRUD forms using the RenderCrudComponentAs<TComponent> method. You must define these columns as Hidden to show them just on CRUD forms.

And finally all columns included in the grid but not in the CRUD forms should be configured as "CRUD hidden" using the SetCrudHidden(true) method.

Notes:

  • You can have more granularity in the "CRUD hidden" configuration. You can use the SetCrudHidden(bool create, bool read, bool update, bool delete) method to configure the columns that will be hidden on each type of form.
  • You can have more granularity in the components configuration. You can use the RenderCrudComponentAs<TCreateComponent, TReadComponent, TUpdateComponent, TDeleteComponent> method to configure the components that will be shown on each type of form. Id you don't want to show any component for a specific type of form you must use NullComponent

This is an example of column definition:

    Action<IGridColumnCollection<Order>, string> columns = (c, baseUri) =>
    {
        c.Add(o => o.OrderID).SetPrimaryKey(true).Titled(SharedResource.Number).SetWidth(100);
        c.Add(o => o.CustomerID, true).SetSelectField(true, o => o.Customer.CustomerID + " - " + o.Customer.CompanyName, 
            () => GetAllCustomers(baseUri));
        c.Add(o => o.EmployeeID, true).SetSelectField(true, o => o.Employee.EmployeeID.ToString() + " - " + o.Employee.FirstName 
            + " " + o.Employee.LastName, () => GetAllEmployees(baseUri));
        c.Add(o => o.ShipVia, true).SetSelectField(true, o => o.Shipper == null ? "" : o.Shipper.ShipperID.ToString() + " - " 
            + o.Shipper.CompanyName, () => GetAllShippers(baseUri));
        c.Add(o => o.OrderDate, "OrderCustomDate").Titled(SharedResource.OrderCustomDate).Format("{0:yyyy-MM-dd}").SetWidth(120);
        c.Add(o => o.Customer.CompanyName).Titled(SharedResource.CompanyName).SetWidth(250).SetCrudHidden(true).SetReadOnlyOnUpdate(true);
        c.Add(o => o.Customer.ContactName).Titled(SharedResource.ContactName).SetWidth(250).SetCrudHidden(true);
        c.Add(o => o.Freight).Titled(SharedResource.Freight).Format("{0:F}");
        c.Add(o => o.Customer.IsVip).Titled(SharedResource.IsVip).SetWidth(70).Css("hidden-xs")
            .RenderValueAs(o => o.Customer.IsVip ? Strings.BoolTrueLabel : Strings.BoolFalseLabel).SetCrudHidden(true);
        c.Add(o => o.RequiredDate, true).Format("{0:yyyy-MM-dd}");
        c.Add(o => o.ShippedDate, true).Format("{0:yyyy-MM-dd}");
        c.Add(o => o.ShipName, true);
        c.Add(o => o.ShipAddress, true);
        c.Add(o => o.ShipCity, true);
        c.Add(o => o.ShipPostalCode, true);
        c.Add(o => o.ShipRegion, true);
        c.Add(o => o.ShipCountry, true);
        c.Add(true).Titled("Images").RenderCrudComponentAs<Carousel, Carousel, NullComponent, NullComponent>();
    };

This is an example of a grid using CRUD:

And this is an auto-genereated edit form:

Custom forms (Optional)

If you want to use custom forms you can enable them using the SetCreateComponent, SetReadComponent, SetUpdateComponent and SetDeleteComponent methods of the GridODataClient object:

    var client = new GridODataClient<Order>(HttpClient, url, query, false, "ordersGrid", 
        c => columns(c, NavigationManager.BaseUri), 10, locale, new List<string> { "Employee", "Shipper" })
            .ODataCrud(true)
            .SetCreateComponent<OrderCreateComponent>()
            .SetReadComponent<OrderReadComponent>()
            .SetUpdateComponent<OrderUpdateComponent>()
            .SetDeleteComponent<OrderDeleteComponent>();

You can define all custom forms or just some of them. If you don't define a custom form for one of the enabled operations an auto-generated form will be used instead.

And finally you will have to create a Blazor component for the custom form. This is an example of edit form:

@using GridBlazor.Resources
@using GridBlazorOData.Shared.Models
@inherits GridUpdateComponentBase<Order>

<h1>@Strings.Add Order</h1>
<EditForm Model="@Item" OnValidSubmit="@UpdateItem">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div class="form-horizontal">
        <div class="form-group">
            <label for="OrderID" class="control-label col-md-2">OrderID: </label>
            <div class="col-md-5">
                <InputNumber id="OrderID" class="form-control" readonly="readonly" @bind-Value="Item.OrderID" />
            </div>
        </div>

        <div class="form-group">
            <label for="CustomerID" class="control-label col-md-2">Customer Id: </label>
            <div class="col-md-5">
                <InputText id="CustomerID" class="form-control" @bind-Value="Item.CustomerID" />
            </div>
        </div>

        <div class="form-group">
            <label for="EmployeeID" class="control-label col-md-2">Employee Id: </label>
            <div class="col-md-5">
                <InputNumber id="EmployeeID" class="form-control" @bind-Value="Item.EmployeeID" />
            </div>
        </div>

        <div class="form-group">
            <label for="ShipVia" class="control-label col-md-2">Ship Via: </label>
            <div class="col-md-5">
                <InputNumber id="ShipVia" class="form-control" @bind-Value="Item.ShipVia" />
            </div>
        </div>

        <div class="form-group">
            <label for="RequiredDate" class="control-label col-md-2">Required Date: </label>
            <div class="col-md-5">
                <InputDate id="RequiredDate" class="form-control" @bind-Value="Item.RequiredDate" />
            </div>
        </div>

        <div class="form-group">
            <label for="ShippedDate" class="control-label col-md-2">Shipped Date: </label>
            <div class="col-md-5">
                <InputDate id="ShippedDate" class="form-control" @bind-Value="Item.ShippedDate" />
            </div>
        </div>

        <div class="form-group">
            <label for="ShipName" class="control-label col-md-2">Ship Name: </label>
            <div class="col-md-5">
                <InputText id="ShipName" class="form-control" @bind-Value="Item.ShipName" />
            </div>
        </div>

        <div class="form-group">
            <label for="ShipAddress" class="control-label col-md-2">Ship Address: </label>
            <div class="col-md-5">
                <InputText id="ShipAddress" class="form-control" @bind-Value="Item.ShipAddress" />
            </div>
        </div>

        <div class="form-group">
            <label for="ShipCity" class="control-label col-md-2">Ship City: </label>
            <div class="col-md-5">
                <InputText id="ShipCity" class="form-control" @bind-Value="Item.ShipCity" />
            </div>
        </div>

        <div class="form-group">
            <label for="ShipPostalCode" class="control-label col-md-2">Ship Postal Code: </label>
            <div class="col-md-5">
                <InputText id="ShipPostalCode" class="form-control" @bind-Value="Item.ShipPostalCode" />
            </div>
        </div>

        <div class="form-group">
            <label for="ShipRegion" class="control-label col-md-2">Ship Region: </label>
            <div class="col-md-5">
                <InputText id="ShipRegion" class="form-control" @bind-Value="Item.ShipRegion" />
            </div>
        </div>

        <div class="form-group">
            <label for="ShipCountry" class="control-label col-md-2">Ship Country: </label>
            <div class="col-md-5">
                <InputText id="ShipCountry" class="form-control" @bind-Value="Item.ShipCountry" />
            </div>
        </div>

        <div class="form-group">
            <label for="Freight" class="control-label col-md-2">Freight: </label>
            <div class="col-md-5">
                <input id="Freight" name="Freight" class="form-control" @bind="Item.Freight" />
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-5">
                <button type="submit" class="btn btn-primary btn-md">@Strings.Save</button>
                <button type="button" class="btn btn-primary btn-md" @onclick="BackButtonClicked">@Strings.Back</button>
            </div>
        </div>
    </div>
</EditForm>

Note: The Blazor component must be to inherited from the GridUpdateComponentBase class.

If you want to use a drop-down list for a field you have to define it as it was for auto-generated forms.

Direct URLs

You can configure a direct route to an specific CRUD form. The first step is to roure alternative routes as follows:

@page "/crud"
@page "/crud/{OrderId}/{Mode}"

Then you have to create and initialize the following parameters:

  • GridMode for the type of CRUD form
  • object[] for the primary keys of the row to be shown on the form

This is an example of these parameters initialization:

@code
{
    ...

    private object[] _keys;
    private GridMode _mode;
    
    ...

    protected override async Task OnParametersSetAsync()
    {
        var locale = CultureInfo.CurrentCulture;

        var query = new QueryDictionary<StringValues>();
        string url = NavigationManager.BaseUri + "odata/Orders";

        var client = new GridODataClient<Order>(HttpClient, url, query, false, "ordersGrid", c =>
            ColumnCollections.OrderColumnsWithCrud(c, NavigationManager.BaseUri), 10, locale, 
            new List<string> { "Employee", "Shipper" })
            .Sortable()
            .Filterable()
            .SetStriped(true)
            .ODataCrud(true)
            .WithMultipleFilters()
            .WithGridItemsCount();

        _grid = client.Grid;

        if (!string.IsNullOrWhiteSpace(OrderId))
        {
            int orderId;
            bool result = int.TryParse(OrderId, out orderId);
            if (result)
            {
                if (Mode.ToLower() == "create")
                {
                    _keys = new object[] { orderId };
                    _mode = GridMode.Create;
                }
                else if (Mode.ToLower() == "read")
                {
                    _keys = new object[] { orderId };
                    _mode = GridMode.Read;
                }
                else if (Mode.ToLower() == "update")
                {
                    _keys = new object[] { orderId };
                    _mode = GridMode.Update;
                }
                else if (Mode.ToLower() == "delete")
                {
                    _keys = new object[] { orderId };
                    _mode = GridMode.Delete;
                }
            }
        }

        // Set new items to grid
        _task = client.UpdateGrid();
        await _task;
    }

    ...

And finaly you have to pass the paramenters initialized before to the GridComponent

<GridComponent T="Order" Grid="@_grid" Mode="_mode" Keys="_keys"></GridComponent>

<- Front-end back-end API | Nested CRUD ->

1
https://gitee.com/fan0217/Grid.Blazor.git
git@gitee.com:fan0217/Grid.Blazor.git
fan0217
Grid.Blazor
Grid.Blazor
master

搜索帮助