web-dev-qa-db-fra.com

LiveData avec plusieurs sources de différents types

J'ai actuellement un projet contenant une liste de MyItem et utilisant Firebase/LiveData. Il est structuré en groupes et chaque groupe a des éléments.

Je souhaite pouvoir mettre à jour cette liste si l'un des événements suivants se produit:

  1. Un élément est mis à jour (sur le backend via Firebase)
  2. Un filtre est changé (un tableau séparé sur Firebase pour chaque utilisateur)
  3. Un élément est mis en signet (un tableau séparé sur Firebase pour chaque utilisateur)

Pour obtenir la liste des contenus, j'ai une fonction comme celle-ci pour retourner LiveData qui se mettra à jour chaque fois qu'un élément est mis à jour (# 1).

base de données

getList(id: String): LiveData<List<MyItem>> {
    val data = MutableLiveData<List<MyItem>>()

    firestore
        .collection("groups")
        .document(id)
        .collection("items")
            .addSnapshotListener { snapshot, exception ->
                val items = snapshot?.toObjects(MyItem::class.Java) ?: emptyList()

                // filter items 

                data.postValue(items)
        }

    return data
}

Et dans mon ViewModel, j'ai une logique pour gérer ce cas.

modèle de vue

private val result = MediatorLiveData<Resource<List<MyItem>>>()

private var source: LiveData<List<MyItem>>? = null
val contents: LiveData<Resource<List<MyItem>>>
    get() {
        val group = database.group

        // if the selected group is changed.
        return Transformations.switchMap(group) { id ->
            // showing loading indicator
            result.value = Resource.loading(null)

            if (id != null) {
                // only 1 source for the current group
                source?.let {
                    result.removeSource(it)
                }

                source = database.getList(id).also {
                    result.addSource(it) {
                        result.value = Resource.success(it)
                    }
                }

                // how to add in source of filter changes?

            } else {
                result.value = Resource.init(null)
            }
            return@switchMap result
        }
    }

La logique est assez complexe et difficile à suivre. Existe-t-il une meilleure façon de structurer cela pour gérer plusieurs changements différents? Quelle est la meilleure façon de stocker les filtres actuels de l'utilisateur?

Merci.

7
Advice-Dog

Je ne sais pas si je reçois votre question correctement ou non, mais si vous avez une vue qui fonctionne avec une liste (quelque chose comme MyItemList) et cette liste mise à jour ou modifiée par plusieurs situations, vous devez travailler avec MediatorLiveData.

Je veux dire que vous devez avoir trois LiveData chacun responsable d'une situation et un MediatorLiveData qui a notifié si chacun d'eux a changé.

voir ci-dessous:

base de données

fun getListFromServer(id: String): LiveData<List<MyItem>> {
    val dataFromServer = MutableLiveData<List<MyItem>>()

    firestore
      .collection("groups")
      .document(id)
      .collection("items")
          .addSnapshotListener { snapshot, exception ->
              val items = snapshot?.toObjects(MyItem::class.Java) ?: emptyList()
              dataFromServer.postValue(items)
      }

    return dataFromServer
}

fun getFilteredData(id: String): LiveData<FilterData> {
    return DAO.user.getFilteredData(id)
}

fun getBookmarkedList(id: String): LiveData<BookmarkData> {
    return DAO.user.getBookmarkedData(id)
}

Et dans le viewModel vous avez un MediatorLiveData qui observe sur ces liveDatas jusqu'à ce que des données aient changé de vue de notification.

viewModel

private val result = MediatorLiveData<<List<MyItem>>()

fun observeOnData(id: String, owner: LifeCycleOwner, observer: Observer<List<MyItem>>) {
   result.observe(owner, observer);

   result.addSource(Database.getListFromServer(id), MyItemList -> {
        if(MyItemList != null)
            result.setValue(MyItemList)
   });
   result.addSource(Database.getFilteredData(id), filterData -> {
        if(filterData != null) {
            val myItemList = result.getValue()
            if (myItemList == null) return

            //here add logic for update myItemList depend On filterData

            result.setValue(myItemList)
        }
   });
   result.addSource(Database.getBookmarkedList(id), bookmarkData -> {
        if(MyItemList != null) {
            val myItemList = result.getValue()
            if (myItemList == null) return

            //here add logic for update myItemList depend On bookmarkData

            result.setValue(myItemList)
        }
   });

}
1
SamiAzar

Votre implémentation de contents comprend plusieurs références à des variables externes, ce qui rend difficile le suivi et le suivi de l'état. Je garderais juste les références aussi locales que possible et ferais confiance à switchMap(liveData) pour faire un bon travail. Le code suivant devrait faire exactement la même chose que le vôtre:

val contents = Transformations.switchMap(database.group) { id ->
    val data = MediatorLiveData<Resource<List<MyItem>>()

    if (id == null) {
        data.value = Resource.init(null)
    } else {
        data.value = Resource.loading(null)
        data.addSource(database.getList(id)) {
            data.value = Resource.success(it)
        }
    }

    return liveData
}

En ce qui concerne getList(id), vous voudrez peut-être également gérer correctement le exception.

1
tynn