Как смапить одну LiveData к другой
Я пытаюсь, следуя принципам UDF, отображать/менять состояние экрана через класс State. Я получаю данные из БД(Room) -> repository -> useCase во ViewModel и там пытаюсь смапить эти данные(одну LiveData к другой LiveData), но не получается. Подскажите что не так. Попробовал сделать тестовую переменную categoryTest и подписаться на неё - все работает, данные во фрагмент приходят и список заполняется. Из этого делаю вывод что проблема кроется где-то внутри MediatorLiveData().apply, но может где-нибудь ещё.
class MenuViewModel @Inject constructor(
private val loadCategoryUseCase: LoadCategoryUseCase,
private val getListCategoryUseCase: GetListCategoryUseCase
) : ViewModel() {
private val _categories = MutableLiveData<StateMenuFragment>()
val categories: LiveData<StateMenuFragment>
get() = _categories
val categoryTest = getListCategoryUseCase() // Тестовая переменная
init {
viewModelScope.launch {
loadCategoryUseCase()
}
getCategories()
}
private fun getCategories() {
_categories.value = StateMenuFragment(loading = true)
viewModelScope.launch {
try {
val result = getListCategoryUseCase() // Возвращает объект типа LiveData<List<Category>>
MediatorLiveData<StateMenuFragment>().apply {
addSource(result) { categoryList ->
val updatedState = _categories.value?.copy(result = categoryList )
?: StateMenuFragment(result = categoryList )
_categories.value = updatedState
}
}
} catch (e: IOException) {
if (_categories.value == StateMenuFragment(result = emptyList())) {
_categories.value = StateMenuFragment(
error = ErrorMessage(isError = true, errorMessage = "error message")
)
}
}
}
}
}
Во фрагменте подписываюсь на них. Но список не приходит
class MenuFragment : Fragment() {
private fun updateState() {
viewModel.categories.observe(viewLifecycleOwner) {
binding.progressBar.visibility = View.GONE
if (it.loading) {
binding.progressBar.visibility = View.VISIBLE
}
if (it.error.isError) {
Toast.makeText(requireContext(), it.error.errorMessage, Toast.LENGTH_SHORT). apply {
setGravity(Gravity.CENTER, 0, 0)
}.show()
}
menuAdapter.submitList(it.result)
}
// Подписываюсь напрямую, миную класс State - данные приходят
viewModel.categoryTest.observe(viewLifecycleOwner) {
menuAdapter.submitList(it)
}
}
}
Класс state
data class StateMenuFragment(
var result: List<Category> = emptyList(),
val loading: Boolean = false,
val error: ErrorMessage = ErrorMessage()
)
data class ErrorMessage(
val isError: Boolean = false,
val errorMessage: String = ""
)
Класс model
data class Category (
val id: Int,
val title: String,
val imageUrl: String
)
Ответы (2 шт):
верно думаете, проблема в MediatorLiveData
код, внутри нее, будет выполняться только когда на медиатор кто-то подпишется. а у вас никто на нее не подписан.
вы можете переделать свой код так, чтобы внутри ВьюМодели подписаться на Медиатор, используя mediator.observeForever(observer)
но тогда стоит не забыть и убрать слушателя с него, при уничтожении ВьюМодели
override fun onCleared() {
mediator.removeObserver(observer)
super.onCleared()
}
Проблема не в apply
, а в том что его вообще не должно быть.
Вы создали объект медиатора, но никуда его не применили, даже ссылку не сохранили.
Нужно присвоить медиатор свойству - тогда вы подпишетесь на него из фрагмента и получите свои данные:
private val _categories = MediatorLiveData<StateMenuFragment>()
...
private fun getCategories() {
...
val result = getListCategoryUseCase() // Возвращает объект типа LiveData<List<Category>>
_categories.addSource(result) { categoryList ->
val updatedState = _categories.value?.copy(result = categoryList )
?: StateMenuFragment(result = categoryList )
_categories.value = updatedState
}