首页 > 解决方案 > 如何在 Razor 页面 (.NetCore 5.1) 中动态设置模型而不导致完整回发

问题描述

我有使用 ASP.NET 网络表单的经验,但现在我正在使用不同的框架,所以我需要一点帮助。我是带有 Razor 页面和 .NET Core 的 ASP.NET MVC 的新手,我需要使用这些技术完成此任务。

我有一个视图模型,例如,假设它是一个人视图模型。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>

标签: c#ajaxasp.net-mvcasp.net-corerazor-pages

解决方案


您可以定义一个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; }
}

索引.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>
}

索引.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.");
        }
        
    }
}

结果:

在此处输入图像描述


推荐阅读