I'm trying to instantiate a viewModel
class which contains a init block where I'm trying to send a Get request with volley. The problem is when I'm trying to instantiate this class in my Fragment class I'm getting this error and I don't know why. Maybe the context from the constructor would be the problem? Thanks:
ViewModel class
class BooksFragmentViewModel(c : Context) : ViewModel() {
lateinit var cont: Context
var books: MutableLiveData<MutableList<BookItem>> = MutableLiveData<MutableList<BookItem>>()
var triggerAddBook = MutableLiveData<Boolean>()
var triggerDeleteBook = MutableLiveData<Boolean>()
var triggerEditBook = MutableLiveData<Boolean>()
var bookRepo: BookRepository = BookRepository()
var bookItemAdd: BookItem? = null
var bookItemDelete: BookItem? = null
var bookItemEdit : BookItem? = null
var bookNew: LiveData<BookItem> = Transformations.switchMap(triggerAddBook) {
if (it != null && it)
addBook()
else
null
}
init {
books = bookRepo.booksGetRequest(c)
}
Fragment Class
class BooksFragment : Fragment(), BooksAdapter.OnDeleteBtnClicked, BooksAdapter.OnEditBtnClicked {
lateinit var booksModel: BooksFragmentViewModel
var position: Int = 0
private var adapter: BooksAdapter = BooksAdapter(this, this)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_books, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
booksModel = ViewModelProvider(requireActivity()).get(BooksFragmentViewModel::class.java)
booksLoading_progressBar.visibility = View.VISIBLE
var c: Context? = context
if (c != null) {
booksModel.setContext(c)
}
The issue is indeed because of the primary constructor with arguments. You are asking the ViewModelProvider
to create an instance of BooksFragmentViewModel
class but the provider does not know anything about the expected argument.
Here is a post on that topic.
ViewModelProvider.Factory
that will handle creation of your view model.The first option is clear, but for the second one, you should do the following.
Create ViewModelProvider.Factory
:
class BooksFragmentViewModelFactory (val context: Context) :
ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(BooksFragmentViewModel::class.java)) {
return BooksFragmentViewModel(context) as T
} else {
throw RuntimeException("Failed to create instance of the ${modelClass.simpleName}")
}
}
}
Use the factory with ViewModelProvider
:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val bookViewModelFactory = BooksFragmentViewModelFactory(requireContext())
booksModel = ViewModelProvider(requireActivity(), bookViewModelFactory).get(BooksFragmentViewModel::class.java)
booksLoading_progressBar.visibility = View.VISIBLE
}
For more explanation check out this codelab from Google (step 8). There is enough examples to understand the basics of how to use a view model factory.