Home > Net >  Update List<model> items if there are any changes in UI
Update List<model> items if there are any changes in UI

Time:12-28

This is a bit hard to explain without showing the full concept, but I will try. I have table of Employees in Record.razor.cs - this.PersonellData. I am adding new Employee into table by this method:

private void AddNew()
{
  this.PersonellData.Add(new Personell
  {
    Id = i   1,
    Name = "John "   i,
    From = DateTime.Today   new TimeSpan(8, 30, 0),
    Category = this.selectedCategoryValue,
  });

  this.DiaryRecord.Employees = new List<Personell>(this.PersonellData);

  i  ;
}

However after adding employee I would like to change category and (From) time in UI. I am able to do it in UI, but data is not changing in List<model> itself. I set breakpoint at

private async Task OnCreateNewDiaryRecord()
{
  if (true)
  {
    this.DiaryRecordModel = new DiaryRecord
    {
      Id = this.DiaryRecordModel.Id,
      Title = this.DiaryRecordModel.Title,
      Description = this.DiaryRecordModel.Description,
      Employees = this.DiaryRecordModel.Employees,
      Date = this.SelectedDate,
    };

    await OnInitializedAsync();
  }
  isDialogVisible = false;
}

in Index.razor.cs there are no changes I have done in UI to each employee in the list. Any suggestions how to get it working? Should I add methods like OnChange for each UI item, but how to proceed forward with updating actual list?

Here is DiaryRecord.cs:

  public class DiaryRecord
  {
    public Guid Id { get; set; }
    public string Title { get; set; }

    public ICollection<Personell> Employees { get; set; }
  }

Here is UI part of list items

    <Table DataSource="this.PersonellData" RowClassName="@(_=>"editable-row")" Bordered [email protected]>
      <Column Title="From" TData="DateTime">
        <TimePicker TValue="DateTime?" @bind-Value="@context.From" OnChange="this.OnDateValueChanged" />
      </Column>
      <Column Title="Category" DataIndex="@nameof(context.Category)" TData="string">
        <Select DataSource="@this.CategoryList"
                @bind-Value="@this.selectedCategoryValue"
                LabelName="@nameof(Category.CategorySymbol)"
                ValueName="@nameof(Category.CategorySymbol)"
                DefaultActiveFirstItem="false"
                EnableSearch
                OnSelectedItemChanged="this.OnSelectedCategoryChangedHandler">
        </Select>
      </Column>
    </Table>

I have tried with OnChange="this.OnDateValueChanged", but don't know how to proceed forward.

CodePudding user response:

Don't call OnInitializedAsync() from your own code. And when you do it will not trigger a UI update.

//await OnInitializedAsync();
await InvokeAsync(StateHasChanged);  // Invoke probably not needed. Just in case.

CodePudding user response:

I've seen your project. There are plenty of errors of code and design. I'm going to display below the code that somehow works. Copy and test it.

Index.razor

@page "/"

@using AntDesign

<h1>Hello, world!</h1>

Welcome to your new app.

<br />
<br />

<Space>
  <SpaceItem>
    <Button Type="button" OnClick="@(()=>{ this.isDialogVisible = true; })">
      Add Record
    </Button>
  </SpaceItem>
</Space>
<Space>
  <Button Type="button" OnClick="Success">
    Success
  </Button>
</Space>

<Record DiaryRecord="DiaryRecordModel"></Record> 

<Modal Title="Create new record"
       Style="width:50%"
       Visible="@this.isDialogVisible"
       Footer="null"
       Draggable="@(true)"
       OnCancel="(e)=>{this.isDialogVisible = false;}">
  <Form Model="this.DiaryRecordModel"
        OnFinish="(e) => this.OnCreateNewDiaryRecord()">
    <FormItem>
      <Record DiaryRecord=this.DiaryRecordModel />
    </FormItem>
    <FormItem WrapperColOffset="8" WrapperColSpan="16">
      <Button Type="@ButtonType.Primary" HtmlType="submit">
        Submit
      </Button>
      <Button OnClick="(e)=>{this.isDialogVisible = false;}">Cancel</Button>
    </FormItem>
  </Form>
</Modal>

Index.razor.cs

using AntDesign;
using BlazorAppAntDemo.Models;
using Microsoft.AspNetCore.Components;
using System;
using System.Threading.Tasks;

namespace BlazorAppAntDemo.Pages
{
  public partial class Index
  {
    [Inject]
    public MessageService Message { get; set; }

    private DiaryRecord DiaryRecordModel { get; set; } = new DiaryRecord();
    private bool isDialogVisible;
    private DateTime SelectedDate { get; set; }

    public Index()
    {
      this.DiaryRecordModel = new DiaryRecord();
      this.DiaryRecordModel.Date = DateTime.Now;
    }

    protected override void OnInitialized()
    {

    }

    private async Task OnCreateNewDiaryRecord()
    {
      if (true)
      {
        this.DiaryRecordModel = new DiaryRecord
        {
          Id = this.DiaryRecordModel.Id,
          Title = this.DiaryRecordModel.Title,
          Description = this.DiaryRecordModel.Description,
          Employees = this.DiaryRecordModel.Employees,
          Date = this.SelectedDate,
        };

                //  await OnInitializedAsync();
                await Task.Yield();
      }
      isDialogVisible = false;
    }

    private async Task Success()
    {
      await this.Message.Success("This is a success message");
    }
  }
}

record.razor

@page "/record"

@using BlazorAppAntDemo.Models

<Template>
  <Collapse DefaultActiveKey="@(new[] { "1" })"
            OnChange="this.Callback"
            ExpandIconPosition="@this.ExpandIconPosition"
            ExpandIcon="caret-right">
    <Panel Header="Basic information" Key="1">
        <Form Model="DiaryRecord">
            <FormItem Label="Record title" NoStyle>
                <Input @bind-Value="@this.DiaryRecord.Title" />
            </FormItem>
            <FormItem Label="Description" NoStyle>
                <Input @bind-Value="@this.DiaryRecord.Description" />
            </FormItem>
            <FormItem Label="Date" NoStyle>
                <DatePicker @bind-Value="@this.DiaryRecord.Date" ShowTime="@true" OnChange="this.OnDateSelected" />
            </FormItem>
        </Form>
    </Panel>
    <Panel Header="Personell" Key="2">
      <ChildContent>
        <Button OnClick="this.AddNew" Type="button" Style="margin-bottom:16px">
          Add person
        </Button>
        <Table DataSource="this.PersonellData" RowClassName="@(_=>"editable-row")" Bordered [email protected]>
          <Column Title="Id" DataIndex="@nameof(context.Id)" TData="int"></Column>
          <Column Title="Name" DataIndex="@nameof(context.Name)" TData="string"></Column>
          <Column Title="From" TData="DateTime">
            <TimePicker TValue="DateTime?" @bind-Value="@context.From" OnChange="this.OnDateValueChanged" />
          </Column>
          <Column Title="Category" DataIndex="@nameof(context.Category)" TData="string">
            <Select DataSource="@this.CategoryList"
                    @bind-Value="@this.selectedCategoryValue"
                    LabelName="@nameof(Category.CategorySymbol)"
                    ValueName="@nameof(Category.CategorySymbol)"
                    DefaultActiveFirstItem="false"
                    EnableSearch
                    OnSelectedItemChanged="this.OnSelectedCategoryChangedHandler">
            </Select>
          </Column>
          <ActionColumn Title="Action">
            <Popconfirm Title="Sure to delete?"
                        OnConfirm="()=> this.Delete(context.Id)"
                        OkText="Yes"
                        CancelText="No">
              <Button Danger>Delete</Button>
            </Popconfirm>
          </ActionColumn>
        </Table>
      </ChildContent>
    </Panel>
    <Panel Header="Attachments" Key="3">
      <ChildContent>


        <span >
          <label  for="upload">
            <div >
              <div tabindex="0"  style="position:relative;">
                <InputFile OnChange="@this.OnFileSelection" multiple id="upload" hidden accept=".png,.jpg,.jpeg,.gif" />
                <div >
                  @if (this.ImgUrls.Any())
                  {
                    @foreach (var url in this.ImgUrls)
                    {
                      <div>
                        <img src="@url" alt="avatar" style="width: 100%" />
                      </div>
                    }
                  }
                  else
                  {
                    <div>
                      <Icon Spin="this.loading" Type="@(this.loading?"Loading":"plus")"></Icon>
                      <div className="ant-upload-text">Upload</div>
                    </div>
                  }
                </div>
              </div>
            </div>
          </label>
        </span>

      </ChildContent>
    </Panel>
    <Panel Header="Another one 1" Key="4">
      <ChildContent>
        <h3>Hola</h3>
      </ChildContent>
    </Panel>
    <Panel Header="Another one 2" Key="5">
      <ChildContent>
        <h3>Hola 2</h3>
      </ChildContent>
    </Panel>
    <Panel Header="Another one 3" Key="6">
      <ChildContent>
        <h3>Hola 3</h3>
      </ChildContent>
    </Panel>
  </Collapse>
</Template>

@code {

}

record.razor.cs

using AntDesign;
using BlazorAppAntDemo.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BlazorAppAntDemo.Pages
{
  public partial class Record
  {
    [Inject] private MessageService Message { get; set; }
    [Inject] private NotificationService Notice { get; set; }

    private string ExpandIconPosition { get; set; }
    private int i = 1;
    private List<Category> CategoryList { get; set; } 
    private string selectedCategoryValue;
    private IQueryable<Personell> PersonellData { get; set; }
    private List<string> ImgUrls { get; set; }
    private List<FileData> FileData { get; set; }

    private string imageUrl;
    private int maxFileSize;
    private byte[] profilePictureByteArray;
    private bool loading;

    [Parameter] public DiaryRecord DiaryRecord { get; set; }

    public Record()
    {
      this.ExpandIconPosition = "left";
      this.PersonellData = new List<Personell>().AsQueryable();
      this.CategoryList = new List<Category>();
      this.ImgUrls = new List<string>();
      this.FileData = new List<FileData>();
      this.maxFileSize = 400000;
    }


    protected override void OnInitialized()
    {
      this.CategoryList.Add(new Category { CategoryName = "CategoryA", CategorySymbol = "A" });
      this.CategoryList.Add(new Category { CategoryName = "CategoryB", CategorySymbol = "B" });
      this.CategoryList.Add(new Category { CategoryName = "CategoryC", CategorySymbol = "C" });
      this.CategoryList.Add(new Category { CategoryName = "CategoryD", CategorySymbol = "D" });
    }

    private void AddNew()
    {
      this.PersonellData.Append(new Personell
      {
        Id = i   1,
        Name = "John "   i,
        From = DateTime.Today   new TimeSpan(7, 30, 0),
        Category = this.selectedCategoryValue,
      });

      this.DiaryRecord.Employees = new List<Personell>(this.PersonellData);

      i  ;
    }

    private void Delete(int id)
    {
        this.PersonellData = this.PersonellData.Where(d => d.Id != id);
    }

    private void OnSelectedCategoryChangedHandler(Category value)
    {
      this.selectedCategoryValue = value.CategoryName;
    }

    private void OnDateValueChanged()
    {

    }

    private void OnDateSelected(DateTimeChangedEventArgs args)
    {
      this.DiaryRecord.Date = args.Date;
    }

    private void Callback(string[] keys)
    {

    }

    private async Task OnFileSelection(InputFileChangeEventArgs e)
    {
      foreach (IBrowserFile file in e.GetMultipleFiles(this.maxFileSize))
      {
        if (file.Size < this.maxFileSize)
        {
          IBrowserFile imgFile = file;
          this.profilePictureByteArray = new byte[imgFile.Size];
          await imgFile.OpenReadStream().ReadAsync(profilePictureByteArray);
          string imageType = imgFile.ContentType;
          this.imageUrl = $"data:{imageType};base64,{Convert.ToBase64String(profilePictureByteArray)}";
          this.ImgUrls.Add(imageUrl);
          this.FileData.Add(new FileData
          {
            Data = profilePictureByteArray,
            FileType = imageType,
            Size = imgFile.Size
          });
        }
        else
        {
          await Message.Error("File is too large!", 5);

          await this.Notice.Open(new NotificationConfig()
          {
            Message = "title",
            Duration = 0,
            Description = "File is too large!"
          });
        }
      }
    }
  }
}

You should also add <AntContainer />, perhaps at the top of the App component (App.razor)

Note: I ran your code in Net5.0 in which you must tell the compiler that a given type is nullable individually. As I didn't want to waste my time, I removed the nullability. If you have some issues related to this let me know. My code works, but is not complete

  • Related