当Post操作已用于文件上载时,如何将viewModel回发到控制器

时间:2013-01-23 23:01:39

标签: jquery asp.net-mvc

嗨,我有一个像这样的控制器:

public class MyController : Controller
    {
        public MyController(Uow uow)
        {
            ;
        }

        public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Index(HttpPostedFileBase file)
        {
            var validationResults = _uow.GetResults(file.FileName, file.InputStream);

            return View(new MyViewModel { Errors = validationResults.Errors, Data = validationResults.Data });
        }

        [HttpPost]
        public void Save(MyViewModel viewModel)
        {
            //Save the data
        }
    }

这有一个类似的观点:

@model MyViewModel 

<form action="" method="post" enctype="multipart/form-data">

    <label for="file" id="filelabel">Filename:</label>
    <input type="file" name="file" id="file" />

    <input type="submit" id="submitbtn" disabled="disabled" />
    <img class="loader" id="loader" style="display: none;" alt="Loading..." src="@Url.Content("~/Content/Images/ajax-loader.gif")" />

    @if (Model != null && Model.Errors.Any())
    {
        foreach (var error in Model.Errors)
        {
            <span class="error">@error</span>
        }    
    }

    <button id="savebtn" >Save</button>

</form>


<script type="text/javascript">

    $(document).ready(function () {

        $('#file').change(function () {
            alert("in here");
            $('#submitbtn').prop('disabled', false);

        });

        $('#submitbtn').click(function () {

            $('#loader').show();

        });

    });
</script>

所以我在这里尝试的是将文件上传到控制器的方法。我已经实现了这一点,并且我在Index Post中收到了它。然后我处理这个工作正常。然后我将相关数据放在viewModel中并重新显示表单。

我想要的是当按下Save按钮时,使用填充的viewModel调用Save Post方法。然而,每当我按下按钮时,它都会发布到Index Post,这是有道理的。

有人可以告诉我如何保留我的文件上传和处理代码,但是当我稍后按下Save按钮时,viewModel会被发送到Save Post方法吗?

1 个答案:

答案 0 :(得分:0)

如果您必须将这些操作分开并且不要更改当前的表单,那么您需要一个单独的表单才能异步发布或不发布到其他操作...这意味着做一些令人讨厌的事情,比如更新另一个中的隐藏字段每当对第一个表单进行更改时使用javascript进行表单...由于表单字段可以在不触发某些javascript事件的情况下进行更改的多种方式,因此不是万无一失的。

当单击保存按钮时,还可以使用javascript更改表单操作..但说实话,这对我来说似乎太脏了,我会使用viewmodel来实现1动作。

我建议您在视图模型上使用HttpPostedFileBase并让Index操作处理验证和保存数据。

有关强类型表单助手和注释的详细信息,请参阅此示例:how to validate input file with jquery and dataannotation in asp.net mvc 3

它显示HttpPostedFileBase属性DataAnnotations,您可能想尝试编写一个自定义注释,如果您需要以这种方式验证它,则会调用您的UOW ..但不确定为什么呢?

我也建议使用Model.ValidationMessageFor()帮助器。

接收视图模型的操作的一般模式如下所示:

[HttpPost]
public ActionResult Index(MyViewModel myViewModel)
{
    try
    {
        // Check your viewmodel is valid
        if (!ModelState.IsValid)
        {
            // not valid so return to the view with the view model as a param
            // The validation message helpers will display the relevant error messages
            return View(myViewModel);
        }

        // modelstate is valid so perform any work i.e. save to database
        // attach any additional information to the view modal to give feedback maybe?
        // or redirect the user to some other page
        return View(myViewModel);
    }
    catch
    {
        // any exceptions then go back to the view and hopefully the validation helpers will kick in
        return View(myViewModel);
    }
}

就个人而言,我会做这类事情,但你可以得到这个想法:

在视图模型中

[DisplayName("Update your image")]
[Required]
public HttpPostedFileBase Image { get; set; }
public string ImageName { get; set; }

在视图中

@if (Model.ImageName != null) { 
    <img src="@Url.Content("~/uploads/" + Model.ImageName)" alt="" />
}

<div class="control-group">
    @Html.LabelFor(model => model.Image)
    <div class="controls">
        @Html.TextBoxFor(model => model.Image, new { @type = "file", @class = "input-file" })
        @Html.ValidationMessageFor(model => model.Image)
    </div>
</div>

在控制器发布操作中,检查ModelState.IsValid

var fileName = "";
// check for a posted file and that it has a size to it before attempting to save the file
if (myViewModel.Image != null && myViewModel.Image.ContentLength > 0)
{
    var currentDate = DateTime.UtcNow.ToString("yyyymmddhhmmss");
    var fileType = Path.GetExtension(myViewModel.Image.FileName);
    Random random = new Random();
    fileName = Path.GetFileNameWithoutExtension(myViewModel.Image.FileName) + "-" + currentDate + random.Next(1, 10000) + fileType;

    // upload the file
    var path = Path.Combine(Server.MapPath("~/uploads"), fileName);
    myViewModel.Image.SaveAs(path);
}

// for saving to the database on an model entity
if (fileName != "")
{
    entity.ImageName = fileName;
}