ROOM/Android Sorting a List with Repository and ViewModel | Kotlin

Issue

current issue: During the first time adding, an object (Deck) is added invisibly. It will only appear once the sorting method has been chosen from the context menu. However, this has to be repeated each time for the screen to be updated.

The issue should lie within the Repository as getAllDecks in referencing allDecks. It is as if the allDecks is not updating the data or not realising its .value is changing. As allDecks.postValue() will in take the List from the database. But this obviously isn’t a LiveData<List>. Thus, it only does a one time thing. So. How would I make the occur with the repository reading the constant updates from the database

I am trying to sort through a list stored in the Repository. My ViewModel has a List that is referencing the items stored in the repository. Sorting occurs when a user presses a context menu item and that will change the list in the Repository. However, this action doesn’t seem to be working.

I tried using the debugging tool and it showed me that the Repository method was being called and things were being reassigned. I believed this should have worked as my ViewModel was referencing the repository and MainActivity would automatically update if the list had changed.

The MainActivity context menu opens and reacts to the onClick is the text for sorting changes depending on the chosen item. Thus, I know it is being called. I also know since my update, delete and insert queries are working that MainActivity is listening to the ViewModel’s changing list (of Decks).

For some reason, I have struggled finding any StackOverflow posts similar to mine and in Kotlin.

Ultimately, I was wondering if someone could point in the right direction for what I am doing wrong.
AND
also, how do you debug issues with the database queries. As I find when the debugger transitions to viewing the SQLite query, the whole debugger gets in a loop.

Kind regards,

PandaPlaysAll

Main Activity (abbreviated)


         globalViewModel.sortBy(Sort.ALPHA_ASC) //Set default sorting

        //Listen for livedata changes in ViewModel. if there is, update recycler view
        globalViewModel.allDecks.observe(this, Observer { deck ->
            deck?.let { adapter.setDecks(deck) }
        })
 override fun onContextItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.sort_by_alpha_asc -> { globalViewModel.sortBy(Sort.ALPHA_ASC) ; currentSort = Sort.ALPHA_ASC ; contextMenuText.setText(R.string.sort_by_alpha_asc) ; return true; }
            R.id.sort_by_alpha_desc -> { globalViewModel.sortBy(Sort.ALPHA_DES) ; currentSort = Sort.ALPHA_DES ; contextMenuText.setText(R.string.sort_by_alpha_des) ; return true; }
            R.id.sort_by_completed_hidden -> { globalViewModel.sortBy(Sort.NON_COM) ; currentSort = Sort.NON_COM ; contextMenuText.setText(R.string.sort_by_non_complete) ; return true; }
            R.id.sort_by_due_date -> { globalViewModel.sortBy(Sort.DUE_DATE) ; currentSort = Sort.DUE_DATE ; contextMenuText.setText(R.string.sort_by_due_date) ; return true; }
            else -> return super.onContextItemSelected(item)
        }
    }


View Model

  private val repository: DeckRepository
    val allDecks: LiveData<List<Deck>>

    init {
        val decksDao = FlashCardDB.getDatabase(application, viewModelScope).DeckDAO()
        repository = DeckRepository(deckDao = decksDao)
        allDecks = repository.getAllDecks()
    }

    fun sortBy(sortMethod: Sort) = viewModelScope.launch(Dispatchers.IO) {
        when (sortMethod) {
            Sort.ALPHA_ASC -> repository.sortBy(Sort.ALPHA_ASC)
            Sort.ALPHA_DES -> repository.sortBy(Sort.ALPHA_DES)
            Sort.NON_COM -> repository.sortBy(Sort.NON_COM)
            Sort.DUE_DATE -> repository.sortBy(Sort.DUE_DATE)
        }
    }

DeckRepository

private var allDecks = MutableLiveData<List<Deck>>() //instantiate object

    fun getAllDecks(): LiveData<List<Deck>> = allDecks //Repository handles livedata transmission. ViewModel references the actual Data.

    suspend fun sortBy(sortingMethod: Sort) {
        when (sortingMethod) {
            Sort.ALPHA_ASC -> allDecks.postValue(deckDao.getDecksSortedByAlphaAsc())
            Sort.ALPHA_DES -> allDecks.postValue(deckDao.getDecksSortedByAlphaDesc())
            Sort.NON_COM -> allDecks.postValue(deckDao.getDecksSortedByNonCompleted())
            Sort.DUE_DATE -> allDecks.postValue(deckDao.getDecksSortedByDueDate())
        }
    }

    suspend fun insert(deck: Deck) {
        deckDao.insert(deck)
    }

Database

  //Sorting
    @Query("SELECT * from deck_table ORDER BY title ASC")
    fun getDecksSortedByAlphaAsc(): List<Deck>


    @Query("SELECT * from deck_table ORDER BY title DESC")
    fun getDecksSortedByAlphaDesc(): List<Deck>

    @Query("SELECT * from deck_table WHERE completed=1 ORDER BY title ASC")
    fun getDecksSortedByNonCompleted(): List<Deck>

    @Query("SELECT * from deck_table ORDER BY date ASC")
    fun getDecksSortedByDueDate(): List<Deck>


    //Modifying
    @Insert(onConflict = OnConflictStrategy.ABORT)
    suspend fun insert(deck: Deck)

Solution

Your activity isn’t showing the changes in real time because your repository reassigns its LiveData. While your approach of having a single LiveData to be observed by the activity is correct, you should actually change only its value, not the reference if that makes sense.

Here’s an example:

Repository

private val allDecks = MutableLiveData<List<Deck>>()

fun getAllDecks(): LiveData<List<Deck>> = allDecks

fun sortBy(sortingMethod: Sort) {
    when (sortingMethod) {
        /* If you're handling your DB operations with coroutines, this function
         * should be suspendable and you should set the value to allDecks
         * with postValue
         */
        Sort.ALPHA_ASC -> allDecks.value = deckDao.getDecksSortedByAlphaAsc()
        Sort.ALPHA_DES -> allDecks.value = deckDao.getDecksSortedByAlphaDesc()
        Sort.NON_COM -> allDecks.value = deckDao.getDecksSortedByNonCompleted()
        Sort.DUE_DATE -> allDecks.value = deckDao.getDecksSortedByDueDate()
    }
}

Consequently, your DAO queries will no longer return LiveData, but the lists themselves:

DAO

@Query("SELECT * from deck_table ORDER BY title ASC")
fun getDecksSortedByAlphaAsc(): List<Deck>


@Query("SELECT * from deck_table ORDER BY title DESC")
fun getDecksSortedByAlphaDesc(): List<Deck>

@Query("SELECT * from deck_table WHERE completed=1 ORDER BY title ASC")
fun getDecksSortedByNonCompleted(): List<Deck>

@Query("SELECT * from deck_table ORDER BY date ASC")
fun getDecksSortedByDueDate(): List<Deck>

Answered By – 92AlanC

Answer Checked By – Mildred Charles (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *