angular icon indicating copy to clipboard operation
angular copied to clipboard

Angular Pipes not working when used inside ContentChildren

Open janvladimirmostert opened this issue 5 years ago • 1 comments

Dart Version, have tried on 2.7.2 and 2.8.0-dev-20

environment:
    sdk: ">=2.7.0 <3.0.0"

AngularDart Version: 5.3.1

dependencies:
    angular: ^5.3.1

I ran into an issue with pipes which appears to be an Angular bug unless i'm using it wrong?

To reproduce the issue, I've defined a simple pipe that simply appends a random number to an input:

@Pipe("RandomPipe10")
class RandomPipe10 extends PipeTransform {
  String transform(String input) {
    return input + Random().nextInt(10).toString();
  }
}

Then i defined a simple component to confirm that the pipe on its own works:

@Component(
  selector: "bug-pipe-test",
  // language=HTML
  template: """
      Testing Pipe inside ContentChildren
      <br />
      This works: {{ 'blah1' | RandomPipe10 }}
	""",
  directives: const [
    BugPipeTestChild,
    NgFor,
    NgTemplateOutlet,
  ],
  pipes: const [
    RandomPipe10,
  ],
)
class BugPipeTest {}

Which correctly outputs

Testing Pipe inside ContentChildren
This works: blah19

Now let's define a directive that we can use to add components with:

@Directive(selector: "[contentDirective]")
class ContentDirective {
  final TemplateRef template;

  ContentDirective(this.template) {
    template.createEmbeddedView();
  }
}

and let's define a second component:

@Component(
  selector: "bug-pipe-test-child",
  // language=HTML
  template: """
    <hr>
    <template ngFor let-item [ngForOf]="contentItems ?? []">
        <div class="flex-grow:0 dark">
            <template [ngTemplateOutlet]="item.template">
            </template>
            This works: {{ 'blah' | RandomPipe10 }}
        </div>
    </template>
    <hr>
  """,
  directives: const [
    NgFor,
    NgTemplateOutlet,
    ContentDirective,
  ],
  pipes: const [
    RandomPipe10,
  ],
)
class BugPipeTestChild {
  @Input()
  @ContentChildren(ContentDirective)
  List<ContentDirective> contentItems = <ContentDirective>[];
}

If i now update the first component to render this component, the pipe still renders correctly:

@Component(
  selector: "bug-pipe-test",
  // language=HTML
  template: """
      Testing Pipe inside ContentChildren
      <br />
      This works: {{ 'blah1' | RandomPipe10 }}
      <bug-pipe-test-child>
          <div *contentDirective>
             BLAH
          </div>
          <div *contentDirective>
            BLAH
          </div>
      </bug-pipe-test-child>
	""",
  directives: const [
    BugPipeTestChild,
    NgFor,
    NgTemplateOutlet,
    ContentDirective,
  ],
  pipes: const [
    RandomPipe10,
  ],
)
class BugPipeTest {}

It correctly renders

Testing Pipe inside ContentChildren
This works: blah13
---------------------------------
BLAH
This works: blah2
BLAH
This works: blah6
---------------------------------

This also outputs the correct output similar to the above obviously with different random numbers:

      <bug-pipe-test-child>
          <div *contentDirective>
             {{ 'BLAH' }}
          </div>
          <div *contentDirective>
             {{ 'BLAH' }}
          </div>
      </bug-pipe-test-child>

If i now include a pipe inside that template, it throws errors and refuses to render

      <bug-pipe-test-child>
          <div *contentDirective>
             {{ 'BLAH' | RandomPipe10 }}
          </div>
          <div *contentDirective>
             {{ 'BLAH' }}
          </div>
      </bug-pipe-test-child>

This is the error:

dart_sdk.js:97372 EXCEPTION: TypeError: Cannot read property 'bind' of undefined
STACKTRACE: 
dart:sdk_internal 3900:20                                        bind
package:lulacomponents/test/bug-pipe-test.template.dart 114:120  build
package:angular/src/core/linker/app_view.dart 244:12             create
package:angular/src/core/linker/template_ref.dart 28:9           createEmbeddedView
package:lulacomponents/test/bug-pipe-test.dart 82:14             new
package:lulacomponents/test/bug-pipe-test.template.dart 65:37    build
package:angular/src/core/linker/app_view.dart 244:12             create
package:lulacomponents/test/bug-pipe-test.template.dart 156:17   build
package:angular/src/core/linker/app_view.dart 258:12             createHostView
package:angular/src/core/linker/component_factory.dart 104:20    create
package:angular/src/core/application_ref.dart 70:41              <fn>
package:angular/src/core/change_detection/host.dart 247:26       <fn>
package:angular/src/core/zone/ng_zone.dart 132:18                <fn>
dart:sdk_internal 30626:14                                       run
package:angular/src/core/zone/ng_zone.dart 129:18                [_run]
dart:sdk_internal 30943:14                                       run
package:angular/src/core/zone/ng_zone.dart 291:23                run
package:angular/src/core/application_ref.dart 139:52             runInZone
package:angular/src/core/change_detection/host.dart 245:5        run
package:angular/src/core/application_ref.dart 69:23              bootstrap
package:angular/src/bootstrap/run.dart 207:16                    runApp
bug_pipe.dart 5:2                                                main
%3Canonymous%3E 1:8                                              <fn>
dwds/src/injected/client.js 8188:15                              runMain
dwds/src/injected/client.js 22128:19                             <fn>
dwds/src/injected/client.js 3550:15                              $protected
dwds/src/injected/client.js 10697:12                             call$2
dwds/src/injected/client.js 3514:20                              _asyncStartSync
dwds/src/injected/client.js 22140:16                             $call$body$main__closure
dwds/src/injected/client.js 22072:19                             call$1
dwds/src/injected/client.js 3852:16                              _rootRunUnary
dwds/src/injected/client.js 12004:128                            runUnary$2$2
dwds/src/injected/client.js 11932:14                             runUnaryGuarded$1$2
dwds/src/injected/client.js 11491:19                             _sendData$1
dwds/src/injected/client.js 11645:62                             perform$1
dwds/src/injected/client.js 11694:14                             call$0
dwds/src/injected/client.js 3715:21                              _microtaskLoop
dwds/src/injected/client.js 3721:11                              _startMicrotaskLoop
dwds/src/injected/client.js 10572:9                              call$1
dwds/src/injected/client.js 1169:26                              invokeClosure
dwds/src/injected/client.js 1188:18                              <fn>

and the full stacktrace:

dart_sdk.js:97372 EXCEPTION: TypeError: Cannot read property 'bind' of undefined
STACKTRACE: 
dart:sdk_internal 3900:20                                        bind
package:lulacomponents/test/bug-pipe-test.template.dart 114:120  build
package:angular/src/core/linker/app_view.dart 244:12             create
package:angular/src/core/linker/template_ref.dart 28:9           createEmbeddedView
package:lulacomponents/test/bug-pipe-test.dart 82:14             new
package:lulacomponents/test/bug-pipe-test.template.dart 65:37    build
package:angular/src/core/linker/app_view.dart 244:12             create
package:lulacomponents/test/bug-pipe-test.template.dart 156:17   build
package:angular/src/core/linker/app_view.dart 258:12             createHostView
package:angular/src/core/linker/component_factory.dart 104:20    create
package:angular/src/core/application_ref.dart 70:41              <fn>
package:angular/src/core/change_detection/host.dart 247:26       <fn>
package:angular/src/core/zone/ng_zone.dart 132:18                <fn>
dart:sdk_internal 30626:14                                       run
package:angular/src/core/zone/ng_zone.dart 129:18                [_run]
dart:sdk_internal 30943:14                                       run
package:angular/src/core/zone/ng_zone.dart 291:23                run
package:angular/src/core/application_ref.dart 139:52             runInZone
package:angular/src/core/change_detection/host.dart 245:5        run
package:angular/src/core/application_ref.dart 69:23              bootstrap
package:angular/src/bootstrap/run.dart 207:16                    runApp
bug_pipe.dart 5:2                                                main

Similarly, this works (and correctly adds a blah attribute with value blah):

      <bug-pipe-test-child>
          <div *contentDirective [attr.blah]="'blah'">
             {{ 'BLAH' }}
          </div>
          <div *contentDirective>
             {{ 'BLAH' }}
          </div>
      </bug-pipe-test-child>

but this throws the same error:

      <bug-pipe-test-child>
          <div *contentDirective [attr.blah]="'blah' | RandomPipe10">
             {{ 'BLAH' }}
          </div>
          <div *contentDirective>
             {{ 'BLAH' }}
          </div>
      </bug-pipe-test-child>
dart_sdk.js:97372 EXCEPTION: TypeError: Cannot read property 'bind' of undefined
STACKTRACE: 

The generated bug-pipe-test.template.dart looks like this

  @override
  ComponentRef<import1.BugPipeTest> build() {
    final doc = import8.document;
    _el_0 = doc.createElement('div');
    final _text_1 = import11.appendText(_el_0, 'BLAH');
    _pipe_RandomPipe10_0_1 = import13.pureProxy1(import9.unsafeCast<ViewBugPipeTest0>(parentView)._pipe_RandomPipe10_0.transform);
    init1(_el_0);
  }

The error appears to be on .transform

Reposted on StackOverflow: https://stackoverflow.com/questions/61189733/angular-pipes-not-working-when-used-inside-contentchildren

janvladimirmostert avatar Apr 13 '20 14:04 janvladimirmostert

I can reproduce this issue even with built-in pipes, this works and correctly uppercases the output:

      This works: {{ 'blah1' | RandomPipe10 | uppercase }}

This throws the same error

      <bug-pipe-test-child>
          <div *contentDirective [attr.blah]="'blah' | uppercase">
             {{ 'BLAH' }}
          </div>
          <div *contentDirective>
             {{ 'BLAH' }}
          </div>
      </bug-pipe-test-child>

and so does this:

This throws the same error

      <bug-pipe-test-child>
          <div *contentDirective [attr.blah]="'blah'">
             {{ 'BLAH' | uppercase }}
          </div>
          <div *contentDirective>
             {{ 'BLAH' }}
          </div>
      </bug-pipe-test-child>

janvladimirmostert avatar Apr 13 '20 14:04 janvladimirmostert