18

I have this ViewModel that gets a list from the network and I populate a RecyclerView with the data (MyAvailabilityRepository returns a MutableLiveData, that's why i'm using Transformations.switchMap):

class MyAvailabilityViewModel : ViewModel() {
    private val getListsParams = MutableLiveData<String>()
    private val getListsObservable = Transformations.switchMap(getListsParams) { 
        organizationId -> MyAvailabilityRepository.getSectionedLists(organizationId) 
    }

    fun getListsObservable() : LiveData<Resource<MutableList<SectionedAvailabilityList>>> {
        return getListsObservable
    }

    fun fetchLists(organizationId: String, forceRefresh: Boolean = false) {
        if (getListsParams.value == null || forceRefresh) {
            getListsParams.value = organizationId
        }
    }
}

Fragment's onActivityCreated:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    ...
    viewModel.getListsObservable().observe(this, Observer { // populate RecyclerView })
    viewModel.fetchLists(organizationId)
}

Since getListParams.value is null the first time, it will set getListsParams.value = organizationId and trigger the switchMap and call the repository to get the list from the network.

When I want to force a refresh (by pull-to-refresh) and call the network again, I can use forceRefresh = true:

override fun onRefresh() {
    viewModel.fetchLists(organizationId, forceRefresh = true)
}

It will set the value of organizationId and trigger the Transformations that will then call network.

But, I have a scenario where I clear the data from my RecyclerView's adapter. If after that, the user click a button, I would like to trigger the observer again so that I re-populate the adapter with the data that the getListsObservable has already fetched. I don't want to call forceRefresh on this one cause i'm sure I already have the data and I would just like to trigger the observer again so that my UI is updated with the existing data. Since getListParams.value is not null at that point, then nothing happens when I call fetchLists(organizationId) later on.

Any idea of how I could achieve that with my current setup?

Mathbl
  • 5,047
  • 4
  • 19
  • 24

3 Answers3

29

Yes, the answer given by Hong Duan is perfect. I am just going to extend that answer here,

The better way of doing is having an extension function.

My extension function looks like this,

fun <T> MutableLiveData<T>.forceRefresh() {
    this.value = this.value
}

Caller function looks like this,

mutableLiveDataObject.forceRefresh()

Happy Coding!!

Prasanna Narshim
  • 778
  • 10
  • 14
5

Try removeObservers() and observe() again:

viewModel.getListsObservable().removeObservers(this)
viewModel.getListsObservable().observe(this, Observer { // populate RecyclerView })

because:

If LiveData already has data set, it will be delivered to the observer.

See the docs.

Or maybe you can change the getListsObservable() to return a MutableLiveData, then call setValue manually:

fun loadCurrentData() {
    getListsObservable.value = getListsObservable.value
}
Hong Duan
  • 4,234
  • 2
  • 27
  • 50
1

I do force refresh by this approach -

if (mainViewModel.mutable != null && mainViewModel.mutable.getValue()) {
    mainViewModel.mutable.setValue(mainViewModel.mutable.getValue());
}
Boken
  • 4,825
  • 10
  • 32
  • 42
Gk Mohammad Emon
  • 6,084
  • 3
  • 42
  • 42