英文:
Blazor EditForm - Create Child Data
问题
在Blazor中,我有一个EditForm
,用户可以在其中指定新任务的详细信息,例如任务的所有者。任务和客户是分开的类,存储在它们自己的数据表中。现在,在用户在编辑表单中指定新任务时,在客户字段中,我希望他们能够搜索现有客户,并且如果客户尚不存在,可以创建一个新客户。
因此,我在任务编辑表单中添加了一个按钮,该按钮打开一个新的编辑表单,可以在其中添加客户详细信息。并且在保存客户详细信息时,我希望用户被重定向回任务编辑表单,以便也可以保存任务。
我的问题是,在重定向到任务编辑表单时,该页面会丢失所有数据,并且不会正确重新初始化表单。如果我不做任何事情,我会收到参数未初始化的错误,如果我手动重新初始化参数,那么我会丢失所有数据,并且保存任务表单的按钮停止工作。难道没有其他方法吗?
这是我的代码示例:
<EditForm Model="@task" OnValidSubmit="@OnValidSubmit">
<DataAnnotationsValidator />
<div class="form-group">
<label>Description :</label>
<div>
<InputText @bind-Value="@task.Description" class="form-control col-sm-3" />
<ValidationMessage For="@(() => task.Description)" />
</div>
</div>
<div>
<label>Client :</label>
<div>
<SfAutoComplete TValue="Client" TItem="Client" Placeholder="Specify client name" AllowCustom=true @bind-Value="@task.Client" DataSource="@clients">
<AutoCompleteFieldSettings Value="Name" />
</SfAutoComplete>
</div>
</div>
<div class ="form-group">
<a class ="btn btn-success" href="/createclient" ><i class = "oi oi-plus"></i> Create New</a>
</div>
<button type="submit" class="btn btn-success">
@ButtonText
</button>
</EditForm>
@code {
[Parameter]
public CreateJobRequest task { get; set; }
[Parameter]
public string ButtonText { get; set; } = "Save";
[Parameter]
public EventCallback OnValidSubmit { get; set; }
private IEnumerable<Api.Entities.Client>? clients;
SfAutoComplete<string, Client> AutocompleteObj { get; set; }
protected override async Task OnInitializedAsync()
{
clients = await ClientService.GetAll();
}
private async Task OnFilter(FilteringEventArgs args)
{
args.PreventDefaultAction = true;
var query = new Query().Where(new WhereFilter() { Field = "Name", Operator = "contains", value = args.Text, IgnoreCase = true });
query = !string.IsNullOrEmpty(args.Text) ? query : new Query();
await AutocompleteObj.FilterAsync(clients, query);
var t = AutocompleteObj.GetItemsAsync();
}
}
我尝试了重新初始化参数:
@if(task == null)
{
task = new CreateJobRequest();
ButtonText = "Save";
OnValidSubmit = new EventCallback();
}
但是除了丢失'task'中的所有数据之外,保存任务表单的按钮仍然无法正常工作。
有人可以帮助我解决这个问题吗?或者是否有一种直接在任务编辑表单中输入客户详细信息的方法?
英文:
In Blazor, I have an EditForm
where the user can specify the details of a new task, such as the owner of the task. The tasks and clients are separate classes, that are stored in their own data table. Now, on the edit form where the user specifies a new task, in the client field, I want them to be able to search for an existing client, and, if the client doesn't exist yet, create a new one.
So I've added a button in the tasks edit form that opens a new edit form where the client details can be added. And on saving those client details, I want the user to be redirected back to the tasks edit form, so they can be saved as well.
My problem is, on redirecting to the task edit form, that page loses all data, and doesn't reinitialize the form correctly. If I don't do anything, I get an error that the parameters aren't initialized, and if I manually re-initialize the parameters, I'm losing all data, and the button to save the task form stops working. Isn't there any way to do this?
This is what my code looks like:
<EditForm Model="@task" OnValidSubmit="@OnValidSubmit">
<DataAnnotationsValidator />
<div class="form-group">
<label>Description :</label>
<div>
<InputText @bind-Value="@task.Description" class="form-control col-sm-3" />
<ValidationMessage For="@(() => task.Description)" />
</div>
</div>
<div>
<label>Client :</label>
<div>
<SfAutoComplete TValue="Client" TItem="Client" Placeholder="Specify client name" AllowCustom=true @bind-Value="@task.Client" DataSource="@clients">
<AutoCompleteFieldSettings Value="Name" />
</SfAutoComplete>
</div>
</div>
<div class ="form-group">
<a class ="btn btn-success" href="/createclient" ><i class = "oi oi-plus"></i> Create New</a>
</div>
<button type="submit" class="btn btn-success">
@ButtonText
</button>
</EditForm>
@code {
[Parameter]
public CreateJobRequest task { get; set; }
[Parameter]
public string ButtonText { get; set; } = "Save";
[Parameter]
public EventCallback OnValidSubmit { get; set; }
private IEnumerable<Api.Entities.Client>? clients;
SfAutoComplete<string, Client> AutocompleteObj { get; set; }
protected override async Task OnInitializedAsync()
{
clients = await ClientService.GetAll();
}
private async Task OnFilter(FilteringEventArgs args)
{
args.PreventDefaultAction = true;
var query = new Query().Where(new WhereFilter() { Field = "Name", Operator = "contains", value = args.Text, IgnoreCase = true });
query = !string.IsNullOrEmpty(args.Text) ? query : new Query();
await AutocompleteObj.FilterAsync(clients, query);
var t = AutocompleteObj.GetItemsAsync();
}
}
I've tried re-initializing the parameters with
@if(task == null)
{
task = new CreateJobRequest();
ButtonText = "Save";
OnValidSubmit = new EventCallback();
}
but besides then losing all data in 'task', the button to save the task form still doesn't work anymore.
Can anyone help me with this? Or is there a way to enter the client details directly in the task edit form?
答案1
得分: 0
以下是代码的翻译部分:
Models:
CTask.cs
public class CTask
{
public int Id { get; set; }
public string Description { get; set; }
public Client Client { get; set; }
public int ClientId { get; set; }
}
Client.cs
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public List<CTask> Tasks { get; set; }
}
AddTask.razor
@page "/addtask"
@using DatabaseTestProject.Business_Logic
@inject TaskBL taskBL;
@if (!DataLoading)
{
<EditForm Model="_task" OnValidSubmit="@OnValidSubmit">
<div class="form-group">
<label>Description :</label>
<InputText required @bind-Value="_task.Description" class="form-control"></InputText>
</div>
@if (!AddingNewClient)
{
<div class="form-group">
<label>Client :</label>
<InputSelect required @bind-Value="_task.ClientId">
@foreach (var client in _clients)
{
<option value="@client.Id">@client.Name</option>
}
</InputSelect>
</div>
<button type="button" @onclick="AddNewClient">New Client</button>
}
else
{
<div class="form-group">
<label>Name :</label>
<InputText required @bind-Value="_task.Client.Name" class="form-control"></InputText>
</div>
<div class="form-group">
<label>Address :</label>
<InputText required @bind-Value="_task.Client.Address" class="form-control"></InputText>
</div>
<div class="form-group">
<label>City :</label>
<InputText required @bind-Value="_task.Client.City" class="form-control"></InputText>
</div>
<button type="button" @onclick="ExistingClient">Existing Client</button>
}
<br />
<br />
<button type="submit">Submit</button>
</EditForm>
}
AddTask.razor.cs
using DatabaseTestProject.Models;
using Microsoft.AspNetCore.Components;
namespace DatabaseTestProject.Pages
{
public partial class AddTask : ComponentBase
{
private CTask _task;
private bool DataLoading = false;
private List<Client> _clients;
private bool AddingNewClient = false;
protected override async Task OnInitializedAsync()
{
if (DataLoading)
{
return;
}
try
{
DataLoading = true;
_task = new CTask();
_clients = await taskBL.GetClientsAsync();
}
catch (Exception)
{
throw;
}
finally
{
DataLoading = false;
}
}
private async Task OnValidSubmit()
{
var result = await taskBL.CreateTaskAsync(_task);
if (result > 0)
{
// Task added - Logic here
}
else
{
// Task NOT added - Logic here
}
}
private void AddNewClient()
{
AddingNewClient = true;
_task.ClientId = 0;
_task.Client = new();
}
private void ExistingClient()
{
AddingNewClient = false;
_task.ClientId = 0;
_task.Client = null;
}
}
}
TaskBL.cs
using DatabaseTestProject.Models;
using DatabaseTestProject.Repositories;
namespace DatabaseTestProject.Business_Logic
{
public class TaskBL
{
private readonly TaskRepos _repos;
public TaskBL(TaskRepos taskRepos)
{
_repos = taskRepos;
}
public async Task<List<Client>> GetClientsAsync()
{
return await _repos.GetClientsAsync();
}
public async Task<int> CreateTaskAsync(CTask task)
{
var result = await _repos.AddTaskAsync(task);
return result;
}
}
}
TaskRepos.cs
using DatabaseTestProject.Data;
using DatabaseTestProject.Models;
using Microsoft.EntityFrameworkCore;
namespace DatabaseTestProject.Repositories
{
public class TaskRepos
{
private readonly IDbContextFactory<ApplicationDbContext> _factory;
public TaskRepos(IDbContextFactory<ApplicationDbContext> factory)
{
_factory = factory;
}
public async Task<List<Client>> GetClientsAsync()
{
using (var _db = _factory.CreateDbContext())
{
return await _db.Clients.ToListAsync();
}
}
public async Task<int> CreateNewClient(Client client)
{
using (var _db = _factory.CreateDbContext())
{
_db.Clients.Add(client);
return await _db.SaveChangesAsync();
}
}
public async Task<int> AddTaskAsync(CTask task)
{
using (var _db = _factory.CreateDbContext())
{
_db.Tasks.Add(task);
return await _db.SaveChangesAsync();
}
}
}
}
希望这能帮助你理解代码!如果需要进一步的帮助,请告诉我。
英文:
Since you did not say if you are using Blazor Server of Blazor WASM with or without Entity Framework Core, for now I've assumed you're using Blazor Server with EF Core. The code below should do just fine:
Models:
CTask.cs
public class CTask
{
public int Id { get; set; }
public string Description { get; set; }
public Client Client { get; set; }
public int ClientId { get; set;
}
Client.cs
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public List<CTask> Tasks { get; set; }
}
AddTask.razor
@page "/addtask"
@using DatabaseTestProject.Business_Logic
@inject TaskBL taskBL;
@if (!DataLoading)
{
<EditForm Model="@_task" OnValidSubmit="@OnValidSubmit">
<div class="form-group">
<label>Description :</label>
<InputText required @bind-Value="@_task.Description" class="form-control"></InputText>
</div>
@if (!AddingNewClient)
{
<div class="form-group">
<label>Client :</label>
<InputSelect required @bind-Value="_task.ClientId">
@foreach (var client in _clients)
{
<option value="@(client.Id)">@(client.Name)</option>
}
</InputSelect>
</div>
<button type="button" @onclick="AddNewClient">New Client</button>
}
else
{
<div class="form-group">
<label>Name :</label>
<InputText required @bind-Value="@_task.Client.Name" class="form-control"></InputText>
</div>
<div class="form-group">
<label>Address :</label>
<InputText required @bind-Value="@_task.Client.Address" class="form-control"></InputText>
</div>
<div class="form-group">
<label>City :</label>
<InputText required @bind-Value="@_task.Client.City" class="form-control"></InputText>
</div>
<button type="button" @onclick="ExistingClient">Existing Client</button>
}
<br />
<br />
<button type="submit">Submit</button>
</EditForm>
}
Note: I don't have Syncfusion. Just replace the InputSelect with your AutoComplete
AddTask.razor.cs
using DatabaseTestProject.Models;
using Microsoft.AspNetCore.Components;
namespace DatabaseTestProject.Pages
{
public partial class AddTask : ComponentBase
{
private CTask _task;
private bool DataLoading = false;
private List<Client> _clients;
private bool AddingNewClient = false;
protected override async Task OnInitializedAsync()
{
if (DataLoading)
{
return;
}
try
{
DataLoading = true;
_task = new CTask();
_clients = await taskBL.GetClientsAsync();
}
catch (Exception)
{
throw;
}
finally
{
DataLoading = false;
}
}
private async Task OnValidSubmit()
{
var result = await taskBL.CreateTaskAsync(_task);
if (result > 0)
{
//Task added - Logic here
}
else
{
//Task NOT added - Logic here
}
}
private void AddNewClient()
{
AddingNewClient = true;
_task.ClientId = 0;
_task.Client = new();
}
private void ExistingClient()
{
AddingNewClient = false;
_task.ClientId = 0;
_task.Client = null;
}
}
}
TaskBL.cs
using DatabaseTestProject.Models;
using DatabaseTestProject.Repositories;
namespace DatabaseTestProject.Business_Logic
{
public class TaskBL
{
private readonly TaskRepos _repos;
public TaskBL(TaskRepos taskRepos)
{
_repos = taskRepos;
}
public async Task<List<Client>> GetClientsAsync()
{
return await _repos.GetClientsAsync();
}
public async Task<int> CreateTaskAsync(CTask task)
{
var result = await _repos.AddTaskAsync(task);
return result;
}
}
}
TaskRepos.cs
using DatabaseTestProject.Data;
using DatabaseTestProject.Models;
using Microsoft.EntityFrameworkCore;
namespace DatabaseTestProject.Repositories
{
public class TaskRepos
{
private readonly IDbContextFactory<ApplicationDbContext> _factory;
public TaskRepos(IDbContextFactory<ApplicationDbContext> factory)
{
_factory = factory;
}
public async Task<List<Client>> GetClientsAsync()
{
using (var _db = _factory.CreateDbContext())
{
return await _db.Clients.ToListAsync();
}
}
public async Task<int> CreateNewClient(Client client)
{
using (var _db = _factory.CreateDbContext())
{
_db.Clients.Add(client);
return await _db.SaveChangesAsync();
}
}
public async Task<int> AddTaskAsync(CTask task)
{
using (var _db = _factory.CreateDbContext())
{
_db.Tasks.Add(task);
return await _db.SaveChangesAsync();
}
}
}
}
When an existing Client
is selected, nothing happens with the client table
. When the user added a new Client
EF Core will add this to the client table
and return the Id to the CTask
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论