Deitools
Deitools

Reputation: 431

ViewModel not initializing or problem design with my viewModel

I've been reading some questions, answers and blogs about MVVM pattern in Android, and I've implemented it in my application.

My application consists of a MainActivity with 3 Tabs. Content of each tab is a fragment.

One of these fragments, is a List of Users stored on Room DB, which is where I've implemented the MVVM (implementing User object, ViewModel, Repository and Adapter with RecycleView).

In this same fragment, I have an "add User" button at the end that leads to a new activity where a formulary is presented to add a new user. In this activity I want to be sure that the full name of user not exists in my DB before saving it.

I was trying to use the same ViewModel to get full UserNames full name, but it seems that ViewModel is never initialized and I dont' know why.

I've read some questions about that viewmodel can't be used in different activities (I use it in MainActivity also in AddUser activity

This is my ViewModel:

class UserViewModel : ViewModel() {

    val allUsersLiveData: LiveData<List<User>>
    private val repository: UserRepository

    init {
        Timber.i("Initializing UserViewModel")
        repository = UserRepository(UserTrackerApplication.database!!.databaseDao())
        allUsersLiveData = repository.getAllUsers()
    }

    fun getAllUsersFullName(): List<String> {

        return allUsersLiveData.value!!.map { it.fullname}
    }

And my AddUser activity:

class AddUser : AppCompatActivity() {
    private lateinit var userList:List<String>
    private lateinit var binding: ActivityAddUserBinding
    private val userViewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_add_user)
        Timber.i("Add User OnCreate")

        binding = ActivityAddUserBinding.inflate(layoutInflater)


        setContentView(binding.root)
    }

    fun addUserClick(v : View){

        //someCode
        val userName = binding.constraintLayoutAddUser.etUserName!!.text.toString()
        if(checkUserExistance(userName)) {
        val text: String = String.format(
                    resources.getString(R.string.repeated_user_name),
                    userName
        Snackbar.make(v, text, Snackbar.LENGTH_LONG).show()
        {
        else
        {
            lifecycleScope.launch {
                UserTrackerApplication.database!!.databaseDao()
                 .insertUser(user)
                 Timber.i("User added!")
        }
        finish()

    }
}

Debugging, I see the log "Initializing UserViewModel" when the fragment of MainActivity is started, but I can't see it when AddUser activity is called. So it seems it's not initializing correctly.

So the questions:

  1. Is this a good approach? I'm making some design mistake?
  2. Why the VM isn't initializing?

EDIT

I forgot to add this function. Calling userViewModel here is where I get the error:

 private fun checkUserExistance(userName: String): Boolean {
        var result = false
        userList = userViewModel.getAllUsersNames() 

        for (usr in userList)
        {
            if(usr.uppercase() == userName.uppercase())
            {
                result = true
                break
            }
        }
        return result
    }

EDIT 2

I added this on my "onCreate" function and started to work:

 userViewModel.allUsersLiveData.observe(this, Observer<List<User>>{
            it?.let {
                // updates the list.
                Timber.i("Updating User Names")
                userList =userViewModel.getAllUsersNames()
            }
        })

Upvotes: 0

Views: 1517

Answers (1)

Yunus D
Yunus D

Reputation: 1256

if you take a look at by viewModels delegate you will see it's lazy it means it will initialize when it is first time accessed

@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}

Upvotes: 1

Related Questions