How do I pass an argument to the view model?

Viewed 254

I need to get the parameter from the argument inside my fragment and give it to the view model, so that it filters the list based on it or not. I thought that it is possible to pass this argument to the factory for the view model, and from there to the constructor of the view model itself, so it will have val onlyFavorites and it can be used right away in the init block.

ContentFragment.kt

class ContentFragment : Fragment(), ItemFavoriteClickListener {

    private val viewModel: ContentViewModel by viewModels(factoryProducer = {
        ContentViewModel.Factory()
    })

    private var adapter: MyItemModelsAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.getBoolean("onlyFavorites", false)
        
        viewModel.items.observe(this) {
            adapter?.items = it
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        recyclerView?.layoutManager = LinearLayoutManager(context)

        adapter = MyItemModelsAdapter(this)
        recyclerView?.adapter = adapter
    }

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

    override fun onFavoriteClick(item: ItemModel, isFavorite: Boolean) {
        viewModel.changeFavoriteState(item, isFavorite)
    }

    companion object {
        fun newInstance(onlyFavorites: Boolean): ContentFragment {
            val contentFragment = ContentFragment()
            val args = Bundle()
            args.putBoolean("onlyFavorites", onlyFavorites)
            contentFragment.arguments = args

            return contentFragment
        }
    }
}

ContentViewModel.kt

class ContentViewModel(
    private val repository: MyItemsRepository
) : ViewModel() {

    class Factory : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return ContentViewModel(MyItemsRepositoryImpl.getInstance()) as T
        }
    }

    private val _items: MutableLiveData<List<ItemModel>> = MutableLiveData()
    val items: LiveData<List<ItemModel>>
        get() = _items

    fun changeFavoriteState(item: ItemModel, favorite: Boolean) {
        repository.setFavorite(item, favorite)
    }

    init {
        _items.value = repository.items.filter { it.isFavorite }
        repository.addItemChangeListener {
            _items.value = repository.items.filter { it.isFavorite }
        }
    }
}

I need your help writing the code. I mentally understand how to do it, but I don't know how to write it. I would be very grateful for a written example with an explanation.

1 Answers

It's not that hard as it seems; just create a new public variable inside your ContentViewModel

Say

var isFavorite: Boolean = false

Then instead of init of a ViewModel, put your fetching logic inside a method.

fun getItems() {
    _items.value = repository.items.filter { it.isFavorite == isFavorite }
}

And now call this method in fragment's onViewCreated() something like this:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    recyclerView?.layoutManager = LinearLayoutManager(context)

    adapter = MyItemModelsAdapter(this)
    recyclerView?.adapter = adapter

    val isFavorite = arguments?.getBoolean("onlyFavorites", false) ?: false
    viewModel.isFavorite = isFavorite

    viewModel.getItems()
    
    viewModel.items.observe(this) {
        adapter?.items = it
    }
}

This way, you have the value for isFavorite before you actually call the getItems method.

I haven't tested this code, but I'm pretty sure it'll work.

Leave a comment if you need any more help with this.

Related