Dnakin
Dnakin

Reputation: 175

LiveData Transformation not getting triggered

I subscribed to ids and search in the ui but i wasn't getting any results so i stepped through with the debugger and found out that the transformation is not getting triggered after the first time. So when i call setIds the first time ids gets updated but for every call after the first one the transformation won't trigger. Same goes for the search.

Any ideas what might possible go wrong?

class MyViewModel : ViewModel() {

    private val repository = Repository.sharedInstance

    var recentRadius: LiveData<List<RecentRadius>>?
    var recentRoute: LiveData<List<RecentRoute>>?

    init {
        recentRadius = repository.recentRadius()
        recentRoute = repository.recentRoute()
    }


    private val idsInput = MutableLiveData<String>()
    fun setIdsInput(textId: String) {
        idsInput.value = textId
    }

    val ids: LiveData<List<String>> = Transformations.switchMap(idsInput) { id ->
        repository.ids(id)
    }

    private val searchInput = MutableLiveData<Search>()
    fun setSearchInput(search: Search) {
        searchInput.value = search
    }


    val search: LiveData<SearchResult> = Transformations.switchMap(searchInput) { search ->
        when (search.type) {
            SearchType.ID -> repository.id(search)
            SearchType.RADIUS -> repository.radius(search)
            SearchType.ROUTE -> repository.route(search)
        }
    }
}

Upvotes: 16

Views: 7541

Answers (4)

rahat
rahat

Reputation: 2056

If you really want it to be triggered.

fun <X, Y> LiveData<X>.forceMap(
    mapFunction: (X) -> Y
): LiveData<Y> {
    val result = MutableLiveData<Y>()
    this.observeForever {x->
        if (x != null) {
            result.value = mapFunction.invoke(x)
        }
    }
    return result
}

Upvotes: 0

Badr Bujbara
Badr Bujbara

Reputation: 8671

for me, it was because the observer owner was a fragment. It stopped triggering when navigating to different fragments. I changed the observer owner to the activity and it triggered as expected.

itemsViewModel.items.observe(requireActivity(), Observer {

The view model was defined as a class property:

    private val itemsViewModel: ItemsViewModel by lazy {
    ViewModelProvider(requireActivity()).get(ItemsViewModel::class.java)
}

Upvotes: 0

Akshay Chordiya
Akshay Chordiya

Reputation: 4841

The most common reason why transformation don't get triggered is when there is no Observer observing it or the input LiveData is not getting changed.

Upvotes: 49

Shahbaz Hashmi
Shahbaz Hashmi

Reputation: 2815

Below example illustrates use of map when observer is attached in the activity.

Activity

class MainActivity : AppCompatActivity() {

lateinit var mBinding : ActivityMainBinding

private val mViewModel : MainViewModel by lazy {
   getViewModel { MainViewModel(this.application) }
}

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
   mBinding.vm = mViewModel

   // adding obeserver
   mViewModel.videoName.observe(this, Observer<String> { value ->
       value?.let {
           //Toast.makeText(this, it, Toast.LENGTH_LONG).show()
       }
   })
 }
}

ViewModel with map

class MainViewModel(val appContext : Application) : AndroidViewModel(appContext) {

   private val TAG = "MainViewModel"

   var videoData = MutableLiveData<VideoDownload>()
   var videoName : LiveData<String>

   init {
      // Update the data
      videoName = Transformations.map(videoData) { "updated : "+it.webUrl }
    }

    fun onActionClick(v : View) {
       // change data
       videoData.value = VideoDownload(System.currentTimeMillis().toString())
    }

    fun onReActionClick(v : View) {
       // check data
       Toast.makeText(appContext, videoName.value, Toast.LENGTH_LONG).show()
    }

}

ViewModel with switchMap

class MainViewModel(val appContext : Application) : AndroidViewModel(appContext) {

    private val TAG = "MainViewModel"

    var videoData = MutableLiveData<VideoDownload>()
    var videoName : LiveData<String>

    init {
       // Update the data
       videoName = Transformations.switchMap(videoData) { modData(it.webUrl) }

    }

    private fun modData(str: String): LiveData<String> {
        val liveData = MutableLiveData<String>()
        liveData.value = "switchmap : "+str
        return liveData
    }

    fun onActionClick(v : View) {
        // change data
        videoData.value = VideoDownload(System.currentTimeMillis().toString())
    }

    fun onReActionClick(v : View) {
        // check data
        Toast.makeText(appContext, videoName.value, Toast.LENGTH_LONG).show()
    }

}

Upvotes: 0

Related Questions