<!DOCTYPE html>
<html>

  <head>
    <base href="." />
    <script type="text/javascript" charset="utf-8">
      window.AngularVersionForThisPlunker = 'latest'
    </script>
    <title>angular playground</title>
    <link rel="stylesheet" href="style.css" />
    <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
    <script src="https://unpkg.com/zone.js/dist/zone.js"></script>
    <script src="https://unpkg.com/zone.js/dist/long-stack-trace-zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </head>

  <body>
    <my-app>
    loading...
  </my-app>
  </body>

</html>
/* Styles go here */

### Angular Starter Plunker - Typescript
var angularVersion = '@4.4.6';

System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for things
  map: {
    
    'app': './src',
    '@angular/core': 'npm:@angular/core'+ angularVersion + '/bundles/core.umd.js',
    '@angular/common': 'npm:@angular/common' + angularVersion + '/bundles/common.umd.js',
    '@angular/compiler': 'npm:@angular/compiler' + angularVersion  + '/bundles/compiler.umd.js',
    '@angular/platform-browser': 'npm:@angular/platform-browser' + angularVersion + '/bundles/platform-browser.umd.js',
    '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic' + angularVersion + '/bundles/platform-browser-dynamic.umd.js',
    '@angular/http': 'npm:@angular/http' + angularVersion + '/bundles/http.umd.js',
    '@angular/router': 'npm:@angular/router' + angularVersion +'/bundles/router.umd.js',
    '@angular/forms': 'npm:@angular/forms' + angularVersion + '/bundles/forms.umd.js',
    '@angular/animations': 'npm:@angular/animations' + angularVersion + '/bundles/animations.umd.js',
    '@angular/platform-browser/animations': 'npm:@angular/platform-browser' + angularVersion + '/bundles/platform-browser-animations.umd.js',
    '@angular/animations/browser': 'npm:@angular/animations' + angularVersion + '/bundles/animations-browser.umd.js',
    
    '@angular/core/testing': 'npm:@angular/core' + angularVersion + '/bundles/core-testing.umd.js',
    '@angular/common/testing': 'npm:@angular/common' + angularVersion + '/bundles/common-testing.umd.js',
    '@angular/compiler/testing': 'npm:@angular/compiler' + angularVersion + '/bundles/compiler-testing.umd.js',
    '@angular/platform-browser/testing': 'npm:@angular/platform-browser' + angularVersion + '/bundles/platform-browser-testing.umd.js',
    '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic' + angularVersion + '/bundles/platform-browser-dynamic-testing.umd.js',
    '@angular/http/testing': 'npm:@angular/http' + angularVersion + '/bundles/http-testing.umd.js',
    '@angular/router/testing': 'npm:@angular/router' + angularVersion + '/bundles/router-testing.umd.js',
    'tslib': 'npm:tslib@1.6.1',
    'rxjs': 'npm:rxjs@5.0.0',
    'typescript': 'npm:typescript@2.2.1/lib/typescript.js'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './main.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});
//main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
//our root app component
import {Component} from '@angular/core'

@Component({
  selector: 'my-app',
  template: `
    <h1>File upload required validation demo</h1>
    <reactive-form></reactive-form>
    <hr/>
    <template-driven-form></template-driven-form>
  `,
})
export class App {
}
import {Directive} from "@angular/core";
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from "@angular/forms";

@Directive({
    selector: "input[type=file]",
    host : {
        "(change)" : "onChange($event.target.files)",
        "(blur)": "onTouched()"
    },
    providers: [
        { provide: NG_VALUE_ACCESSOR, useExisting: FileValueAccessor, multi: true }
    ]
})
export class FileValueAccessor implements ControlValueAccessor {
    value: any;
    onChange = (_) => {};
    onTouched = () => {};

    writeValue(value) {}
    registerOnChange(fn: any) { this.onChange = fn; }
    registerOnTouched(fn: any) { this.onTouched = fn; }
}
import {Directive} from "@angular/core";
import {NG_VALIDATORS, Validator, FormControl} from "@angular/forms";

@Directive({
    selector: "[requireFile]",
    providers: [
        { provide: NG_VALIDATORS, useExisting: FileValidator, multi: true },
    ]
})
export class FileValidator implements Validator {
    static validate(c: FormControl): {[key: string]: any} {
      return c.value == null || c.value.length == 0 ? { "required" : true} : null;
    }

    validate(c: FormControl): {[key: string]: any} {
      return FileValidator.validate(c);
    }
}
import {NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {FormsModule, ReactiveFormsModule} from '@angular/forms'

import {App} from './app'
import {ReactiveFormComponent} from './reactive-form.component'
import {TemplateDrivenFormComponent} from './template-driven-form.component'

import {FileValueAccessor} from './file-control-value-accessor'
import {FileValidator} from './file-input.validator'


@NgModule({
  imports: [ 
    BrowserModule, 
    FormsModule,
    ReactiveFormsModule
  ],
  declarations: [ 
    App,
    ReactiveFormComponent,
    TemplateDrivenFormComponent,
    FileValueAccessor,
    FileValidator
  ],
  bootstrap: [ App ]
})
export class AppModule {}
import {Component} from '@angular/core'
import {FormGroup, FormControl} from '@angular/forms'
import {FileValidator} from './file-input.validator'

@Component({
  selector: 'reactive-form',
  template: `
      <h3>Reactive forms demo</h3>
      
      <form [formGroup]="frm">
        <input type="file" formControlName="file" />
        <p *ngIf="frm.controls.file.errors?.required" style="color: red">This field is required!</p>
        
        <div>
          <button type="submit" [disabled]="!frm.valid">Submit</button>
        </div>
      </form>
  `,
})
export class ReactiveFormComponent {

  frm = new FormGroup({});
  
  constructor() {
    this.buildForm();
  }
  
  private buildForm() {
      this.frm = new FormGroup({
          file: new FormControl("", [FileValidator.validate])
      });
  }
}      
import {Component} from '@angular/core'
import {FormGroup, FormControl} from '@angular/forms'
import {FileValidator} from './file-input.validator'

@Component({
  selector: 'template-driven-form',
  template: `
      <h3>Template driven forms demo</h3>
      
      <form #f="ngForm">
        <input type="file" [ngModel]="fileContent" name="file" #file="ngModel" requireFile/>
        <p *ngIf="!file.valid" style="color: red">This field is required!</p>

        <div>
          <button type="submit" [disabled]="!f.valid">Submit</button>
        </div>
      </form>
  `,
})
export class TemplateDrivenFormComponent {

  fileContent: any;
  
  constructor() {
  }
}