在ajax呈现部分视图后更新主视图

本文关键字:更新 主视图 视图 ajax 现部 | 更新日期: 2023-09-27 18:04:21

我有一个用户可以运行的"应用程序"列表。每个应用程序都针对我们的特定API来演示API的功能。其中一些应用程序需要用户输入,这样我就可以将用户给定的参数传递到API中。

每个应用程序都负责生成代表其输出的HTML。对于不需要任何输入的应用程序,这是一个直接的过程,从ajax请求在控制器/操作中执行它们,并使用输出更新视图。

挑战在于连接用户输入支持。我已经走了90%的路,却遇到了一个障碍。应用程序负责实例化它们自己的视图模型。使用一点惯例,每个应用程序都有一个关联的部分视图,该视图位于应用程序名称空间所在的相同路径下。这让我可以为应用程序创建视图模型,并像这样为每个应用程序返回部分视图:

public ActionResult GetViewModel(string appId)
{
    IApp app = AppFactory.GetAppById(appId);
    string path = app.GetType().FullName.Replace('.', '/');
    return PartialView($"~/Views/{path}.cshtml", app.CreateViewModel());
}

使用应用程序提供的视图模型的部分视图示例如下:

@using Examples.DataAccess.Query;
@model Query_02_ParameterizedQueryViewModel
@using (@Html.BeginForm("RunAppFromViewModel", "Home", FormMethod.Post))
{
    @Html.ValidationSummary(true)
    <fieldset>
        <div class="form-inline">
            <div class="form-group">
                @Html.LabelFor(viewModel => viewModel.City)
                @Html.EditorFor(viewModel => viewModel.City, new { placeholder = "Phoenix" })
                @Html.ValidationMessageFor(viewModel => viewModel.City)
                @Html.HiddenFor(viewModel => viewModel.AppId)
            </div>
        </div>
        <button class="btn btn-default" type="submit">Run</button>
    </fieldset>
}

主视图有一个打开模态引导对话框的按钮。当对话框打开时,我向服务器发出一个ajax请求,以获取视图模型和部分视图。然后,我将部分视图插入模态对话框,并更新客户端验证,使其与不引人注目的东西一起工作。问题是,然而,当表单被张贴回服务器,并从应用程序输出的HTML从服务器返回到客户端,我不知道如何更新主视图与它。

例如,这是主视图和JavaScript,处理基于视图模型的应用程序和非基于虚拟机的应用程序。

@using Examples.Browser.ViewModels;
@using Examples.Browser.Models;
@{
    ViewBag.Title = "Home Page";
}
@model List<ApiAppsViewModel>
<div class="jumbotron">
    <h1>Framework API Micro-Apps</h1>
    <p class="lead">Micro-apps provide a way to execute the available APIs in the Framework, without having to write any code, and see the results.</p>
</div>
<div class="container">
    <h3 class="text-center">Available API Apps</h3>
    <div class="table-responsive">
        <table class="table table-hover table-responsive">
            <tr>
                <th>@nameof(ApiApp.Components)</th>
                <th>@nameof(ApiApp.Name)</th>
                <th>@nameof(ApiApp.Description)</th>
                <th>Options</th>
            </tr>
            @foreach (ApiAppsViewModel app in this.Model)
            {
                <tr>
                    <td>@string.Join(",", app.Components)</td>
                    <td>@app.Name</td>
                    <td>@app.Description</td>
                    <td>
                        @if (app.IsViewModelRequired)
                        {
                            <button type="button"
                                    data-app="@app.Id.ToString()"
                                    data-vm-required="@app.IsViewModelRequired"
                                    data-app-name="@app.Name"
                                    data-toggle="modal"
                                    data-target="#appParameters"
                                    class="btn btn-success">
                                Run App
                            </button>
                        }
                        else
                        {
                            <button type="button"
                                    data-app="@app.Id.ToString()"
                                    data-vm-required="@app.IsViewModelRequired"
                                    class="btn btn-success">
                                Run App
                            </button>
                        }
                    </td>
                </tr>
                <tr class="hidden">
                    <td colspan="4">
                        <div class="container alert alert-info" data-app="@app.Id.ToString()">
                        </div>
                    </td>
                </tr>
            }
        </table>
    </div>
</div>

<div class="modal fade"
     id="appParameters"
     role="dialog"
     aria-labelledby="appParametersLabel">
    <div class="modal-dialog"
         role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title" id="appParametersLabel"></h4>
            </div>
            <div class="modal-body" id="appDialogBody">
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript">
    $('.btn-success').click(function () {
        var button = $(this);
        var appId = $(this).data("app");
        var vmRequired = $(this).data("vm-required");
        if (vmRequired == "False") {
            var url = "/Home/RunApp?appId=" + appId;
            $.get(url, function (data) {
                $("div[data-app='" + appId + "']").html(data);
                var buttonColumn = button.parent();
                var appRow = buttonColumn.parent();
                var hiddenRow = appRow.next()
                hiddenRow.removeClass("hidden");
                appRow.click(function () {
                    var hiddenColumn = hiddenRow.children().first();
                    var resultsDiv = hiddenColumn.children().first();
                    resultsDiv.empty();
                    hiddenRow.addClass("hidden");
                    $(this).off();
                    hiddenRow.off();
                })
                hiddenRow.click(function () {
                    var hiddenColumn = $(this).children().first();
                    var resultsDiv = hiddenColumn.children().first();
                    resultsDiv.empty();
                    $(this).addClass("hidden");
                    appRow.off();
                    $(this).off();
                })
            });
        } else {
            var appName = $(this).data("app-name");
            $('#appParametersLabel').html(appName);
            var url = "/Home/GetViewModel?appId=" + appId;
            $.get(url, function (data) {
                $('#appDialogBody').html(data);
                var dialog = $('#appDialogBody');
                $.validator.unobtrusive.parse(dialog);
            });
            $('#appParameters').modal({
                keyboard: true,
                backdrop: "static",
                show: false,
            }).on('show', function () {
            });
        }
    });
</script>

当不需要视图模型时,我将结果塞进不可见的行中,并使该行可见。由于视图模型应用程序的表单数据是从部分视图提交的,所以当我从控制器返回HTML时,它会将其作为原始文本呈现给浏览器。我想我可以写一些java脚本来处理这个问题,但我不确定那会是什么样子。我如何从部分视图中获得表单帖子,将它生成的HTML返回到主视图中不可见的行?

这是表单发送和返回的控制器动作,以及非基于视图模型的应用程序用来运行它们的应用程序和生成HTML的控制器动作。

[HttpGet]
public async Task<string> RunApp(string appId)
{
    IApp app = AppFactory.GetAppById(appId);
    if (app == null)
    {
        return "failed to locate the app.";
    }
    IAppOutput output = await app.Run();
    if (output == null)
    {
        return "Failed to locate the app.";
    }
    return output.GetOutput();
}
[HttpPost]
public async Task<string> RunAppFromViewModel(FormCollection viewModelData)
{
    IApp app = AppFactory.GetAppById(viewModelData["AppId"]);
    foreach(PropertyInfo property in TypePool.GetPropertiesForType(app.ViewModel.GetType()))
    {
        property.SetValue(app.ViewModel, viewModelData[property.Name]);
    }
    IAppOutput output = await app.Run();
    return output.GetOutput();
}

在ajax呈现部分视图后更新主视图

如果您想用RunAppFromViewModel()方法返回的数据更新现有页面,那么您需要使用ajax提交表单。由于表单是在加载初始页面后动态加载的,因此需要使用事件委托。您还需要存储希望在加载表单时更新的元素。

var hiddenRow;
$('.btn-success').click(function () {
    // store the element to be updated
    hiddenRow = $(this).closest('tr').next('tr').find('.container');
    ....
});
// Handle the submit event of the form
$('#appDialogBody').on('submit', 'form', function() {
    // check if the form is valid
    if (!$(this).valid())
    {
        return;
    }
    var formData = $(this).serialize(); // serialize the forms controls
    var url = '@Url.Action("RunAppFromViewModel", "Home");
    $.post(url, formData , function(response) {
        hiddenRow.html(response); // assumes your returning html
    });
    return false; // cancel the default submit
});