Search code examples
androidkotlinandroid-fragmentsmvvmandroid-viewmodel

How can I send a variable from a fragment to a view model in MVVM architecture in kotlin?


Well I am a beginner with android and kotlin so I have been trying to send a variable semesterSelected from the fragment ViewCourses to my viewmodel UserViewModel is the codes are down below.

`class ViewCourses(path: String) : ReplaceFragment() {
    private var semesterSelected= path

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        container?.removeAllViews()
        return inflater.inflate(R.layout.fragment_view_courses, container, false)
    }



    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        userRecyclerView = view.findViewById(R.id.recyclerView)
        userRecyclerView.layoutManager = LinearLayoutManager(context)
        userRecyclerView.setHasFixedSize(true)
        adapter = MyAdapter()
        userRecyclerView.adapter = adapter

        makeToast(semesterSelected)

//        The variable I am trying to send to UserViewModel is  -->> semesterSelected
        var viewModel: UserViewModel = ViewModelProvider(this)[UserViewModel::class.java]

        viewModel.allUsers.observe(viewLifecycleOwner) {

            adapter.updateUserList(it)

        }

    }
}
class UserViewModel : ViewModel() {

    private val repository: UserRepository = UserRepository("CSE/year3semester1").getInstance()
    private val _allUsers = MutableLiveData<List<CourseData>>()
    val allUsers: LiveData<List<CourseData>> = _allUsers


    init {

        repository.loadUsers(_allUsers)

    }

}

The reason I am doing this is I am wanting a to send a variable to my repository UserRepository all the way from ViewCourses and thought sending this via UserViewModel might be a way .

class UserRepository(semesterSelected: String) {
// The variable I am expecting to get from UserViewModel
    private var semesterSelected = semesterSelected
    private val databaseReference: DatabaseReference =
        FirebaseDatabase.getInstance().getReference("course-list/$semesterSelected")

    @Volatile
    private var INSTANCE: UserRepository? = null

    fun getInstance(): UserRepository {
        return INSTANCE ?: synchronized(this) {

            val instance = UserRepository(semesterSelected)
            INSTANCE = instance
            instance
        }


    }


    fun loadUsers(userList: MutableLiveData<List<CourseData>>) {

        databaseReference.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {

                try {

                    val courseList: List<CourseData> = snapshot.children.map { dataSnapshot ->

                        dataSnapshot.getValue(CourseData::class.java)!!

                    }

                    userList.postValue(courseList)

                } catch (e: Exception) {

                }


            }

            override fun onCancelled(error: DatabaseError) {

            }

        })


    }

}

I tried something like below

class ViewCourses(path: String) : ReplaceFragment() {
    private var semesterSelected= path

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        container?.removeAllViews()
        return inflater.inflate(R.layout.fragment_view_courses, container, false)
    }



    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        userRecyclerView = view.findViewById(R.id.recyclerView)
        userRecyclerView.layoutManager = LinearLayoutManager(context)
        userRecyclerView.setHasFixedSize(true)
        adapter = MyAdapter()
        userRecyclerView.adapter = adapter

        makeToast(semesterSelected)
**// Sending the variable as parameter**
        var viewModel: UserViewModel = ViewModelProvider(this)[UserViewModel(semesterSelected)::class.java]

        viewModel.allUsers.observe(viewLifecycleOwner) {

            adapter.updateUserList(it)

        }

    }
}
class UserViewModel(semesterSelected: String) : ViewModel() {

    private val repository: UserRepository = UserRepository("CSE/year3semester1").getInstance()
    private val _allUsers = MutableLiveData<List<CourseData>>()
    val allUsers: LiveData<List<CourseData>> = _allUsers


    init {

        repository.loadUsers(_allUsers)

    }

}

but doing this my app crashes . how can this be done ? Thanks in Advance.


Solution

  • var viewModel: UserViewModel = ViewModelProvider(this)[UserViewModel(semesterSelected)::class.java]
    

    UserViewModel(semesterSelected)::class.java NOR UserViewModel::class.java is a constructor for the view model.

    If you would want to have ViewModel with that NEEDS initial parameters, you will have to create your own factory for that - which is a tad more complicated and for your case, it might be overkill for what you are trying to do but in the longterm it will pay off(Getting started with VM factories).

    With that said, your needs can be easily solved by one function to initialize the view model.

    class UserViewModel() : ViewModel() {
    
        private lateinit var repository: UserRepository
        private val _allUsers = MutableLiveData<List<CourseData>>()
        val allUsers: LiveData<List<CourseData>> = _allUsers
    
        fun initialize(semesterSelected: String) {
            repository = UserRepository("CSE/year3semester1").getInstance()
            repository.loadUsers(_allUsers)
        }
    

    }