问题描述
我有使用 ASP.NET webforms 的经验,但现在我正在使用不同的框架,所以我需要一些帮助。我是 ASP.NET MVC 的新手,使用 Razor 页面和 .NET Core,我需要使用这些技术完成这项任务。
我有一个视图模型,为了举例,假设它是一个人视图模型。 Person
有一些属性,如姓名、年龄、性别等。它有一个属性 Hobbies,它是一个存储每个人的爱好的 IEnumerable<Hobbies>
。爱好具有描述、花费时间等属性。
我有一个表单,我可以在其中编辑一个人并保存它。在这个表单上,我有一个按钮可以发布表单并将所有信息保存到数据库中。
我的问题是我需要能够动态分配和删除爱好,而无需发布整个表单。例如,一个人可以有多个爱好,所以我有一个加号按钮来添加一个新的爱好并将其存储在视图模型中而不发布整个表单,因为用户仍然想添加更多的爱好,当他完成时,他点击保存按钮,这样他就可以保存所有信息。
我该怎么做?我尝试通过 ADD Hobbies 按钮上的 Ajax 调用来实现,在该按钮中我使用 JSON.stringify(Person)
将人员传递给操作,并且我能够向视图模型添加一个爱好,但后来我明白了视图模型是总是用页面第一次加载的信息格式化,所以我没有在内存中操作视图模型。每次点击“添加”按钮时,我总是只添加一个爱好。
我必须做的另一件事是为用户添加的每个爱好呈现输入,以便他可以键入爱好的描述等。
我什至不能嵌套两个表单,因为我试图在 Save Form 中的 AddHobbies 中添加一个 ajax.BeginForm,但这也不可能。
谁能帮我解决这个问题?我知道如果我发布我的代码会更好,但我不能。实际场景比这复杂得多,但这个人和爱好很好地解释了我需要什么。
我的课程(只是示例,并非真实案例)
public class Person
{
public int Id { get; set; }
[required]
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
[ForeignKey(nameof(Hobbies))]
public int HobbieId { get; set; }
public IEnumerable<Hobbie> Hobbies { get; set; }
public class Hobbie
{
public int Id { get; set; }
public string Description { get; set; }
public double TimeSpent { get; set; }
}
}
Controller API Action
public JsonResult OnPostAddHobbieAsync([FromBody]Person person)
{
try
{
person.Hobbies.Add(new Hobbie() { Description = "My Hobbie",TimeSpent= 120 });
return new JsonResult(person);
}
catch (Exception ex)
{
return new JsonResult("Error generating new credentials. Please contact your administrator.");
}
}
剃刀页面:
<form id="registerForm" method="post" enctype="multipart/form-data">
<!--// Person information inputs-->
<div class="form-group">
<label asp-for="Person.Name"></label>
<div class="input-group">
<input asp-for="Person.Name" class="form-control" autocomplete="off" />
</div>
<span asp-validation-for="Person.Name" class="text-danger"></span>
</div>
<!----- All the persons Properties Inputs
--- All the persons Properties Inputs
--- All the persons Properties Inputs-->
<label asp-for="Person.Hobbies"></label>
<br />
<a class="btn btn-primary" style="border-radius:100px" onclick="addHobbie()"><i class="fas fa-plus"></i></a> <!--// Button to add a new hobbie-->
<div class="form-group" id="HobbiesDiv">
@foreach(var hobbie in Model.Person.Hobbies) <!--// this is too loop through the Hobbies and render inputs so the user can type the description-->
{
<a class="btn btn-danger" onclick="DeleteHobbie();" style="border-radius:100px"><i class="fas fa-minus fa-fw"></i></a> <!--// button to delete an existing hobbie-->
<div id="customWrapper">
<label asp-for="@hobbie.Description"></label>
<input asp-for="@Hobbie.Description" />
<span asp-validation-for="@Hobbie.Description"></span>
</div>
}
</div>
<div class="col-12 pt-5">
<button class="btn btn-primary float-right" type="submit"><i class="fas fa-save"></i> Save</button> <!--// when this button is clicked,it posts the form and save all the information added-->
<a asp-page="/Controller/Index" type="button" class="btn btn-danger float-right mr-2">Cancel</a>
</div>
<input asp-for="Person.Id" />
<script>
function addHobbie() {
var json = @Html.Raw(Json.Serialize(@Model.Person));
var url = "/Controller/Edit?handler=AddHobbieAsync";
$.ajax({
url: url,type: "POST",dataType: "json",contentType: 'application/json; charset=utf-8',data: JSON.stringify(json),success: function (data) {
alert(data.customFields);
// I successfully have a new hobbie in data. Next Time I call this fucntion,I loose the hobbies that where returned in the prevIoUs call. Also,I have
//to bind the new hobbie that was added,but since it doesn't cause a postback,the foreach loop still does not render any tags. I dont kNow if this is the better aproach
//to achieve my goal
},error: function (xhr,textStatus,errorThrown) {
alert(xhr.responseText);
}
});
}
</script>
解决方法
您可以定义一个 static
变量来存储爱好。
public static IList<Hobbie> Hobbies { get; set; }
而且我建议使用局部视图来展示爱好,否则提交整个模型时,模型绑定会出错。
基于您的代码的简单演示。
型号:
public class Person
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public List<Hobbie> Hobbies { get; set; }
}
public class Hobbie
{
public int Id { get; set; }
public string Description { get; set; }
public double TimeSpent { get; set; }
}
Index.cshtml:
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<form id="registerForm" method="post" enctype="multipart/form-data">
<div class="form-group">
<label asp-for="Person.Name"></label>
<div class="input-group">
<input asp-for="Person.Name" class="form-control" autocomplete="off" />
</div>
<span asp-validation-for="Person.Name" class="text-danger"></span>
</div>
<label asp-for="Person.Hobbies"></label>
<br />
<a class="btn btn-primary" style="border-radius:100px" onclick="addHobbie()"><i class="fas fa-plus"></i></a> <!--// Button to add a new hobbie-->
<div class="form-group" id="HobbiesDiv">
@if (Model.Person.Hobbies != null)
{
<partial name="_HobbiesPartial" model="Model.Person.Hobbies" />
}
</div>
<div class="col-12 pt-5">
<button class="btn btn-primary float-right" type="submit"><i class="fas fa-save"></i> Save</button> <!--// when this button is clicked,it posts the form and save all the information added-->
<a asp-page="/Index" type="button" class="btn btn-danger float-right mr-2">Cancel</a>
</div>
<input asp-for="Person.Id" type="hidden" />
</form>
@section scripts{
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous" />
<script>
function addHobbie() {
var url = "/Index?handler=AddHobbie";
$.ajax({
url: url,type: "POST",headers: {
'RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val()
},success: function (data) {
$('#HobbiesDiv').empty();
$('#HobbiesDiv').html(data)
},error: function (xhr,textStatus,errorThrown) {
alert(xhr.responseText);
}
});
}
</script>
}
_HobbiesPartial.cshtml:
@model List<RazorTest.Models.Hobbie>
@{
Layout = null;
}
@for (int i = 0; i < Model.Count; i++)
{
<a class="btn btn-danger" onclick="DeleteHobbie();" style="border-radius:100px"><i class="fas fa-minus"></i></a> <!--// button to delete an existing hobbie-->
<div id="customWrapper">
<label asp-for="@Model[i].Description"></label>
<input name="Person.Hobbies[@i].Description" value="@Model[i].Description" />
<span asp-validation-for="@Model[i].Description"></span>
</div>
}
Index.cshtml.cs:
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
[BindProperty]
public Person Person { get; set; }
public static List<Hobbie> Hobbies { get; set; }
public void OnGet()
{
Person = new Person
{
Id = 1,Name = "ABC",Hobbies = new List<Hobbie>
{
new Hobbie{ Description = "my hobbie",TimeSpent = 120 }
}
};
Hobbies = Person.Hobbies;
}
public void OnPost()
{
var person = Person;
}
public IActionResult OnPostAddHobbie()
{
try
{
Hobbies.Add(new Hobbie() { Description = "my hobbie",TimeSpent = 120 });
return Partial("_HobbiesPartial",Hobbies);
}
catch (Exception ex)
{
return new JsonResult("error generating new credentials. please contact your administrator.");
}
}
}
结果: