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

Time:11-08

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.

CodePudding user response:

A ViewModel must be created using a ViewModelProvider.Factory. But there is a default Factory that is automatically used if you don't specify one. The default factory can create ViewModels who have constructor signatures that are one of the following:

  1. empty, for example MyViewModel: ViewModel.

  2. saved state handle, for example MyViewModel(private val savedStateHandle: SavedStateHandle): ViewModel

  3. application, for example MyViewModel(application: Application): AndroidViewModel(application)

  4. both, for example MyViewModel(application: Application, private val savedStateHandle: SavedStateHandle): AndroidViewModel(application)

If your constructor doesn't match one of these four above, you must create a ViewModelProvider.Factory that can instantiate your ViewModel class and use that when specifying your ViewModelProvider. In Kotlin, you can use by viewModels() for easier syntax. All the instructions for how to create your ViewModelFactory are here.

CodePudding user response:

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)
    }

}

  • Related