For those who don’t use / like that much Material Design, we still can do some Bootstrap … right ?

I like to use ngBootstrap for components that require jQuery in the original Bootstrap distribution. The good thing is you don’t have to import Bootstrap javascript files, and keep doing Angular code. (I really don’t need to say that we don’t want to mix Angular app with any other javascript lib, but sometimes I have to say it when faced with projects demands …)

Ok, now I came across a nice Bootstrap component : Custom Input File.

It’s a nice one, but since it’s an overlay on top of native HTML input type, there is a problem : the name of the file(s) is not filled when you select … a file. The issue is described here : bootstrap file browser.

The jQuery code that would fill the name of the files would look like that :

$('.custom-file input').change(function (e) {
var files = [];
for (var i = 0; i < $(this)[0].files.length; i++) {
files.push($(this)[0].files[i].name);
}
$(this).next('.custom-file-label').html(files.join(', '));
});

Now with a very simple @ViewChild annotation, we will do the same in Angular in a reactive form :

formImport: FormGroup;
fileToUpload: File = null;constructor() {
this.formImport = new FormGroup({
importFile: new FormControl('', Validators.required)
});
}

The HTML code looks something like that :

<form novalidate [formGroup]="formImport">
<div class="input-group mb-3">

<div class="custom-file">
<input type="file" class="custom-file-input" multiple formControlName="importFile" id="importFile" (change)="onFileChange($event.target.files)">
<label class="custom-file-label" #labelImport for="importFile"><i class="fas fa-search"></i> Choose file</label>
</div>
</div>
<div class="form-group">
<button class="btn btn-outline-secondary" type="button" (click)="import()"><i class="fas fa-file-import"></i> Importer
</button>
</div>
</form>

And in the component, we declare and use the DOM element #labelImport :

@ViewChild('labelImport')
labelImport: ElementRef;

When the input is modified, with (change) event, it does send a File list, that is types FileList. This is not a Javascript Array, but it can be casted as such with Array.from(). No need to remind the reason of the list : the “multiple” option that we can place on the input.

Then we simply fill the text of the ViewChild :

onFileChange(files: FileList) {
this.labelImport.nativeElement.innerText = Array.from(files)
.map(f => f.name)
.join(', ');
this.fileToUpload = files.item(0);
}

And … well this is it 🙂

Of course there is probably a PR on the ngBootstrap project to implement this as a component, or not. I like how Bootstrap is still very easy to work with, that’s one reason I don’t choose Material in some cases.

Here is a working — I hope — stackblitz.

https://stackblitz.com/edit/angular-custom-file-input?embed=1&file=src/app/app.component.ts