在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">×</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();
}
如果您想用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
});