Ajax 调用不在 MVC 的嵌套编辑器模板中执行

Ajax call is not performed inside a nested editor templates in MVC

提问人:Vasily Pascal 提问时间:10/10/2023 最后编辑:Vasily Pascal 更新时间:10/11/2023 访问量:67

问:

我正在尝试创建一个用于创建/编辑测验的 Web 应用程序管理工具,该工具可以完全调整(使用 ASP.NET Core MVC)。

我所说的完全可调是什么意思:

  1. 管理员用户可以为每个测验添加/删除任意数量的问题。
  2. 管理员用户可以在每个问题的答案上添加/删除任何数字。

问题是,在类中,我有一个集合,每个集合都有一个集合。QuizViewModelQuestionsQuestionAnswers

我已经创建了 2 个用于添加和删除的按钮,通过使用 EditorTemplates 和对我的控制器方法执行 Ajax 调用,该方法正在从集合中添加/删除对象。所有绑定都运行良好。QuestionsQuestions

但是,当我尝试为每个添加/删除创建相同的逻辑时,就会出现问题。我正在尝试执行与我相同的操作,方法是为每个答案的部分分配 div 它是唯一的 id,即AnswerQuestionQuestions

string answersContainerId = $"answers-container-{Model.QuestionNumber}";

对“添加/删除答案”按钮执行相同的操作。

以下是我的课程的简化结构:

public class QuizViewModel
{
    public string QuizName { get; set; }
    public List<QuizQuestion> Questions { get; set; }

    public QuizViewModel()
    {
        this.Questions = new List<QuizQuestion>
        {
            new QuizQuestion() // First must-have question
            {
                QuestionNumber = 1
            }
        };
    }
}
public class QuizQuestion
{   
    public string QuestionName { get; set; }
    public int Duration { get; set; } // Question duration (sec)
    public int QuestionNumber { get; set; }
    public List<QuizAnswer> Answers { get; set; } // List of answers per 1 question

    public QuizQuestion()
    {
        Duration = 30; // default
        Answers = new List<QuizAnswer>() // When creating a new empty question, by default we create 3 answers for it
        {
            new QuizAnswer{ AnswerNumber = 1 },
            new QuizAnswer{ AnswerNumber = 2 },
            new QuizAnswer{ AnswerNumber = 3}
        };
    }
}
public class QuizAnswer
{
    public string AnswerName { get; set; }
    public bool IsCorrect { get; set; }
    public int AnswerNumber { get; set; }
}

以下是视图(简化):Views>Quiz>CreateQuiz.cshtml

@model QuizViewModel

@using (Html.BeginForm("CreateQuiz", "Quiz", FormMethod.Post, new { id = "form-create-new-quiz" }))
{
    @Html.AntiForgeryToken()

    <h3>Quiz Name:</h3>
    <div class="form-group">
        @Html.LabelFor(model => model.QuizName,"Quiz Name:", htmlAttributes: new { @class = "control-label" })
        @Html.TextBoxFor(model => model.QuizName, new { @class = "form-control"})
        @Html.ValidationMessageFor(model => model.QuizName, "", new { @class = "text-danger" })
    </div>
    
    <div>
        <h1>Quiz Questions:</h1>
        <div id="questions-container">
            @Html.EditorFor(model => model.Questions)
        </div>
    </div>
    
    <div>
        <button id="btn-add-new-question" type="button" class="btn btn-success">Add new question</button>
        <button id="btn-remove-quiz-question" type="button" class="btn btn-danger">Remove Question</button>
    
        <input type="submit" value="Submit Quiz" class="btn btn-primary" />
    </div>

}

@section Scripts 
{
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>

    <script type="text/javascript">
        $("#btn-add-new-question").on('click', function () {
            $.ajax({
                async: true,
                data: $('#form-create-new-quiz').serialize(),
                type: "POST",
                url: "/Quiz/AddQuizQuestion",
                success: function (partialView) {
                    console.log("partialView: " + partialView);
                    $('#questions-container').html(partialView);
                }
            });
        });
    </script>
    
    <script type="text/javascript">
        $("#btn-remove-quiz-question").on('click', function () {
            $.ajax({
                async: true,
                data: $('#form-create-new-quiz').serialize(),
                type: "POST",
                url: "/Quiz/RemoveQuizQuestion",
                success: function (partialView) {
                    console.log("partialView: " + partialView);
                    $('#questions-container').html(partialView);
                }
            });
        });
    </script>
}

我在文件夹中有一个编辑器模板文件和类:.cshtmlQuestionAnswerEditorTemplates

  1. Views>Shared>EditorTemplates>QuizQuestion.cshtml文件:
@model QuizQuestion

@{
    string currentQuestionAddAnswerButtonId = $"btn-add-new-answer-to-question-{Model.QuestionNumber}";
    string currentQuestionRemoveAnswerButtonId = $"btn-remove-answer-from-question-{Model.QuestionNumber}";
    string answersContainerId = $"answers-container-{Model.QuestionNumber}";
}

<div class="form-group">
    <div>
        @Html.LabelFor(question => question.QuestionName,"Question Name:", htmlAttributes: new { @class = "control-label" })
        @Html.TextBoxFor(question => question.QuestionName, new { @class = "form-control"})
        @Html.ValidationMessageFor(question => question.QuestionName, "", new { @class = "text-danger" })
    </div>

    <div id="@{@answersContainerId}">
        @Html.EditorFor(question => question.Answers)
    </div>

    <button id="@{@currentQuestionAddAnswerButtonId}" type="button" class="btn btn-success">Add New Answer</button>
    <button id="@{@currentQuestionRemoveAnswerButtonId}" type="button" class="btn btn-danger">Remove Answer</button>

</div>

@section Scripts
{
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">

    <script type="text/javascript">
        $("#@{@currentQuestionAddAnswerButtonId}").on('click', function () {
            $.ajax({
                async: true,
                data: $('#form-create-new-quiz').serialize(),
                type: "POST",
                url: "/Quiz/AddNewAnswerToQuestion",
                success: function (partialView) {
                    console.log("partialView: " + partialView);
                    $('#@{@answersContainerId}').html(partialView);
                }
            });
        });
    </script>

    <script type="text/javascript">
        $("#@{@currentQuestionRemoveAnswerButtonId}").on('click', function () {
            $.ajax({
                async: true,
                data: $('#form-create-new-quiz').serialize(),
                type: "POST",
                url: "/Quiz/RemoveAnswerFromQuestion",
                success: function (partialView) {
                    console.log("partialView: " + partialView);
                    $('#@{@answersContainerId}').html(partialView);
                }
            });
        });
    </script>
}
  1. Views>Shared>EditorTemplates>QuizAnswer.cshtml文件:
@model QuizAnswer

<div>
    <h2><strong>Answer @Html.DisplayFor(answer => answer.AnswerNumber)</strong></h2>
    @Html.HiddenFor(answer => answer.AnswerNumber)

    <div class="form-group">
        <div>
            @Html.LabelFor(answer => answer.AnswerName,"Answer:", htmlAttributes: new { @class = "control-label" })
            @Html.TextBoxFor(answer => answer.AnswerName, new { @class = "form-control"})
            @Html.ValidationMessageFor(answer => answer.AnswerName, "", new { @class = "text-danger" })
        </div>

        <div class="form-check">
            @Html.CheckBoxFor(answer => answer.IsCorrect, new { @class = "form-check-input"})
            @Html.LabelFor(answer => answer.IsCorrect, "Is Correct", new { @class = "form-check-label"})
        </div>
    </div>
</div>

分别,我确实有 2 个编辑器模板的部分视图:

  1. Views>Quiz>QuizQuestions.cshtml:
@model QuizViewModel
@Html.EditorFor(model => model.Questions)
  1. Views>Quiz>QuizAnswers.cshtml:
@model QuizQuestion
@Html.EditorFor(model => model.Answers)

最后,这是我的班级:QuizController

public class QuizController : Controller
{
    public IActionResult CreateQuiz()
    {
        return View(new QuizViewModel());
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> AddQuizQuestion([Bind("Questions")] QuizViewModel quizViewModel)
    {
        QuizQuestion newQuizQuestion = new QuizQuestion
        {
            QuestionNumber = quizViewModel.Questions.Count + 1
        };

        quizViewModel.Questions.Add(newQuizQuestion);
        return PartialView("QuizQuestions", quizViewModel);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> RemoveQuizQuestion([Bind("Questions")] QuizViewModel quizViewModel)
    {
        if (quizViewModel.Questions.Count is not 1)
        {
            quizViewModel.Questions.RemoveAt(quizViewModel.Questions.Count - 1);
        }

        return PartialView("QuizQuestions", quizViewModel);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> AddNewAnswerToQuestion([Bind("Questions")] QuizViewModel quizViewModel)
    {
        // Define to which question to add answer
        var quizQuestion = quizViewModel.Questions.FirstOrDefault();
        return PartialView("QuizAnswers", quizQuestion);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> RemoveAnswerFromQuestion([Bind("Questions")] QuizViewModel quizViewModel)
    {
        // Define from which question to remove answer
        var quizQuestion = quizViewModel.Questions.FirstOrDefault();
        return PartialView("QuizAnswers", quizQuestion);
    }
}

所以最后,当我按下唯一的添加/删除答案按钮时,我的控制器方法和没有被调用。AddNewAnswerToQuestion()RemoveAnswerFromQuestion()

但是,相同的逻辑适用于添加/删除问题。

所以我认为问题出在嵌套类中。

真的希望有人能帮忙,我被困住了。 如果您需要更多信息,请告诉我。

这是我在 Stack Overflow 上的第一个问题,所以如果有什么没有得到很好的解释,我深表歉意。

C# ajax asp.net-core-mvc 模型绑定 mvc-editor-templates

评论

0赞 Tiny Wang 10/10/2023
抱歉,我未能用您的代码片段在我这边重现您的问题,当提到这一点时,您在浏览器控制台窗口中是否有任何错误消息?my Controller methods AddNewAnswerToQuestion() and RemoveAnswerFromQuestion() are not called
0赞 Vasily Pascal 10/10/2023
@TinyWang 对不起,我在简化代码以提高可见性时犯了一些错误。我忘了在测验控制器中包含 CreateQuiz 方法,也忘了取消注释 Methods 中的工作代码。现在,所有代码片段都 100% 正常工作。问题是当我转到 Quiz/CreateQuiz 页面并按下 Add/Remove QUESTION 按钮时,Ajax 调用了 AddQuizQuestion() 和 RemoveQuizQuestion() 方法,并且按预期返回了 Questions 的部分视图,但是当我按下 Add/Remove ANSWER 按钮时,AddNewAnswerToQuestion() 和 RemoveAnswerFromQuestion() 没有被调用。
0赞 Tiny Wang 10/11/2023
我们只将 create.cshtml 中编写的 js 函数加载到应用程序网站中,i.stack.imgur.com/nUW1Y.png 我的想法是,我们可能不得不重新设计视图。这对我来说太复杂了。
0赞 Tiny Wang 10/13/2023
你对这个问题有什么更新吗?
1赞 Vasily Pascal 10/16/2023
@TinyWang您建议的解决方案有效!要知道,在部分视图中,我们不需要使用 脚本部分 ,这是一件重要的事情。非常感谢您的帮助和参与!除了主要问题之外,目前我正在考虑如何确定哪个确切的问题要求添加/删除答案。

答:

0赞 Tiny Wang 10/11/2023 #1

我们可以看到我们没有 for ,这意味着我们没有按钮的 onclick 事件处理程序。<script>Views>Shared>EditorTemplates>QuizQuestion.cshtmlAdd New Answer

enter image description here

这是因为在部分视图中,我们用来包围脚本。让我们删除并查看结果。@section Scripts{ }@section Scripts{ }QuizQuestion.cshtml

enter image description here

在点击添加答案按钮的控制器方法后,我停止了故障排除......