cjames
cjames

Reputation: 75

Android, how to observe LiveData from within the same ViewModel

I'm a complete novice when it comes to LiveData and MVVM architecture. I'm trying to figure out how to observe a LiveData<List> in the ViewModel to update another variable depending on if it is empty or not.

I'm getting the LiveData from my Room database with this:

class MealsViewModel @Inject constructor(
    private val mealDao : MealDao
) : ViewModel() {

    ...

    private val currentDay: MutableLiveData<Date> = MutableLiveData(Date())
    val meals = Transformations.switchMap(currentDay){ date -> mealDao.getMeals(date).asLiveData() }

I would like for another variable in the ViewModel, private val empty: Boolean, to update anytime the list is returned empty (null). This will be used in updating an ImageView in the Fragment from Visible.GONE to Visible.VISIBLE.

How do I check if val meals is empty synchronously?

I've read around and saw some people said to useobserveForever, but the architecture guide explicitly advises against any observers in ViewModels.

I could probably observe the LiveData in the Fragment, but that would require business logic in the Fragment, ie:

viewModel.meals.observe(viewLifecycleOwner) {

    if meals.value.isEmpty() imageView.visibility = View.VISIBLE else imageView.visibility = View.GONE
}

And I'd like to keep the Fragment as 'dumb' as possible, so I'd prefer to have that logic in the ViewModel. Is that possible?

Upvotes: 0

Views: 6903

Answers (2)

Eishon
Eishon

Reputation: 1324

I don't know exactly how your code is set up but you can do something like below

Add variable to ViewModel

val empty = MutableLiveData<Boolean>()

In meals observer viewModel.meals.observe(viewLifecycleOwner) {

viewModel.empty,postValue(meals.value.isEmpty())

Then observe from empty

Using MediatorLiveData

In your ViewModel class, create

val empty = MediatorLiveData<Boolean>()

Then

empty.addSource(meals) {
    empty.value = it.isEmpty()
}

Upvotes: 0

Dương Minh
Dương Minh

Reputation: 2421

You can check live data meal to see if it's empty or null and then trigger with your live data empty like this:

In the viewmodel, you create a livedata isEmptyMeals. This live data variable will always trigger when meals value change and will check if your meals value are empty or null.

MealsViewModel.kt

class MealsViewModel @Inject constructor(
    private val mealDao : MealDao
) : ViewModel() {

    ...

    private val currentDay: MutableLiveData<Date> = MutableLiveData(Date())
    val meals = Transformations.switchMap(currentDay){ date -> mealDao.getMeals(date).asLiveData() }
    
    val isEmptyMeals = meals.map {
        it.isNullOrEmpty()
    }
}

And in the fragment, you will listen to observe the livedata isEmptyMeals and perform the logic to hide or show the image view you want.

Fragment.kt

viewModel.isEmptyMeals.observe(viewLifecycleOwner) {
    imageView.visibility = if (it) View.VISIBLE else View.GONE
}

Upvotes: 1

Related Questions