Search code examples
angularfirebasefirebase-realtime-databaseangular4-forms

Angular 4 ERROR TypeError: items.filter is not a function


I do have a struggle understand custom filters in Angular 4. I know the version is a bit outdated but my project is entirely built with Angular 4 and i can't update it since the release is this month. Please give a hand.

I receive this error in console:

ERROR TypeError: items.filter is not a function
at FilterPipe.transform (filter.pipe.ts:14)
at Object.eval [as updateRenderer] (UploadListComponent.html:49)
at Object.debugUpdateRenderer [as updateRenderer] (core.es5.js:13113)
at checkAndUpdateView (core.es5.js:12260)
at callViewAction (core.es5.js:12620)
at execEmbeddedViewsAction (core.es5.js:12578)
at checkAndUpdateView (core.es5.js:12256)
at callViewAction (core.es5.js:12620)
at execEmbeddedViewsAction (core.es5.js:12578)
at checkAndUpdateView (core.es5.js:12256)

Ok. My UploadModule is this:

 import { NgModule } from '@angular/core';
import {CommonModule} from '@angular/common';
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import { Routes, RouterModule } from '@angular/router';
import {UploadFromComponent} from '../uploads/shared/upload-from/upload-from.component';
import {UploadListComponent} from './shared/upload-list/upload-list.component';
import {UploadsService} from '../../shared/services/uploads.service';
import {AngularFireDatabaseModule} from 'angularfire2/database';
import {SharedModule} from './../shared/shared.module';
import { FilterPipe } from './shared/filter.pipe';
import {User} from '../../shared/services/user';


const routes: Routes = [
    {path: '', component: UploadListComponent},
    {path: 'app-upload-from', component: UploadFromComponent},
];

@NgModule({
    imports: [
        CommonModule,
        RouterModule.forChild(routes),
        NgbModule.forRoot(),
        ReactiveFormsModule,
        FormsModule,
        AngularFireDatabaseModule,
        SharedModule
    ],
    declarations: [UploadFromComponent, UploadListComponent, FilterPipe],
    providers: [UploadsService, FilterPipe, User]

})

export class UploadsModule {
}

The upload.component is tis

  import { Component, OnInit } from '@angular/core';
import { Upload } from '../upload';
import { UploadsService } from '../../../../shared/services/uploads.service';
import { FirebaseListObservable } from 'angularfire2/database-deprecated';
import { Pipe, PipeTransform} from '@angular/core';
import { FilterPipe } from './../filter.pipe';
import {User} from '../../../../shared/services/user';


@Component({
  selector: 'app-upload-list',
  templateUrl: './upload-list.component.html',
  styleUrls: ['./upload-list.component.scss']
})



export class UploadListComponent implements OnInit {
  secondaryPath: FirebaseListObservable<Upload[]>
  selectedFiles: FileList;
  currentUpload: Upload;
  uploads: Upload[];
  noUploads = false;
  user_key: string;
  filterdata: Array<any>;

  constructor(private uploadsService: UploadsService, public filter: FilterPipe, private user: User) { }

  ngOnInit() {
    this.user_key = localStorage.getItem('user_key');
    this.uploadsService.getUploadsto(this.user_key).subscribe(uploads => {
                this.uploads = uploads;
                if (this.uploads.length === 0) {
                    this.noUploads = true;
                } else {
                    this.noUploads = false;
                }
            })
        }

  detectFiles(event) {
    this.selectedFiles = event.target.files;
}
uploadSingle() {
  const file = this.selectedFiles.item(0);
  this.currentUpload = new Upload(file);
  this.uploadsService.pushUploadtp(this.currentUpload);
  this.currentUpload = null;
}

}

The filter.pipe.ts is this 

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

@Pipe({
    name: 'filter',
    pure: false
  })

  export class FilterPipe implements PipeTransform {
    transform(items: Array<any>, filterdata: string): any[] {
    if (!items) {return []; }
    if (!filterdata) {return items; }
     console.log(filterdata);
     filterdata = filterdata.toString().toLowerCase();
     return items.filter( it => {
       console.log(it);
       return it.name.toLowerCase().includes(filterdata);
      });
    }
   }

Upload-list.html here:

<div class="row">
  <div class="col-xl-10 text-xs-center">
      <div class="card card-default">
          <div class="card-header card-default">
              Fisiere
          </div>
          <div class="card-block">
              <div *ngIf="currentUpload">
                  <progress class="progress is-success" min=1 max=100
                            value="{{ currentUpload?.progress }}"></progress>

                  Progress: {{currentUpload?.name}} | {{currentUpload?.progress}}% Complete
              </div>

              <div *ngIf="noUploads">
                Se pare ca nu exista fisiere incarcate
            </div>



              <hr>

              <div class="card card-default">
                  <div class="card-block">
                      <h6>Upload new file</h6>
                      <label>
                          <input type="file" class="btn btn-secondary" (change)="detectFiles($event)">
                      </label>

                      <button class="btn btn-primary"
                              [disabled]="!selectedFiles"
                              (click)="uploadSingle()">
                          Upload
                      </button>
                  </div>
              </div>

          </div>

      </div>
  </div>
  <div *ngIf="uploads" class="mini-container">
        <div>
            <input type="text" [(ngModel)]="filterdata" placeholder="Cauta aici" class="col-xs-10 text-xs-center" name="filterdata"/>
        </div>
        <div class="cards">
    <div class="cardsp" *ngFor="let upload of uploads">
        <div class="card__inner">
          <span><a href="{{upload.url}}">{{upload.name | filter: filterdata }}</a></span>
          <i class="fa fa-folder-o"></i>
        </div>
      </div>
    </div>
</div>
</div>

The point is that searching on web for answers didn't helped. The most fit answers were like "You try to return an object of an array while trying to return an array". I am not that good at vanilla JS or TS for me is a bit harder to understand. If you can provide an example or explanation about this error please help. I do need to do did filter and can't see what i am missing.

The list that need to be retrieved and filtered is on firebase. I don't know how to declare that array in a way that the filter will be able to get the data and filter it.


Solution

  • You are applying filter on upload.name, so unless upload.name is an array you will be getting an error.

    <span><a href="{{upload.url}}">{{upload.name | filter: filterdata }}</a></span>
    

    I believe you should instead apply the filter on the uploads, like:

    <div class="cardsp" *ngFor="let upload of uploads | filter : filterdata">
    

    but then you will need to modify your FilterPipe to

    transform(items: Array<any>, filterdata: string): any[] {
        if (!items) {return []; }
        if (!filterdata) {return items; }
        filterdata = filterdata.toString().toLowerCase();
        return items.filter( it => {
            //return it.name.toLowerCase().includes(filterdata);
            return it.upload.name.toLowerCase().includes(filterdata);
        });
    }