You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在非MVC的Razor页面提交JavaScript动态添加的输入值?

解决方案

针对你在Razor Pages中开发动态试题考试创建功能遇到的问题,给你三个可行的解决方案,按需选择:


方案1:前端维护数据,JSON序列化一次性提交

完全在前端动态生成试题表单,最后把所有考试信息(基础字段+试题)序列化成JSON提交,无需频繁发起后端请求。

前端代码(cshtml)

<form method="post">
    <div>
        <label>考试名称:</label>
        <input id="examName" type="text" required />
    </div>
    <div>
        <label>考试时长(分钟):</label>
        <input id="examDuration" type="number" min="1" required />
    </div>
    <div>
        <label>选择试题类型:</label>
        <select id="questionType">
            <option value="Open">开放题</option>
            <option value="Choice">选择题</option>
            <option value="Judge">判断题</option>
        </select>
        <button type="button" onclick="addQuestion()">添加试题</button>
    </div>
    <div id="questionsContainer"></div>
    <input type="hidden" id="questionsJson" name="QuestionsJson" />
    <button type="submit">创建考试</button>
</form>

<script>
    let questionIndex = 0;

    function addQuestion() {
        const type = document.getElementById('questionType').value;
        const container = document.getElementById('questionsContainer');
        const questionDiv = document.createElement('div');
        questionDiv.className = 'question-item';
        questionDiv.dataset.type = type;
        questionDiv.dataset.index = questionIndex;

        // 题目内容输入框
        questionDiv.innerHTML += `<div><label>题目:</label><input class="q-content" type="text" required /></div>`;

        // 按类型生成对应字段
        if (type === 'Choice') {
            questionDiv.innerHTML += `
                <div class="options">
                    <div><label>选项1:</label><input class="q-option" type="text" required /></div>
                    <div><label>选项2:</label><input class="q-option" type="text" required /></div>
                    <button type="button" onclick="addMoreOption(this)">添加选项</button>
                </div>
                <div><label>正确选项索引:</label><input class="q-correct-index" type="number" min="0" required /></div>
            `;
        } else if (type === 'Judge') {
            questionDiv.innerHTML += `
                <div>
                    <label>正确答案:</label>
                    <input type="radio" name="judge-${questionIndex}" value="true" required /> 对
                    <input type="radio" name="judge-${questionIndex}" value="false" required /> 错
                </div>
            `;
        }

        // 删除按钮
        questionDiv.innerHTML += `<button type="button" onclick="this.parentElement.remove()">删除</button>`;
        container.appendChild(questionDiv);
        questionIndex++;
    }

    function addMoreOption(btn) {
        const optionsDiv = btn.parentElement;
        const optionCount = optionsDiv.querySelectorAll('.q-option').length + 1;
        const newOption = document.createElement('div');
        newOption.innerHTML = `<label>选项${optionCount}:</label><input class="q-option" type="text" required />`;
        optionsDiv.insertBefore(newOption, btn);
    }

    // 提交前序列化所有数据
    document.querySelector('form').addEventListener('submit', function(e) {
        const examData = {
            name: document.getElementById('examName').value,
            duration: parseInt(document.getElementById('examDuration').value),
            questions: []
        };

        document.querySelectorAll('.question-item').forEach(item => {
            const type = item.dataset.type;
            const content = item.querySelector('.q-content').value;
            const question = { type, content };

            if (type === 'Choice') {
                question.options = Array.from(item.querySelectorAll('.q-option')).map(opt => opt.value);
                question.correctIndex = parseInt(item.querySelector('.q-correct-index').value);
            } else if (type === 'Judge') {
                question.correctAnswer = item.querySelector(`input[name="judge-${item.dataset.index}"]:checked`).value === 'true';
            }

            examData.questions.push(question);
        });

        document.getElementById('questionsJson').value = JSON.stringify(examData);
    });
</script>

PageModel代码(cshtml.cs)

public class CreateExamModel : PageModel
{
    [BindProperty]
    public string QuestionsJson { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid) return Page();

        // 反序列化提交的数据
        var submitData = JsonSerializer.Deserialize<ExamSubmitData>(QuestionsJson);
        if (submitData == null || !submitData.questions.Any())
        {
            ModelState.AddModelError("", "请至少添加一道试题");
            return Page();
        }

        // 转换为你的实体类并写入数据库
        var exam = new Exam
        {
            Name = submitData.name,
            Duration = submitData.duration,
            Questions = submitData.questions.Select(q => 
            {
                switch(q.type)
                {
                    case "Open": return new OpenQuestion { Content = q.content };
                    case "Choice": return new ChoiceQuestion 
                    { 
                        Content = q.content, 
                        Options = q.options, 
                        CorrectOptionIndex = q.correctIndex 
                    };
                    case "Judge": return new JudgeQuestion 
                    { 
                        Content = q.content, 
                        CorrectAnswer = q.correctAnswer.Value 
                    };
                    default: throw new ArgumentException("无效的试题类型");
                }
            }).ToList()
        };

        // await _dbContext.Exams.AddAsync(exam);
        // await _dbContext.SaveChangesAsync();

        return RedirectToPage("/Exams/Index");
    }

    // 临时接收类
    private class ExamSubmitData
    {
        public string name { get; set; }
        public int duration { get; set; }
        public List<QuestionSubmitData> questions { get; set; }
    }

    private class QuestionSubmitData
    {
        public string type { get; set; }
        public string content { get; set; }
        public List<string> options { get; set; }
        public int? correctIndex { get; set; }
        public bool? correctAnswer { get; set; }
    }

    // 你的实体类示例
    public class Exam { public int Id { get; set; } public string Name { get; set; } public int Duration { get; set; } public List<BaseQuestion> Questions { get; set; } }
    public abstract class BaseQuestion { public int Id { get; set; } public string Content { get; set; } }
    public class OpenQuestion : BaseQuestion { }
    public class ChoiceQuestion : BaseQuestion { public List<string> Options { get; set; } public int CorrectOptionIndex { get; set; } }
    public class JudgeQuestion : BaseQuestion { public bool CorrectAnswer { get; set; } }
}

方案2:利用ASP.NET模型绑定规则,动态生成带正确name的输入框

ASP.NET支持集合绑定,只要输入框的name属性符合集合名[索引].属性名格式,就能自动绑定到PageModel的集合属性。

PageModel代码

public class CreateExamModel : PageModel
{
    [BindProperty]
    public ExamViewModel Exam { get; set; } = new ExamViewModel();

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid) return Page();

        // 转换为实体并保存
        // ...

        return RedirectToPage("/Exams/Index");
    }

    public class ExamViewModel
    {
        public string Name { get; set; }
        public int Duration { get; set; }
        public List<QuestionViewModel> Questions { get; set; } = new List<QuestionViewModel>();
    }

    public class QuestionViewModel
    {
        public string Type { get; set; }
        public string Content { get; set; }
        public List<string> Options { get; set; } = new List<string>();
        public int? CorrectOptionIndex { get; set; }
        public bool? CorrectAnswer { get; set; }
    }
}

前端代码

<form method="post">
    <div>
        <label>考试名称:</label>
        <input asp-for="Exam.Name" type="text" required />
    </div>
    <div>
        <label>考试时长(分钟):</label>
        <input asp-for="Exam.Duration" type="number" min="1" required />
    </div>
    <div>
        <label>选择试题类型:</label>
        <select id="questionType">
            <option value="Open">开放题</option>
            <option value="Choice">选择题</option>
            <option value="Judge">判断题</option>
        </select>
        <button type="button" onclick="addQuestion()">添加试题</button>
    </div>
    <div id="questionsContainer"></div>
    <button type="submit">创建考试</button>
</form>

<script>
    let questionIndex = 0;

    function addQuestion() {
        const type = document.getElementById('questionType').value;
        const container = document.getElementById('questionsContainer');
        const questionDiv = document.createElement('div');
        questionDiv.className = 'question-item';

        // 隐藏字段存储试题类型
        questionDiv.innerHTML += `<input type="hidden" name="Exam.Questions[${questionIndex}].Type" value="${type}" />`;

        // 题目内容
        questionDiv.innerHTML += `
            <div>
                <label>题目:</label>
                <input name="Exam.Questions[${questionIndex}].Content" type="text" required />
            </div>
        `;

        if (type === 'Choice') {
            // 初始生成2个选项
            for (let i = 0; i < 2; i++) {
                questionDiv.innerHTML += `
                    <div>
                        <label>选项${i+1}:</label>
                        <input name="Exam.Questions[${questionIndex}].Options[${i}]" type="text" required />
                    </div>
                `;
            }
            // 添加更多选项按钮
            questionDiv.innerHTML += `<button type="button" onclick="addChoiceOption(${questionIndex}, this)">添加选项</button>`;
            // 正确选项索引
            questionDiv.innerHTML += `
                <div>
                    <label>正确选项索引:</label>
                    <input name="Exam.Questions[${questionIndex}].CorrectOptionIndex" type="number" min="0" required />
                </div>
            `;
        } else if (type === 'Judge') {
            // 判断题答案
            questionDiv.innerHTML += `
                <div>
                    <label>正确答案:</label>
                    <input type="radio" name="Exam.Questions[${questionIndex}].CorrectAnswer" value="true" required /> 对
                    <input type="radio" name="Exam.Questions[${questionIndex}].CorrectAnswer" value="false" required /> 错
                </div>
            `;
        }

        // 删除按钮
        questionDiv.innerHTML += `<button type="button" onclick="this.parentElement.remove()">删除</button>`;
        container.appendChild(questionDiv);
        questionIndex++;
    }

    function addChoiceOption(qIndex, btn) {
        const optionsCount = btn.parentElement.querySelectorAll('[name^="Exam.Questions['+qIndex+'].Options"]').length;
        const newOption = document.createElement('div');
        newOption.innerHTML = `
            <label>选项${optionsCount+1}:</label>
            <input name="Exam.Questions[${qIndex}].Options[${optionsCount}]" type="text" required />
        `;
        btn.parentElement.insertBefore(newOption, btn);
    }
</script>

方案3:用Session暂存考试数据(解决你之前的加题丢失问题)

如果需要后端参与试题添加逻辑,可把考试对象存在Session中,每次加题时从Session取出修改,再存回去,避免频繁操作数据库。

第一步:配置Session

在Program.cs中添加Session服务并启用:

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

// 启用Session(要放在UseRouting之后,UseAuthorization之前)
app.UseSession();

PageModel代码

public class CreateExamModel : PageModel
{
    [BindProperty]
    public Exam NewExam { get; set; }

    public IActionResult OnGet()
    {
        // 从Session取考试数据,没有则初始化
        NewExam = HttpContext.Session.Get<Exam>("CurrentExam") ?? new Exam { Questions = new List<BaseQuestion>() };
        return Page();
    }

    public IActionResult OnPostAddQuestion(string questionType)
    {
        var exam = HttpContext.Session.Get<Exam>("CurrentExam") ?? new Exam { Questions = new List<BaseQuestion>() };

        // 添加对应类型的试题
        switch (questionType)
        {
            case "Open": exam.Questions.Add(new OpenQuestion()); break;
            case "Choice": exam.Questions.Add(new ChoiceQuestion { Options = new List<string> { "", "" } }); break;
            case "Judge": exam.Questions.Add(new JudgeQuestion()); break;
        }

        // 存回Session
        HttpContext.Session.Set<Exam>("CurrentExam", exam);
        return RedirectToPage();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid) return Page();

        var exam = HttpContext.Session.Get<Exam>("CurrentExam");
        if (exam == null || !exam.Questions.Any())
        {
            ModelState.AddModelError("", "请先添加试题");
            return Page();
        }

        // 更新基础信息
        exam.Name = NewExam.Name;
        exam.Duration = NewExam.Duration;

        // 保存到数据库
        // await _dbContext.Exams.AddAsync(exam);
        // await _dbContext.SaveChangesAsync();

        // 清除Session
        HttpContext.Session.Remove("CurrentExam");
        return RedirectToPage("/Exams/Index");
    }
}

// Session扩展方法
public static class SessionExtensions
{
    public static void Set<T>(this ISession session, string key, T value)
    {
        session.SetString(key, JsonSerializer.Serialize(value));
    }

    public static T Get<T>(this ISession session, string key)
    {
        var json = session.GetString(key);
        return json == null ? default : JsonSerializer.Deserialize<T>(json);
    }
}

// 实体类同方案1

前端代码

<form method="post">
    <div>
        <label>考试名称:</label>
        <input asp-for="NewExam.Name" type="text" required />
    </div>
    <div>
        <label>考试时长(分钟):</label>
        <input asp-for="NewExam.Duration" type="number" min="1" required />
    </div>
    <div>
        <label>选择试题类型:</label>
        <select id="questionType">
            <option value="Open">开放题</option>
            <option value="Choice">选择题</option>
            <option value="Judge">判断题</option>
        </select>
        <button type="submit" formaction="?handler=AddQuestion" formmethod="post">添加试题</button>
    </div>
    <div>
        <h4>已添加的试题</h4>
        @for (int i = 0; i < Model.NewExam.Questions.Count; i++)
        {
            <div class="question-item">
                <input type="hidden" asp-for="NewExam.Questions[i].Type" />
                <div>
                    <label>题目:</label>
                    <input asp-for="NewExam.Questions[i].Content" type="text" required />
                </div>
                @if (Model.NewExam.Questions[i] is ChoiceQuestion choiceQ)
                {
                    for (int j = 0; j < choiceQ.Options.Count; j++)
                    {
                        <div>
                            <label>选项@(j+1):</label>
                            <input asp-for="NewExam.Questions[i].Options[j]" type="text" required />
                        </div>
                    }
                    <button type="button" onclick="addChoiceOption(this, @i)">添加选项</button>
                    <div>
                        <label>正确选项索引:</label>
                        <input asp-for="NewExam.Questions[i].CorrectOptionIndex" type="number" min="0" required />
                    </div>
                }
                @if (Model.NewExam.Questions[i] is JudgeQuestion judgeQ)
                {
                    <div>
                        <label>正确答案:</label>
                        <input type="radio" asp-for="
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 11:55:25