Does Firestore + AsyncPipe for internal object in AngularDart 5?

Issue

I’ve been working on something that uses a shared dart package through for firestore and come across an interesting issue.

I have a business logic object that is basically as follows:

class HomeBloc {
  final Firestore _firestore;
  CollectionReference _ref;

  HomeBloc(this._firestore) {
    _ref = _firestore.collection('test');
  }

  Stream<List<TestModel>> get results {
    return _ref.onSnapshot.asyncMap((snapshot) {
      return snapshot.docs.map((ds) => TestModel(ds.get('data') as String)).toList();
    }
  }
}

Given the following code component:

@Component(
  selector: 'my-app',
  templateUrl: 'app_component.html',
  directives: [coreDirectives],
  pipes: [commonPipes]
)
class AppComponent extends OnInit {
  HomeBloc bloc;
  Stream<List<TestModel>> results;

  AppComponent() {

  }

  @override
  void ngOnInit() {
    print("Initializing component");
    fb.initializeApp(
      //...
    );

    getData();
  }

  Future<void> getData() async {
    final store = fb.firestore();
    bloc = HomeBloc(store);
  }
}

I would expect the following to work, but it does not:

<div *ngIf="bloc != null">
  <h2>Loaded properly</h2>
  <ul>
    <li *ngFor="let item of bloc.results | async">
    {{item.data}}
    </li> 
  </ul>
</div>

However, if I instead change getData and the html to the following:

Future<void> getData() async {
  final store = fb.firestore();
  bloc = HomeBloc(store);
  results = bloc.results;  
}

// HTML
<ul *ngFor="let item of results | async">

Everything works as expected. What’s going on here?

Solution

The answer is that the get method is creating a new list every time its accessed, which isn’t giving Angular an oppotunity to render the items before resetting. The correct implementation of HomeBloc:

class HomeBloc {
  final Firestore _firestore;
  CollectionReference _ref;

  HomeBloc(this._firestore) {
    _ref = _firestore.collection('test');
    _results = _ref.onSnapshot.asyncMap((snapshot) {
      return snapshot.docs.map((ds) => TestModel(ds.get('data') as String)).toList();
  }

  Stream<List<TestModel>> _results;
  Stream<List<TestModel>> get results => _results;
}

Answered By – Jeff

Answer Checked By – David Marino (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *