For a simple form I don't want to maintain a whole temp directory system for async uploads through most VueJs components. I just want vue to manage the state of my submit button, maybe some basic client validation as well. But I want a regular form postback with the input type="file" browser component.

The tricky part is you can't bind it to a v-model, vue will throw console errors (I mean otherwise it seems to "work" in that it's storing something).

So my solution is to hook into the change event and just store the filename in vue... the posted file itself can live native in the input, but now we should be able to inform our client logic.

So lets start with the markup, please feel free to swap out the Razor markup for whatever your platform of choice is (Note the @@ should just be a @ for non-asp.net, it's just an escape)

<div id="upload-test">
    @if (TempData["UploadError"] != null)
    {
        <div class="error">
            @Html.Raw(TempData["UploadError"])
        </div>
    }

<form action="/UploadFile/" enctype="multipart/form-data" method="post"> <input type="file" id="myFile" name="file" required="required" @@change="onSelectFile()" ref="fileInput" />

 &lt;input type="hidden" name="returnUrl" value="@HttpContext.Current.Request.Url.PathAndQuery" /&gt;
 &lt;button type="submit" class="btn btn-primary" :disabled="hasInvalidFile"&gt;Upload&lt;/button&gt;

</form> </div>

upload-test.jsView on GitHub
var $myWidget = new Vue({
    el: '#upload-test',
    name: "UploadTest",
    data: {
        file: null
    },
    created: function () {

},
mounted: function () {
    var $that = this;

},
methods: {
    onSelectFile: function (e) {
        console.log(this.$refs.fileInput, e);
        this.file = this.$refs.fileInput.files[0].name;
    }
},
computed: {
    hasInvalidFile: function () {
        return (this.file == null) ? true : false;
    },
    extension: function () {
        if (this.file != null) {
            return '.' + this.file.split('.').pop();
        } else {
            return "";
        }
    }
},
watch: {

}

});

  • So very simply as stated. When a file is picked with the native browser UI, vuejs triggers the change event into onSelectFile().
  • The Vue method gets the input reference, gets the first file, and stores it's name into Vue.
  • Now the computed method "hasInvalidFile" is able to handle the disabled state of the button.

Now we have Vue Validation, with the simplicity of a postback!

Clearly you could go further to check allowed extensions (left in the helper), but this just a basic example for clarity.

If you ARE a .net developer, here's the stub you can use for the Controllers HttpPost

HandlerCSharp.csView on GitHub
        [HttpPost]
        public ActionResult UploadFile(HttpPostedFileBase file, string returnUrl)
        {
            if (file == null)
            {
                TempData["UploadError"] = "File Error";
                return RedirectPermanent(returnUrl);
            }

        try
        {
            var path = Util.MapPath(filePath);
            file.SaveAs(path);
            
        }catch(Exception ex)
        {
            TempData["UploadError"] = ex.Message;
        }

        return RedirectPermanent(returnUrl);
    }</code></pre></div>

Happy Coding!