AngularJS 1.x - Validation on File Input type

Introduction

You might already known that AngularJS 1.x doesn’t support model binding for File Input type at the moment. This means, implementing any validation on File Input type is not straight forward as validation entirely depends on model binding.

I have come across a requirement where, I need to create a form with ‘required’ validation on File Input type and I also need to display bootstrap form feedback. There are number of great solutions out there to make file upload with angular easier but none of them provide easier way to enforce validation.

What I did?

First I have made file input type to look like a button as explained here, and then made it as an add-on to a normal disabled text input as follows

<div class="form-group">
    <label class="col-md-2 control-label" for="photoUrl">Photo</label>
    <div class="col-md-4">
        <div class="input-group">
            <span class="btn btn-primary btn-file input-group-addon">
                Browse <input type="file" id="photoUrlSelector" name="photoUrlSelector" accept="image/*" >
            </span>
            <input class="form-control" type="text" id="photoUrl" name="photoUrl" data-ng-disabled="true">
        </div>
    </div>
</div>

The output looks like this:

Next, I have used ng-file-upload to make file selection update the model and made $touched property of the form field true on mouse click. So,

<span class="btn btn-primary btn-file input-group-addon">
    Browse <input type="file" id="photoUrlSelector" name="photoUrlSelector" accept="image/*">
</span>

is changed as follows

<span class="btn btn-primary btn-file input-group-addon">
    Browse <input type="file" id="photoUrlSelector" name="photoUrlSelector" ngf-select ng-model="vm.author.photoUrl" accept="image/*" data-ng-click="addAuthorForm.photoUrl.$touched=true">
</span>

Then, the first file name is set as model for the text input type as follows, so it shows the selected file

<input class="form-control" type="text" id="photoUrl" name="photoUrl" data-ng-model="vm.author.photoUrl[0].name" data-ng-required="true" data-ng-disabled="true">

as you can see, the input field is also decorated with ng-required directive to enforce required field validation.

Now, until the File Input type is selected, the $touched property will be false and it will become true when user touches it. The user selected file object will be available in the model we used in the File Input type and selected file name will be shown in the text input type.

The File object can be used in your controller to send file to the back end or what ever you want to do with it.

After adding bootstrap feedback, the form group looks like this

<div class="form-group has-feedback" data-ng-class="{'has-error':addAuthorForm.photoUrl.$invalid && addAuthorForm.photoUrl.$touched,
     'has-success':addAuthorForm.photoUrl.$valid && addAuthorForm.photoUrl.$touched}">
    <label class="col-md-2 control-label" for="photoUrl">Photo</label>
    <div class="col-md-4">
        <div class="input-group">
            <span class="btn btn-primary btn-file input-group-addon">
                Browse <input type="file" id="photoUrlSelector" name="photoUrlSelector" ngf-select ng-model="vm.author.photoUrl" accept="image/*" data-ng-click="addAuthorForm.photoUrl.$touched=true">
            </span>
            <input class="form-control" type="text" id="photoUrl" name="photoUrl" data-ng-model="vm.author.photoUrl[0].name" data-ng-required="true" data-ng-disabled="true">
            <span data-ng-if="addAuthorForm.photoUrl.$invalid && addAuthorForm.photoUrl.$touched" class="glyphicon glyphicon-remove form-control-feedback"></span>
            <span data-ng-if="addAuthorForm.photoUrl.$valid && addAuthorForm.photoUrl.$touched" class="glyphicon glyphicon-ok form-control-feedback"></span>

        </div>
    </div>
    <span class="help-block">
        <span data-ng-show="addAuthorForm.photoUrl.$error.required &amp;&amp; addAuthorForm.photoUrl.$touched">The Photo is required field.</span>
    </span>
</div>

Output with validation error looks like this

and success looks like this

Conclusion

This is not perfect but it does the job. We could also create a directive to hide away all the messy details but that is not necessary until you need to use this in many places in your application.

Please let me know if you know a better way to achieve this.

comments powered by Disqus