【问题标题】:How to use snapshotChanges() method to get both key value and filter the data?如何使用 snapshotChanges() 方法获取键值和过滤数据?
【发布时间】:2018-08-14 08:57:52
【问题描述】:

我正在开发一个 Angular Firebase 项目,我需要在其中过滤我的数据库并获取键值。目前我在我的服务代码中使用 valueChanges() 方法(在 getUnreadBooks 和 getFavoriteBooks 方法中,如下所示)来获取数据并过滤它。但是当我尝试在模板文件中获取键值时,它给了我键值作为“未定义”。我尝试使用 snapshotChanges() 方法,但无法解决如何使用它来获取键值以及过滤数据。下面分别是我的 Angular FirebaseService、home.component.ts(我在其中注入我的服务代码)和 home.component.html(模板文件)代码:

import { Injectable } from '@angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Injectable()
export class FirebaseService {

  books: Observable<any[]>;
  unreadBooks;
  favoriteBooks;

  constructor(private db: AngularFireDatabase) {}

  getBooks(){
        this.books = this.db.list('/books').valueChanges() as Observable<any[]>;
        return this.books;
    }       

  getFavoriteBooks(){
    this.favoriteBooks = this.db.list('/books').valueChanges() as Observable<any[]>;
    this.favoriteBooks = this.favoriteBooks.map(books => {
        const topRatedBooks = books.filter(item =>  item.rate>4);
        return topRatedBooks;
    })
    return this.favoriteBooks;
  }

  getUnreadBooks(){
    this.unreadBooks = this.db.list('/books').valueChanges() as Observable<any[]>;
    this.unreadBooks = this.unreadBooks.map(books => {
        const ub = books.filter(item => item.dateread == null);
        return ub;
    })
    return this.unreadBooks;
  }
}

Home.Component.ts 文件 =>

import { Component, OnInit } from '@angular/core';
import { FirebaseService } from '../../services/firebase.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  //favorite Books
  favoriteBooks: any;
  unreadBooks: any;

  constructor(private firebaseService: FirebaseService) { }

  ngOnInit() {
    this.firebaseService.getFavoriteBooks()
        .subscribe(favBooks => {
            this.favoriteBooks = favBooks;
            console.log(this.favoriteBooks);
        })
    this.firebaseService.getUnreadBooks()
        .subscribe(ubBooks => {
            this.unreadBooks = ubBooks;
            console.log('Unread Books:', this.unreadBooks);
        })
  }

}

Home.component.html 文件 =>

<mat-toolbar>
    My Top Rated Books
</mat-toolbar>
<mat-grid-list cols="3">
    <mat-grid-tile *ngFor="let book of favoriteBooks">
        <mat-card>
            <mat-card-header>
                <mat-card-title>
                    <h4>{{book.title}}</h4>
                </mat-card-title>
            </mat-card-header>
            <img mat-card-image src="{{book.imageUrl}}" alt="{{book.title}}">
            <mat-card-actions>
                <button mat-button mat-raised-button class="detailsButton" [routerLink]="['/book/'+book.$key]">
                    <i class="material-icons">visibility</i>Book Details</button>
                <button mat-button mat-raised-button class="editButton" [routerLink]="['/editbook/'+book.$key]">
                    <i class="material-icons">mode_edit</i>Edit Book</button>
            </mat-card-actions>
        </mat-card>     
    </mat-grid-tile>
</mat-grid-list>

<mat-toolbar>
    Books I have not read yet
</mat-toolbar>
<mat-grid-list cols="3">
    <mat-grid-tile *ngFor="let book of unreadBooks">
        <mat-card>
            <mat-card-header>
                <mat-card-title>
                    <h4>{{book.title}}</h4>
                </mat-card-title>
            </mat-card-header>
            <img mat-card-image src="{{book.imageUrl}}" alt="{{book.title}}">
            <mat-card-actions>
                <button mat-button mat-raised-button class="detailsButton" [routerLink]="['/book/'+book.$key]">
                    <i class="material-icons">visibility</i>Book Details</button>
                <button mat-button mat-raised-button class="editButton" [routerLink]="['/editbook/'+book.$key]">
                    <i class="material-icons">mode_edit</i>Edit Book</button>
            </mat-card-actions>
        </mat-card>     
    </mat-grid-tile>
</mat-grid-list>

【问题讨论】:

  • 你用snapshotChanges()做了什么?检查这个例子app
  • 我尝试在 getFavoriteBooks() 方法(在 FirebaseService File 中)中使用 snapshotChanges() 代替 valueChanges() 来获取键值。但是在使用 snapshotChanges() 时无法解决如何在内部应用过滤器方法。我需要过滤我的书籍以及获取过滤书籍的键值。我怎么能这样做?

标签: angular firebase firebase-realtime-database angularfire2


【解决方案1】:

声明一个函数以将 id 添加到您的对象:

documentToDomainObject = _ => {
    const object = _.payload.doc.data();
    object.id = _.payload.doc.id;
    return object;
}

并在您的 getBooks 方法中使用它:

getBooks(){
  this.books = this.db.list('/books').snapshotChanges()
  .pipe(map(actions => actions.map(this.documentToDomainObject)));

  return this.books;
}

【讨论】:

  • 谢谢。但它在'.pipe(map..)'部分给了我一个错误:“找不到名称'map'。你的意思是'Map'”
  • 需要导入rxjs算子映射import { map } from 'rxjs/operators/map';
  • 绝对是地图而不是地图。您是否将 rxjs 添加到您的项目中?
  • 我怎么能用这个:getFavoriteBooks(){ this.favoriteBooks = this.db.list('/books').valueChanges() as Observable; this.favoriteBooks = this.favoriteBooks.map(books => { const topRatedBooks = books.filter(item => item.rate>4); return topRatedBooks; }) return this.favoriteBooks; }。我想通过这种方法过滤我的书籍以及获取过滤书籍的关键值。我怎么能这样做?
  • getFavoriteBooks(){ return this.db.list('/books').snapshotChanges() .pipe(map(actions => actions.map(this.documentToDomainObject) .filter(item => item.rate>4))); }
【解决方案2】:

需要导入rxjs operator map 从'rxjs/operators'导入{地图}; // 在 rxjs 6 版本中

【讨论】:

  • 欢迎来到 Stack Overflow。您能否详细说明您的答案并提供一个有效的代码示例?
【解决方案3】:

在我的情况下,我通过导入 rxjs 运算符映射来解决它,由 .pipe 组合

import { map } from 'rxjs/operators';

例子:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AngularFirestore } from 'angularfire2/firestore';
import { IdeaService } from './app.service';

import { config } from './app.config';
import { Idea } from './app.model';
import {
  AngularFirestoreDocument,
  AngularFirestoreCollection
} from 'angularfire2/firestore';

@Component({
selector: 'app-root',
template: `
    <ul>
        <li *ngFor="let idea of ideas | async">
            <pre>{{ idea | json }}</pre>
        </li>
    </ul>
`
})
export class AppComponent {
  public ideasCollection: AngularFirestoreCollection<Idea>;
  public ideas: Observable<any[]>;

  constructor(db: AngularFirestore, private ideaService: IdeaService) {
    this.ideasCollection = db.collection<Idea>(config.collection_endpoint);
    this.ideas = this.ideasCollection.snapshotChanges().pipe(
        map(actions => {
          return actions.map(a => {
            const data = a.payload.doc.data() as Idea;
            const id = a.payload.doc.id;
            return { id, ...data };
          });
        }));
    }
}

【讨论】:

    【解决方案4】:

    this.data.list('/expenseCategories').snapshotChanges().forEach(snapshot=> {
          snapshot.forEach(keys => {
            TransactionService.expenseCategories.push(keys.payload.key);
          })
        });

    【讨论】:

    • 不需要添加额外的循环,只需按照建议使用管道和地图运算符,额外的循环会降低性能
    【解决方案5】:

    我导入了这个 import { map } from 'rxjs/operators' 这解决了我的问题

    【讨论】:

      【解决方案6】:

      这是使用 angular 9 并且工作正常。这是在 service.ts 中设置的,并从特定的 .ts 文件中获取数据,并且通常将数据读入模板

       getAll() {
          return this.database.list('/products').snapshotChanges()
          .pipe(map( action => action
            .map(a => {
              const key = a.payload.key;
              const data = a.payload.val();
              return  data;
            })));
        }
      

      特定的.ts

      constructor(private productService: ProductService) {
          this.products$ = this.productService.getAll();
         }
      

      特定的.html

      <tr *ngFor="let product of products$ | async" >
          <td> {{ product.title }}</td>
          <td> {{ product.price }}</td>
      

      【讨论】:

        【解决方案7】:

        简单有效的解决方案:你会得到id和data

        将其添加到您的服务文件的标题部分

        import { map } from 'rxjs/operators';
        

        你的 getBooks 方法:

        getBooks(){
           return this.books = this.db.list('/books').snapshotChanges()
             .pipe(map(action => action
                        .map(a => {
                            let obj:any = a.payload.doc.data()
                            return {
                                ...obj,
                                id: a.payload.doc.id
                            };
                        })
                    ));
        }
        

        【讨论】:

          【解决方案8】:

          从 Firebase 文档中,我看到使用 valueChanges() 时无法获取密钥,只能在使用 snapshotChanges() 时获取。

          基于 Neelaka 的回答,如果您使用的是 Angular 12 和最新版本的 firebase,如果您有一个使用 (keyup) 事件绑定的表单,您可以获得密钥并像这样进行过滤:

          在您的product.services.ts 文件中:

            getAll(){
              return this.db.list('/products').snapshotChanges().pipe(map(actions => {
                return actions.map(a => {
                  const key = a.payload.key;
                  const data = a.payload.val();
                  return {data, key};
                })
              }));
            }
          

          admin-products.component.ts 文件中:

          export class AdminProductsComponent implements OnDestroy {
          
            products!: { title: string; }[] | any;
            filteredProducts!: any[];
            subscription: Subscription;
          
          
            constructor(private productService: ProductService) {
              
              this.subscription = this.productService.getAll().subscribe(products => {
                this.filteredProducts = this.products = products; 
              });
              
             }
          
            filter(query: string){
              
              this.filteredProducts = (query) ? 
              this.products.filter((p: { data: {title:string}, key: string }) => p.data.title.toLocaleLowerCase().includes(query.toLocaleLowerCase())) : 
              this.products;
            }
          
            ngOnDestroy() {
              this.subscription.unsubscribe();
            }
          
          }
          
          

          admin-products.html

          
          <br>
          <p>
              <a routerLink="/admin/products/new" class="btn btn-primary">New Product</a>
          </p>
          <br>
          <p>
            <input #query (keyup)="filter(query.value)" type="text" class="form-control" placeholder="Search...">
          </p>
          <br>
          <table class="table">
              <thead>
                <tr>
                  <th scope="col">Title</th>
                  <th scope="col">Price</th>
                  <th scope="col"></th>
                  <th scope="col"></th>
                </tr>
              </thead>
              <tbody>
                <tr *ngFor="let p of filteredProducts">
                  <td>{{ p.data.title }}</td>
                  <td>{{ p.data.price }}</td>
                  <td>
                      <a [routerLink]="['/admin/products/', p.key]">Edit</a> ✍️ 
                  </td>
                  <td>
                      <a (click)="delete(p.key)" id="delete">Delete</a> ? 
                  </td>
                </tr>
              </tbody>
            </table>
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-05-31
            • 1970-01-01
            相关资源
            最近更新 更多