Back to Devexpress

Edit Model in Blazor TreeList

blazor-405170-components-treelist-editing-and-validation-edit-model.md

latest15.3 KB
Original Source

Edit Model in Blazor TreeList

  • Oct 09, 2024
  • 9 minutes to read

Once a user starts editing a row, the TreeList creates a clone of the edited data item – an edit model. The edit model has the same data type and property values as the edited data item. This topic describes how to access, initialize, customize, and save the edit model. The approaches listed below apply to all edit modes.

Initialize New Rows

Follow the steps below to initialize an edit model for new data rows:

  1. Handle the CustomizeEditModel event.
  2. Check the event argument’s IsNew property to identify new rows.
  3. Use EditModel and ParentDataItem properties to link a new node to its parent.
  4. (Optional) Use the EditModel property to initialize model fields with predefined values.

The following snippet initializes an edit model for new tasks:

razor
<DxTreeList Data="TreeListData"
            KeyFieldName="Id"
            ParentKeyFieldName="ParentId"
            @* ... *@
            CustomizeEditModel="TreeList_CustomizeEditModel">
    <Columns>
        <DxTreeListCommandColumn />
        <DxTreeListDataColumn FieldName="Name" Caption="Task" />
        <DxTreeListDataColumn FieldName="EmployeeName" />
        <DxTreeListDataColumn FieldName="StartDate" />
        <DxTreeListDataColumn FieldName="DueDate" />
    </Columns>
</DxTreeList>

@code {
    List<EmployeeTask> TreeListData { get; set; }

    void TreeList_CustomizeEditModel(TreeListCustomizeEditModelEventArgs e) {
        if(e.IsNew) {
            var newTask = (EmployeeTask)e.EditModel;
            newTask.Id = TreeListData.Max(x => x.Id) + 1;
            newTask.Name = "New task";
            newTask.StartDate = DateTime.Today;
            newTask.DueDate = DateTime.Today.AddDays(7);
            if(e.ParentDataItem != null)
                newTask.ParentId = ((EmployeeTask)e.ParentDataItem).Id;
        }
    }
    // ...
}

Save Changes

During edit operations, the TreeList applies user changes only to the edit model and keeps the data item unchanged. Handle the events listed below to process changes:

EditModelSaving

This event fires if validation succeeds after a user saves changes or you call the SaveChangesAsync() method. Handle this event to check access permissions, assign changes from the edit model to the corresponding data item, and post changes to the underlying data source. The IsNew event argument identifies whether the edit model corresponds to a new or existing row.

Call the CopyChangesToDataItem() method to copy all changed fields including nested properties from the edit model to the data item. This method copies changes regardless of how you make changes: in the event handler, razor code, or business logic.

DataItemDeletingThis event fires when a user confirms the row delete operation in the confirmation dialog. Handle this event to check user access permissions and delete the corresponding data item from the data source. The DataItem and ParentDataItem event arguments allow you to access the processed data item and its parent.

TreeList data should be reloaded after you post updates to the data source. The TreeList component automatically reloads its data in response to the following actions:

In other cases, call the Reload() method to reload TreeList data.

In the following example, the TreeList component reloads data after an event handler saves changes:

razor
@inject EmployeeTaskService EmployeeTaskService

<DxTreeList Data="TreeListData"
            KeyFieldName="Id"
            ParentKeyFieldName="ParentId"
            EditModelSaving="TreeList_EditModelSaving"
            DataItemDeleting="TreeList_DataItemDeleting"
            CustomizeEditModel="TreeList_CustomizeEditModel">
    <Columns>
        <DxTreeListCommandColumn />
        <DxTreeListDataColumn FieldName="Name" Caption="Task" />
        <DxTreeListDataColumn FieldName="EmployeeName" />
        <DxTreeListDataColumn FieldName="StartDate" />
        <DxTreeListDataColumn FieldName="DueDate" />
    </Columns>
</DxTreeList>

@code {
    List<EmployeeTask> TreeListData { get; set; }

    protected override void OnInitialized() {
        TreeListData = EmployeeTaskService.GenerateData();
    }
    void TreeList_CustomizeEditModel(TreeListCustomizeEditModelEventArgs e) {
        if(e.IsNew) {
            var newTask = (EmployeeTask)e.EditModel;
            newTask.Id = TreeListData.Max(x => x.Id) + 1;
            newTask.Name = "New task";
            newTask.StartDate = DateTime.Today;
            newTask.DueDate = DateTime.Today.AddDays(7);
            if(e.ParentDataItem != null)
                newTask.ParentId = ((EmployeeTask)e.ParentDataItem).Id;
        }
    }
    async Task TreeList_EditModelSaving(TreeListEditModelSavingEventArgs e) {
        if(e.IsNew)
            TreeListData.Add((EmployeeTask)e.EditModel);
        else
            e.CopyChangesToDataItem();
    }
    async Task TreeList_DataItemDeleting(TreeListDataItemDeletingEventArgs e) {
        TreeListData.Remove((EmployeeTask)e.DataItem);
    }
}
csharp
public class EmployeeTask {
    public int Id { get; set; }
    public int ParentId { get; set; }
    public string Name { get; set; }
    public string EmployeeName { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime DueDate { get; set; }
    public int Progress { get; set; }
    public EmployeeTask() { }
    public EmployeeTask(
        int id,
        int parentId,
        string name,
        string employeeName,
        DateTime startDate,
        DateTime dueDate,
        int progress
        )
    {
        Id = id;
        ParentId = parentId;
        Name = name;
        EmployeeName = employeeName;
        StartDate = startDate;
        DueDate = dueDate;
        Progress = progress;
    }
}
csharp
public class EmployeeTaskService {
    public List<EmployeeTask> GenerateData() {
        return new List<EmployeeTask>() {
            new EmployeeTask(1, 0, "Simplify & Clarify Product Messaging", "John Heart", new DateTime(2018, 4, 3), new DateTime(2018, 4, 14), 14),
            new EmployeeTask(2, 1, "Prepare Financial Reports", "Samantha Bright", new DateTime(2018, 4, 3), new DateTime(2018, 4, 7), 17),
            new EmployeeTask(3, 1, "Prepare Marketing Plan", "Arthur Miller", new DateTime(2018, 4, 7), new DateTime(2018, 4, 14), 11),
            new EmployeeTask(4, 0, "Create Action Plan to Improve Customer Engagement", "Robert Reagan", new DateTime(2017, 8, 8), new DateTime(2018, 4, 8), 23),
            new EmployeeTask(5, 4, "Update Personnel Files", "Greta Sims", new DateTime(2017, 8, 8), new DateTime(2017, 10, 18), 100),
            new EmployeeTask(6, 4, "Review Health Insurance Options", "Brett Wade", new DateTime(2017, 9, 27), new DateTime(2017, 11, 10), 37),
            new EmployeeTask(7, 4, "Choose Between PPO and HMO Health Plan", "Sandra Johnson", new DateTime(2017, 12, 13), new DateTime(2018, 3, 23), 17),
            new EmployeeTask(8, 4, "Update Google Adwords Strategy", "Ed Holmes", new DateTime(2017, 8, 23), new DateTime(2017, 12, 23), 45),
            new EmployeeTask(9, 4, "Create New Brochure Design", "Barb Banks", new DateTime(2018, 1, 3), new DateTime(2018, 3, 14), 17),
            new EmployeeTask(10, 4, "Obtain Price Quote for New Brochure", "Kevin Carter", new DateTime(2018, 2, 1), new DateTime(2018, 3, 15), 18),
            new EmployeeTask(11, 4, "Brochure Design Review", "Cindy Stanwick", new DateTime(2017, 8, 22), new DateTime(2017, 10, 28), 4),
            new EmployeeTask(12, 4, "Review Website Redesign Strategy", "Sammy Hill", new DateTime(2017, 9, 16), new DateTime(2018, 3, 6), 73),
            new EmployeeTask(13, 4, "Roll Out New Website", "Davey Jones", new DateTime(2017, 11, 7), new DateTime(2018, 2, 6), 9),
            new EmployeeTask(14, 4, "Update Sales/Marketing Strategy", "Victor Norris", new DateTime(2017, 12, 13), new DateTime(2018, 4, 2), 5),
            new EmployeeTask(15, 4, "Update Sales/Revenue Report", "Mary Stern", new DateTime(2017, 12, 25), new DateTime(2018, 4, 2), 4),
            new EmployeeTask(16, 4, "Direct vs Online Sales Comparison Report", "Robin Cosworth", new DateTime(2018, 1, 2), new DateTime(2018, 3, 20), 14),
            new EmployeeTask(17, 4, "Review Sales Report and Approve Modifications", "Kelly Rodriguez", new DateTime(2017, 9, 4), new DateTime(2017, 10, 30), 8),
            new EmployeeTask(18, 4, "Update R&D Strategy", "James Anderson", new DateTime(2017, 11, 13), new DateTime(2017, 12, 4), 12),
            new EmployeeTask(19, 4, "Discuss Updated R&D Strategy", "Antony Remmen", new DateTime(2017, 10, 29), new DateTime(2017, 12, 31), 14),
            new EmployeeTask(20, 4, "Update QA Strategy", "Olivia Peyton", new DateTime(2017, 10, 31), new DateTime(2017, 11, 2), 18),
            new EmployeeTask(21, 4, "Schedule Training Events", "Taylor Riley", new DateTime(2017, 11, 19), new DateTime(2018, 4, 7), 21),
            new EmployeeTask(22, 4, "Approve Hiring of John Jeffers", "Amelia Harper", new DateTime(2018, 1, 7), new DateTime(2018, 4, 8), 10),
            new EmployeeTask(23, 0, "Increase Average Subscription Price", "Wally Hobbs", new DateTime(2017, 8, 9), new DateTime(2017, 9, 13), 96),
            new EmployeeTask(24, 23, "Update Non-Compete Agreements", "Brad Jameson", new DateTime(2017, 8, 9), new DateTime(2017, 9, 3), 96),
            new EmployeeTask(25, 23, "Update Employee Records with New NDA", "Karen Goodson", new DateTime(2017, 8, 23), new DateTime(2018, 9, 10), 100)
        };
    }
}
csharp
// ...
builder.Services.AddSingleton<EmployeeTaskService>();

Create a Custom Edit Model

The TreeList component cannot generate an edit model in the following cases:

  • A data item class does not have a parameterless constructor.
  • Data item fields bound to TreeList columns are read-only.
  • In a hierarchical data source, a data type of children nodes differs from the parent node’s data type. In this case, the TreeList cannot determine which data type to use for new rows and generates edit models only for existing rows.

You can create a custom edit model or customize an automatically generated edit model as follows:

  1. Handle the CustomizeEditModel event.
  2. Use the DataItem event argument to access the proccessed data item. Note that the data item is null for new rows.
  3. Assign your custom edit model to the EditModel event argument.

When you obtain an EditModel object in event handlers or edit templates, cast this object to your custom edit model class as shown below:

razor
<DxTreeList Data="TreeListData"
            KeyFieldName="Id"
            ParentKeyFieldName="ParentId"
            EditMode="TreeListEditMode.EditForm"
            EditModelSaving="TreeList_EditModelSaving"
            DataItemDeleting="TreeList_DataItemDeleting"
            CustomizeEditModel="TreeList_CustomizeEditModel">
    <Columns>
        <DxTreeListCommandColumn />
        <DxTreeListDataColumn FieldName="Name" Caption="Task" />
        <DxTreeListDataColumn FieldName="EmployeeName" />
        <DxTreeListDataColumn FieldName="StartDate" />
        <DxTreeListDataColumn FieldName="DueDate" />
    </Columns>
    <EditFormTemplate Context="editFormContext">
        @{ 
            var editModel = (EmployeeTaskEditModel)editFormContext.EditModel; 
        }
        @* ... *@
    </EditFormTemplate>
</DxTreeList>

@code {
    List<EmployeeTask> TreeListData { get; set; }

    class EmployeeTaskEditModel {
        public int Id { get; set; }
        public int ParentId { get; set; }
        [Required]
        public string Name { get; set; }
        public string EmployeeName { get; set; }
        public DateTime StartDate { get; set; }
        [Required]
        [Range(typeof(DateTime), "1/12/2016", "1/12/2025", ErrorMessage = "DueDate must be between {1:d} and {2:d}")]
        public DateTime DueDate { get; set; }
    }
    async Task TreeList_EditModelSaving(TreeListEditModelSavingEventArgs e) {
        var editModel = (EmployeeTaskEditModel)e.EditModel;
    }
    void TreeList_CustomizeEditModel(TreeListCustomizeEditModelEventArgs e) {
        if(e.IsNew) {
            var newTask = new EmployeeTaskEditModel { };
            newTask.Id = TreeListData.Max(x => x.Id) + 1;
            newTask.Name = "New task";
            newTask.StartDate = DateTime.Today;
            newTask.DueDate = DateTime.Today.AddDays(7);
            e.EditModel = newTask;
        }
        else {
            var dataItem = (EmployeeTask)e.DataItem;
            e.EditModel = new EmployeeTaskEditModel {
                Id = dataItem.Id,
                ParentId = dataItem.ParentId,
                Name = dataItem.Name,
                EmployeeName = dataItem.EmployeeName,
                StartDate = dataItem.StartDate,
                DueDate = dataItem.DueDate
            };
        }
    }
    // ...
}

Edit Data with Nested Properties

When you edit data, the TreeList clones a bound data item – namely creates an object of the same type and copies all property values into it. If a property is of a reference type, the reference is copied but the referred object is not copied. As the result, the original object and its clone refer to the same object.

To edit nested objects, create a copy of these objects manually (make a deep copy of the original data item object). You can use the following approaches to implement a deep copy operation:

  • Handle the CustomizeEditModel event, clone the nested object, and assign it to field whose value is the reference type.

  • Create constructors for the data item type and the nested object’s type. In the data item type constructor, the nested object type constructor clones the nested object with its property values. Handle the CustomizeEditModel event and call a data item’s class constructor to initialize the edit model.