如何在 Razor 页面 (.NetCore 5.1) 中动态设置模型而不导致完整回发
How to dynamically set the model without causing a full postback in a Razor Page (.NetCore 5.1)
我有使用 ASP.NET 网络表单的经验,但现在我正在使用不同的框架,所以我需要一些帮助。我是 ASP.NET 使用 Razor 页面和 .NET Core 的 MVC 的新手,我需要使用这些技术完成此任务。
我有一个视图模型,为了举例,假设它是一个人视图模型。 Person
有一些属性,如姓名、年龄、性别等。它有一个 属性 爱好,这是一个 IEnumerable<Hobbies>
存储每个人的爱好。爱好具有描述、花费时间等属性
我有一个可以编辑一个人并保存的表格。在此表单上,我有一个按钮 post 表单并将所有信息保存到数据库中。
我的问题是我需要能够动态分配和删除爱好,而无需 post 整个表格。例如,一个人可以有多个爱好,所以我有一个加号按钮来添加一个新的爱好并将其存储在视图模型中,而无需 posting 整个表单,因为用户仍然想添加更多的爱好并且何时他完成了,然后他点击了保存按钮,这样他就可以保存所有的信息。
我该怎么做?我尝试通过在 ADD Hobbies 按钮上调用 Ajax 来完成此操作,我使用 JSON.stringify(Person)
将人传递给操作并且我能够向视图模型添加一个爱好,但后来我明白了视图模型总是使用页面第一次加载的信息进行格式化,所以我没有在内存中操作视图模型。每次单击“添加”按钮时,我总是只添加一个爱好。
我要做的另一件事是呈现用户添加的每个爱好的输入,以便他可以键入爱好的描述等。
我什至不能嵌套两个表单,因为我试图在保存表单中添加一个 ajax.BeginForm 到 AddHobbies,但这也不可能。
有人可以帮我吗?我知道如果我 post 编辑我的代码会更好,但我不能。实际情况比这复杂得多,但是这个 Person and Hobbies 很好地解释了我所需要的。
我的类(只是例子,不是真实案例)
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.");
}
}
}
结果:
我有使用 ASP.NET 网络表单的经验,但现在我正在使用不同的框架,所以我需要一些帮助。我是 ASP.NET 使用 Razor 页面和 .NET Core 的 MVC 的新手,我需要使用这些技术完成此任务。
我有一个视图模型,为了举例,假设它是一个人视图模型。 Person
有一些属性,如姓名、年龄、性别等。它有一个 属性 爱好,这是一个 IEnumerable<Hobbies>
存储每个人的爱好。爱好具有描述、花费时间等属性
我有一个可以编辑一个人并保存的表格。在此表单上,我有一个按钮 post 表单并将所有信息保存到数据库中。
我的问题是我需要能够动态分配和删除爱好,而无需 post 整个表格。例如,一个人可以有多个爱好,所以我有一个加号按钮来添加一个新的爱好并将其存储在视图模型中,而无需 posting 整个表单,因为用户仍然想添加更多的爱好并且何时他完成了,然后他点击了保存按钮,这样他就可以保存所有的信息。
我该怎么做?我尝试通过在 ADD Hobbies 按钮上调用 Ajax 来完成此操作,我使用 JSON.stringify(Person)
将人传递给操作并且我能够向视图模型添加一个爱好,但后来我明白了视图模型总是使用页面第一次加载的信息进行格式化,所以我没有在内存中操作视图模型。每次单击“添加”按钮时,我总是只添加一个爱好。
我要做的另一件事是呈现用户添加的每个爱好的输入,以便他可以键入爱好的描述等。
我什至不能嵌套两个表单,因为我试图在保存表单中添加一个 ajax.BeginForm 到 AddHobbies,但这也不可能。
有人可以帮我吗?我知道如果我 post 编辑我的代码会更好,但我不能。实际情况比这复杂得多,但是这个 Person and Hobbies 很好地解释了我所需要的。
我的类(只是例子,不是真实案例)
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.");
}
}
}
结果: