<p dir="auto"><strong>I'm submitting a ...</strong>  (check one with "x")</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="[X ] bug report =&gt; search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request =&gt; Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question"><pre class="notranslate"><code class="notranslate">[X ] bug report =&gt; search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request =&gt; Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
</code></pre></div>
<p dir="auto"><strong>Current behavior</strong><br>
Changing value of enabled <code class="notranslate">&lt;input type="file"&gt;</code> doesn't trigger any validators. Only need to change type to "text" and everything is working as expected.</p>
<p dir="auto"><strong>Expected behavior</strong><br>
Changing value of <code class="notranslate">&lt;input type="file"&gt;</code> should trigger validators.</p>
<p dir="auto"><strong>Minimal reproduction of the problem with instructions</strong><br>
View</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="&lt;md-card&gt;
  &lt;md-card-title&gt;Audio test creator&lt;/md-card-title&gt;
  &lt;md-card-content&gt;
    &lt;form [formGroup]=&quot;creationForm&quot; (ngSubmit)=&quot;onSubmit(creationForm)&quot;&gt;

      &lt;select (change)=&quot;onChange($event.target.value)&quot; class=&quot;custom-select&quot;      formControlName=&quot;method&quot;&gt;
        &lt;option value=&quot;&quot; selected disabled&gt;Test method&lt;/option&gt;
        &lt;option *ngFor=&quot;let testMethod of testMethods&quot; [value]=&quot;testMethod.value&quot;&gt;
          {{testMethod.display}}
        &lt;/option&gt;
      &lt;/select&gt;

      &lt;md-input placeholder=&quot;Test name&quot; type=&quot;text&quot; formControlName=&quot;name&quot;&gt;&lt;/md-input&gt;

      &lt;div [formGroup]=&quot;abxForm&quot; &gt;
        &lt;input type=&quot;file&quot; formControlName=&quot;soundA&quot; (change)=&quot;onInputChange($event.target.value)&quot;&gt;
      &lt;/div&gt;

      &lt;button type=&quot;submit&quot; [disabled]=&quot;!creationForm.valid&quot;&gt;Submit&lt;/button&gt;
    &lt;/form&gt;

  &lt;/md-card-content&gt;
&lt;/md-card&gt;
"><pre class="notranslate"><code class="notranslate">&lt;md-card&gt;
  &lt;md-card-title&gt;Audio test creator&lt;/md-card-title&gt;
  &lt;md-card-content&gt;
    &lt;form [formGroup]="creationForm" (ngSubmit)="onSubmit(creationForm)"&gt;

      &lt;select (change)="onChange($event.target.value)" class="custom-select"      formControlName="method"&gt;
        &lt;option value="" selected disabled&gt;Test method&lt;/option&gt;
        &lt;option *ngFor="let testMethod of testMethods" [value]="testMethod.value"&gt;
          {{testMethod.display}}
        &lt;/option&gt;
      &lt;/select&gt;

      &lt;md-input placeholder="Test name" type="text" formControlName="name"&gt;&lt;/md-input&gt;

      &lt;div [formGroup]="abxForm" &gt;
        &lt;input type="file" formControlName="soundA" (change)="onInputChange($event.target.value)"&gt;
      &lt;/div&gt;

      &lt;button type="submit" [disabled]="!creationForm.valid"&gt;Submit&lt;/button&gt;
    &lt;/form&gt;

  &lt;/md-card-content&gt;
&lt;/md-card&gt;

</code></pre></div>
<p dir="auto">Component class</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="export class SoundTestCreatorComponent implements OnInit {

  readonly ABX_SECTION = &quot;abxForm&quot;;

  abxForm: FormGroup;
  creationForm : FormGroup;

  testMethods = [
    {value: SoundTestEnum[SoundTestEnum.ABX], display: SoundTestEnum[SoundTestEnum.ABX]},
    {value: SoundTestEnum[SoundTestEnum.MUSHRA], display: SoundTestEnum[SoundTestEnum.MUSHRA]}
  ];

  constructor(private fb: FormBuilder) {
    this.creationForm = this.fb.group({
      &quot;name&quot;: [&quot;&quot;, Validators.required],
      &quot;method&quot;: [&quot;&quot;, Validators.required],
    });

    this.abxForm = this.fb.group({
      &quot;soundA&quot;: [&quot;&quot;, CustomValidators.fileInputRequired]
    });
  }

  ngOnInit() { }

  onSubmit(creationForm : any) {
    console.log('Test creation form submitted');
    console.log(creationForm);
  }

  onChange(selectedMethod : string) {
    this.removeFormSections();
    if(selectedMethod === SoundTestEnum[SoundTestEnum.ABX]) {
      this.addAbxFormSection();
    }
    if(selectedMethod === SoundTestEnum[SoundTestEnum.MUSHRA]) {
    }
    console.log(selectedMethod);
  }

  private addAbxFormSection() {
    this.creationForm.addControl(this.ABX_SECTION, this.abxForm);
  }

  private removeFormSections() {
      this.creationForm.removeControl(this.ABX_SECTION);
  }

  onInputChange(val) {
    console.log(val);
    console.log(this.abxForm.enabled);
  }"><pre class="notranslate"><code class="notranslate">export class SoundTestCreatorComponent implements OnInit {

  readonly ABX_SECTION = "abxForm";

  abxForm: FormGroup;
  creationForm : FormGroup;

  testMethods = [
    {value: SoundTestEnum[SoundTestEnum.ABX], display: SoundTestEnum[SoundTestEnum.ABX]},
    {value: SoundTestEnum[SoundTestEnum.MUSHRA], display: SoundTestEnum[SoundTestEnum.MUSHRA]}
  ];

  constructor(private fb: FormBuilder) {
    this.creationForm = this.fb.group({
      "name": ["", Validators.required],
      "method": ["", Validators.required],
    });

    this.abxForm = this.fb.group({
      "soundA": ["", CustomValidators.fileInputRequired]
    });
  }

  ngOnInit() { }

  onSubmit(creationForm : any) {
    console.log('Test creation form submitted');
    console.log(creationForm);
  }

  onChange(selectedMethod : string) {
    this.removeFormSections();
    if(selectedMethod === SoundTestEnum[SoundTestEnum.ABX]) {
      this.addAbxFormSection();
    }
    if(selectedMethod === SoundTestEnum[SoundTestEnum.MUSHRA]) {
    }
    console.log(selectedMethod);
  }

  private addAbxFormSection() {
    this.creationForm.addControl(this.ABX_SECTION, this.abxForm);
  }

  private removeFormSections() {
      this.creationForm.removeControl(this.ABX_SECTION);
  }

  onInputChange(val) {
    console.log(val);
    console.log(this.abxForm.enabled);
  }
</code></pre></div>
<p dir="auto">CustomValidator to check calls.</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="function isEmptyInputValue(value: any) {
  return value == null || typeof value === 'string' &amp;&amp; value.length === 0;
}

export class CustomValidators {
  static fileInputRequired(c: FormControl) {
    return (isEmptyInputValue(c.value)) ? {
      fileInputRequired: {
        valid: false
      }
    } : null;
  }
}"><pre class="notranslate"><code class="notranslate">function isEmptyInputValue(value: any) {
  return value == null || typeof value === 'string' &amp;&amp; value.length === 0;
}

export class CustomValidators {
  static fileInputRequired(c: FormControl) {
    return (isEmptyInputValue(c.value)) ? {
      fileInputRequired: {
        valid: false
      }
    } : null;
  }
}
</code></pre></div>
<ul dir="auto">
<li><strong>Angular version:</strong> 2.1</li>
<li><strong>Browser:</strong>  ALL</li>
<li><strong>Language:</strong> [ TypeScript 2.0.2 ]</li>
<li><strong>Node (for AoT issues):</strong> <code class="notranslate">node --version</code> =   6.0.0</li>
</ul>
<p dir="auto"><strong>I'm submitting a ...</strong>  (check one with "x")</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="[x] bug report =&gt; search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request =&gt; Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question"><pre class="notranslate"><code class="notranslate">[x] bug report =&gt; search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request =&gt; Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
</code></pre></div>
<p dir="auto">I am submitting this as a bug report but it may turn out to be a general limitation in how Angular apps can be built and bundled. Given that there is <a href="https://github.com/systemjs/builder/issues/739" data-hovercard-type="issue" data-hovercard-url="/systemjs/builder/issues/739/hovercard">a similar issue posted on the SystemJS-Builder issue tracker</a> my use case may not be so unique and of interest to a wider community.</p>
<p dir="auto"><strong>Use Case and Current behavior</strong><br>
Consider an NgModule <code class="notranslate">LazyFeatureModule</code>. The feature implements a workflow with a few View Components that may be used in multiple applications.</p>
<p dir="auto">It was transpiled against Angular 2.4.6 and bundled with <a href="http://rollupjs.org" rel="nofollow">Rollup</a>. The entry file to Rollup is <code class="notranslate">foo-feature/index.js</code> which only exports <code class="notranslate">LazyFeatureModule</code>. From this Rollup generates <code class="notranslate">foo-feature/bundle.js</code> which contains all the dependencies required by <code class="notranslate">LazyFeatureModule</code> - including all (ES2015-imported) Angular dependencies - and which eventually exports <code class="notranslate">LazyFeatureModule</code> again. <code class="notranslate">bundle.js</code> was meant to become a pre-bundled Angular module shipped with an npm-package, such that applications which lazily load the bundle don't need to be rebuilt when updating the feature. They should be fine with running an <code class="notranslate">npm update foo-feature</code> on their side.</p>
<p dir="auto">The application which aims to incorporate the pre-bundled feature has a routing configuration which lazily loads the feature like so:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{path: 'my-foo-feature', loadChildren: './node_modules/foo-feature/bundle.js#LazyFeatureModule'}"><pre class="notranslate"><span class="pl-kos">{</span><span class="pl-c1">path</span>: <span class="pl-s">'my-foo-feature'</span><span class="pl-kos">,</span> <span class="pl-c1">loadChildren</span>: <span class="pl-s">'./node_modules/foo-feature/bundle.js#LazyFeatureModule'</span><span class="pl-kos">}</span></pre></div>
<p dir="auto">The bundle gets loaded, so the paths are correct, but I get an error stating <code class="notranslate">No NgModule metadata found for LazyFeatureModule</code>.</p>
<p dir="auto">Rollup builds the feature-bundle without warnings and the export produced by Rollup actually looks quite good. These are the last few lines in <code class="notranslate">bundle.js</code>:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// ...
exports.LazyFeatureModule = (function () {
    function LazyFeatureModule() {
    }
    return LazyFeatureModule;
}());
exports.LazyFeatureModule = __decorate([
    NgModule({
        imports: [
            CommonModule,
            RouterModule.forChild(ROUTES)
        ],
        declarations: [
            HelloView
        ]
    })
], exports.LazyFeatureModule);"><pre class="notranslate"><span class="pl-c">// ...</span>
<span class="pl-s1">exports</span><span class="pl-kos">.</span><span class="pl-c1">LazyFeatureModule</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-k">function</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">function</span> <span class="pl-v">LazyFeatureModule</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-kos">}</span>
    <span class="pl-k">return</span> <span class="pl-v">LazyFeatureModule</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-s1">exports</span><span class="pl-kos">.</span><span class="pl-c1">LazyFeatureModule</span> <span class="pl-c1">=</span> <span class="pl-en">__decorate</span><span class="pl-kos">(</span><span class="pl-kos">[</span>
    <span class="pl-v">NgModule</span><span class="pl-kos">(</span><span class="pl-kos">{</span>
        <span class="pl-c1">imports</span>: <span class="pl-kos">[</span>
            <span class="pl-v">CommonModule</span><span class="pl-kos">,</span>
            <span class="pl-v">RouterModule</span><span class="pl-kos">.</span><span class="pl-en">forChild</span><span class="pl-kos">(</span><span class="pl-c1">ROUTES</span><span class="pl-kos">)</span>
        <span class="pl-kos">]</span><span class="pl-kos">,</span>
        <span class="pl-c1">declarations</span>: <span class="pl-kos">[</span>
            <span class="pl-v">HelloView</span>
        <span class="pl-kos">]</span>
    <span class="pl-kos">}</span><span class="pl-kos">)</span>
<span class="pl-kos">]</span><span class="pl-kos">,</span> <span class="pl-s1">exports</span><span class="pl-kos">.</span><span class="pl-c1">LazyFeatureModule</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">So LazyFeatureModule is actually decorated with NgModule metadata and exported.  I could track down the problem and the reason seems to be that when Metadata is resolved, a function</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content=" function _isNgModuleMetadata(obj) {
    return obj instanceof NgModule;
  }
"><pre class="notranslate"><code class="notranslate"> function _isNgModuleMetadata(obj) {
    return obj instanceof NgModule;
  }

</code></pre></div>
<p dir="auto">is called and returns <em>false</em>.</p>
<p dir="auto">Above function is included in both bundles. When the error happens it is called inside of <em>app-bundle</em> but <code class="notranslate">obj</code> is a <code class="notranslate">DecoratorFactory</code> of the lazy-loaded feature bundle. Since the feature bundle has been bundled separately, there's not only one but two<code class="notranslate">NgModule</code> definitions, one in <em>app-bundle</em> and one in <em>feature-bundle</em>. So we got two bundles with two <code class="notranslate">NgModule</code> class definitions. Therefore I suspect that when above code in <em>app-bundle</em> tests a <code class="notranslate">DecoratorFactory</code>  of <em>feature-bundle</em> it will test against a different NgModule constructor function reference and therefore returns <em>false</em> for the feature bundle.</p>
<p dir="auto">As "fix" for the problem at hand I could suggest that above function replaces a hard <em>instanceof</em> check for a test against a special property which indicates that <code class="notranslate">obj</code> is a valid NgModule (something which TypeScript Reference calls <em>Discriminant Properties</em>). However I wonder if my intended use case is possible at all or whether code duplication may also affect dependency injection (which compares against constructor references).</p>
<p dir="auto">One of the motivations of shipping <em>pre-bundled</em> features was to enable apps to npm-update the feature without the need for a complete rebuild and to ease the build configuration for applications incorporating the feature.</p>
<p dir="auto">However, as of today <em>deduplication</em> seems to be a strong requirement when building apps out of multiple chunks (multiple chunks are a logical consequence when aiming for lazy-loading). Now the questions which remain for me:</p>
<ul dir="auto">
<li>Do you consider this issue a bug or did Angular never intend to support the given use case?</li>
<li>Do you agree with the analysis given in the issue or may I am wrong? If code duplication is a problem should <em>deduplication</em> be mentioned in guides on bundling Angular apps?</li>
</ul>
<p dir="auto"><strong>Angular version:</strong> 2.4.6<br>
... bundles use JIT compiler at the moment</p>
<p dir="auto"><strong>Please tell us about your environment:</strong></p>
<ul dir="auto">
<li>rollup@0.41.4</li>
<li>rollup-plugin-commonjs@7.0.0</li>
<li>rollup configured with<br>
format: <code class="notranslate">"iife"</code> for app-bundle<br>
format: <code class="notranslate">"cjs"</code> for feature-bundle</li>
<li>gulp 3.9.1</li>
</ul>
<ul dir="auto">
<li><strong>Browser:</strong> [all]</li>
<li><strong>Language:</strong> [TypeScript | ES6 | ES5]</li>
</ul>