【问题标题】:The pipe ' ' could not be found angular2 custom pipe找不到管道“” angular2 自定义管道
【发布时间】:2016-12-24 17:32:51
【问题描述】:

我似乎无法修复此错误。我有一个搜索栏和一个 ngFor。我正在尝试使用这样的自定义管道过滤数组:

import { Pipe, PipeTransform } from '@angular/core';

import { User } from '../user/user';

@Pipe({
  name: 'usersPipe',
  pure: false
})
export class UsersPipe implements PipeTransform {
  transform(users: User [], searchTerm: string) {
    return users.filter(user => user.name.indexOf(searchTerm) !== -1);
  }
}

用法:

<input [(ngModel)]="searchTerm" type="text" placeholder="Search users">

<div *ngFor="let user of (users | usersPipe:searchTerm)">
...
</div>

错误:

zone.js:478 Unhandled Promise rejection: Template parse errors:
The pipe 'usersPipe' could not be found ("
<div class="row">
    <div  
    [ERROR ->]*ngFor="let user of (user | usersPipe:searchTerm)">

Angular 版本:

"@angular/common": "2.0.0-rc.5",
"@angular/compiler": "2.0.0-rc.5",
"@angular/core": "2.0.0-rc.5",
"@angular/platform-browser": "2.0.0-rc.5",
"@angular/platform-browser-dynamic": "2.0.0-rc.5",
"@angular/router": "3.0.0-rc.1",
"@angular/forms": "0.3.0",
"@angular/http": "2.0.0-rc.5",
"es6-shim": "^0.35.0",
"reflect-metadata": "0.1.3",
"rxjs": "5.0.0-beta.6",
"systemjs": "0.19.26",
"bootstrap": "^3.3.6",
"zone.js": "^0.6.12"

【问题讨论】:

  • 你是否将它包含在组件的管道中?
  • 我才意识到这就是原因。为什么自定义管道的角度示例从不这样做:angular.io/resources/live-examples/pipes/ts/plnkr.html
  • 他们将其定义为全局管道。如果您在许多地方使用自定义管道并且不想在每个注释中定义,您可以对自定义管道执行相同的操作。
  • @SumamaWaheed 我很确定它在文档中的某个时间点存在,但你是对的,文档现在没有提及/显示它。

标签: angular angular2-forms angular2-pipe


【解决方案1】:

注意:仅当您不使用角度模块时

由于某种原因,这不在文档中,但我不得不在组件中导入自定义管道

import {UsersPipe} from './users-filter.pipe'

@Component({
    ...
    pipes:      [UsersPipe]
})

【讨论】:

  • 在您上面发布的 Plunker 中,管道被导入到应用模块中(就像您对组件本身所做的那样),这使得管道可用于该模块中的所有组件。
  • 哦,好吧,我实际上是在查看实际使用自定义管道的组件。我不知道它可以在 app 模块中全局导入。谢谢
  • users-filter.pipe?为什么是 .pipe?
  • 这只是一个命名约定,文件名为 users-filter.pipe.ts
  • @SachinThampan 可能是因为这是 Angular 在 rc5 中的时候我认为..从那以后事情发生了变化,特别是 NgModule。请查看 NgModule 的文档
【解决方案2】:

确保您没有遇到“跨模块”问题

如果使用管道的组件不属于“全局”声明管道组件的模块,则找不到管道,您会收到此错误消息。

在我的例子中,我已经在一个单独的模块中声明了管道,并将这个管道模块导入到任何其他具有使用该管道的组件的模块中。

我已经声明了一个 您使用管道的组件是

管道模块

 import { NgModule }      from '@angular/core';
 import { myDateFormat }          from '../directives/myDateFormat';

 @NgModule({
     imports:        [],
     declarations:   [myDateFormat],
     exports:        [myDateFormat],
 })

 export class PipeModule {

   static forRoot() {
      return {
          ngModule: PipeModule,
          providers: [],
      };
   }
 } 

在其他模块中的使用(例如 app.module)

  // Import APPLICATION MODULES
  ...
  import { PipeModule }    from './tools/PipeModule';

  @NgModule({
     imports: [
    ...
    , PipeModule.forRoot()
    ....
  ],

【讨论】:

  • 这个解决方案是唯一对我有用的解决方案。原来管道需要有一个模块
  • 我也发现这部分解决了我在 Ng 2.8 中找不到管道的错误。* 此外,我有打字稿“警告”仅与使用的命名约定有关,在解决这些问题后,促成了这一点自定义管道转换正确并最终在模板中找到。
  • 这确实是唯一对我有用的解决方案!我尝试了整个共享模块的事情,但这个解决方案就像一个魅力。谢谢@Karl!
  • 我们如何在我们的组件测试中测试这个,
  • 谢谢!我的问题是我错过了“exports:[myPipe]”,认为导出仅适用于模块!
【解决方案3】:

我发现上面的“跨模块”答案对我的情况非常有帮助,但我想对此进行扩展,因为还有另一个问题需要考虑。如果你有一个子模块,它在我的测试中也看不到父模块中的管道。出于这个原因,您可能需要将管道放入自己的单独模块中。

以下是我为解决子模块中不可见的管道所采取的步骤摘要:

  1. 从(父)SharedModule 中取出管道并放入 PipeModule 中
  2. 在 SharedModule 中,导入 PipeModule 并导出(对于应用的其他部分依赖于 SharedModule 以自动获得对 PipeModule 的访问权限)
  3. 对于 Sub-SharedModule,导入 PipeModule,这样它就可以访问 PipeModule,而无需重新导入 SharedModule,否则会产生循环依赖问题等问题。

上述“跨模块”答案的另一个脚注:当我创建 PipeModule 时,我删除了 forRoot 静态方法并在我的共享模块中导入了没有该方法的 PipeModule。我的基本理解是 forRoot 对于单例这样的场景很有用,不一定适用于过滤器。

【讨论】:

  • 感谢您添加此内容。仅在将其添加到 Sub-SharedModule 后才对我有用
  • 是的,这里也一样,我缺少的部分是将 PipesModule 导入 Sub-SharedModule。
  • 我仍然对实际解决问题的方法感到困惑。在 #2 中,您说“为应用程序的其他部分导出以自动获得访问权限”,但在 #3 中,您说将 PipeModule 导入 Sub-SharedModule。 Sub-SharedModule 已经在导入 SharedModule 了吧?如果我们已经在 SharedModule 中导出 PipeModule,为什么还需要导入它?
  • 好问题 - 要清楚.. Sub-SharedModule 是指导入 SharedModule 的模块,而不是已经导入 SharedModule 的模块。在这种情况下,Sub SharedModule 无法访问 PipesModule。
【解决方案4】:

你需要在模块声明中包含你的管道:

declarations: [ UsersPipe ],
providers: [UsersPipe]

【讨论】:

  • 请注意,我最初只是将管道包含在提供程序中。这需要包含在模块文件的声明和提供程序中。
  • 我一直在苦苦挣扎,直到我听从了你的建议。我浪费了大约 4 小时才弄清楚。谢谢你的建议。
  • 为什么没有记录?文档状态 You must include your pipe in the declarations array of the AppModule. : angular.io/guide/pipes。不管怎样,你的回答也解决了我的问题。
  • 谢谢,尤瓦尔。我添加了一个细节:声明你需要管道的模块。
  • 如果模块要被其他模块导入,别忘了还包括exports: [UsersPipe]
【解决方案5】:

我在我的管道所在的同一目录中为管道创建了一个模块

import { NgModule } from '@angular/core';
///import pipe...
import { Base64ToImage, TruncateString} from './'  

   @NgModule({
        imports: [],
        declarations: [Base64ToImage, TruncateString],
        exports: [Base64ToImage, TruncateString]
    })

    export class SharedPipeModule { }   

现在在 app.module 中导入该模块:

import {SharedPipeModule} from './pipe/shared.pipe.module'
 @NgModule({
     imports: [
    ...
    , PipeModule.forRoot()
    ....
  ],

现在可以通过在嵌套模块中导入相同的内容来使用它

【讨论】:

    【解决方案6】:

    对于 Ionic,您可能会遇到 @Karl 提到的多个问题。完美适用于离子延迟加载页面的解决方案是:

    1. 使用以下文件创建管道目录:pipes.tspipes.module.ts

    //管道.ts内容(里面可以有多个管道,记得要

    use @Pipe function before each class)
    import { PipeTransform, Pipe } from "@angular/core";
    @Pipe({ name: "toArray" })
    export class toArrayPipe implements PipeTransform {
      transform(value, args: string[]): any {
        if (!value) return value;
        let keys = [];
        for (let key in value) {
          keys.push({ key: key, value: value[key] });
        }
        return keys;
      }
    }
    

    //管道.module.ts内容

    import { NgModule } from "@angular/core";
    import { IonicModule } from "ionic-angular";
    import { toArrayPipe } from "./pipes";
    
    @NgModule({
      declarations: [toArrayPipe],
      imports: [IonicModule],
      exports: [toArrayPipe]
    })
    export class PipesModule {}
    
    1. 将 PipesModule 包含到 app.module 和 @NgModule 导入部分

      import { PipesModule } from "../pipes/pipes.module"; @NgModule({ imports: [ PipesModule ] });

    2. 在要使用自定义管道的每个 .module.ts 中包含 PipesModule。不要忘记将其添加到导入部分。 // 例子。文件:pages/my-custom-page/my-custom-page.module.ts

      import { PipesModule } from "../../pipes/pipes.module"; @NgModule({ imports: [ PipesModule ] })

    3. 就是这样。现在您可以在模板中使用自定义管道。例如

    &lt;div *ngFor="let prop of myObject | toArray"&gt;{{ prop.key }}&lt;/div&gt;

    【讨论】:

    • 这是 ionic 的救星,谢谢。因为“ionic generate pipe ....”不会生成所有需要的步骤,比如创建pipes.module.ts。你的方法非常有效。请注意,对于 ionic v4,需要对导入路径进行一些小的更改。
    • 我发现你不需要app.module.ts中的imports,所以这一步可以省略。一定是因为“共享模块”无论如何都被导入到需要它的每个其他模块中。如果 app.module 导入可以在全球范围内工作,那就太好了,但我试过了,但没有。
    • 我仍然面临这个问题:控制台错误文件:node_modules/@angular/core/fesm5/core.js:4002:0 错误错误:未捕获(承诺):NullInjectorError:StaticInjectorError(AppModule) [ToHtmlPipe -> DomSanitizer]: StaticInjectorError(Platform: core)[ToHtmlPipe -> DomSanitizer]: NullInjectorError: No provider for DomSanitizer!
    【解决方案7】:
    import { Component, Pipe, PipeTransform } from '@angular/core';
    
    @Pipe({
      name: 'timePipe'
    })
    export class TimeValuePipe implements PipeTransform {
    
      transform(value: any, args?: any): any {
       var hoursMinutes = value.split(/[.:]/);
      var hours = parseInt(hoursMinutes[0], 10);
      var minutes = hoursMinutes[1] ? parseInt(hoursMinutes[1], 10) : 0;
      console.log('hours ', hours);
      console.log('minutes ', minutes/60);
      return (hours + minutes / 60).toFixed(2);
      }
    }
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      name = 'Angular';
      order = [
        {
          "order_status": "Still at Shop",
          "order_id": "0:02"
        },
        {
          "order_status": "On the way",
          "order_id": "02:29"
        },
        {
          "order_status": "Delivered",
          "order_id": "16:14"
        },
         {
          "order_status": "Delivered",
          "order_id": "07:30"
        }
      ]
    }
    
    Invoke this module in App.Module.ts file.
    

    【讨论】:

      【解决方案8】:

      在此处提出替代答案:

      为管道制作单独的模块不是必需的,但绝对是一种替代方法。查看官方文档脚注:https://angular.io/guide/pipes#custom-pipes

      您使用自定义管道的方式与使用内置管道的方式相同。
      您必须将管道包含在 AppModule 的声明数组中。 如果你选择将管道注入到一个类中,你必须在你的 NgModule 的 providers 数组中提供它。

      您所要做的就是将您的管道添加到 declarations 数组中,并将 providers 数组添加到您要使用管道的module 中。 p>

      declarations: [
      ...
      CustomPipe,
      ...
      ],
      providers: [
      ...
      CustomPipe,
      ...
      ]
      

      【讨论】:

      • 但是单个管道不能成为2个模块的一部分......上述方法使其在不同模块中更容易接近。
      【解决方案9】:

      如果您在运行测试时看到此错误,请确保您已导入管道所属的模块,例如:

          beforeEach(async(() => {
              TestBed.configureTestingModule({
                  imports: [CustomPipeModule],
                  declarations: [...],
                  providers: [...],
                  ...
              }).compileComponents();
          }));
      

      【讨论】:

        【解决方案10】:

        自定义管道: 创建自定义管道时,必须在正在使用的模块和组件中注册。

        export class SummaryPipe implements PipeTransform{
        //Implementing transform
        
          transform(value: string, limit?: number): any { 
            if (!value) {
                return null;
            }
            else {
                let actualLimit=limit>0?limit:50
               return value.substr(0,actualLimit)+'...'
            } 
          }
        }
        

        添加管道装饰器

         @Pipe({
                name:'summary'
            })
        

        并参考

        import { SummaryPipe } from '../summary.pipe';` //**In Component and Module**
        
        <div>
            **{{text | summary}}**  //Name should same as it is mentioned in the decorator.
        </div>
        

        //summary是管道装饰器中声明的名字

        【讨论】: