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

时间:2016-08-27 06:11:06

标签: c# jquery ajax asp.net-mvc asp.net-mvc-5

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

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

挑战在于连接用户输入支持。我已经成功地获得了90%的路程,并且遇到了障碍。应用程序负责实例化自己的视图模型。使用一些约定,每个App都有一个关联的局部视图,它位于应用程序命名空间所在的相同路径下。这允许我为应用程序创建一个视图模型,并返回每个应用程序的局部视图,如下所示:

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从服务器返回到客户端时,我不知道如何用它更新主视图。

例如,这是主视图和处理基于视图模型的应用程序和非基于VM的应用程序的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>

如果不需要视图模型,我会将结果填入不可见的行并使行可见。由于View Model应用程序从部分视图提交了表单数据,因此当我从控制器返回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();
}

1 个答案:

答案 0 :(得分:1)

如果要使用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
});