简体   繁体   English

Android viewmodel 传递参数以查看 model 从视图

[英]Android viewmodel pass parameter to view model from view

I have a Fragment code -我有一个片段代码 -

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

   val safeArgs: PetDetailsViewArgs by navArgs()
   val petId = safeArgs.petId

   viewModel.getPetDetailsForId(petId).observe(viewLifecycleOwner, {
     // ...
   })
}

I have a ViewModel code -我有一个 ViewModel 代码 -

private val viewState = PetDetailsViewState()

fun getPetDetailsForId(id: String?): LiveData<PetDetailsViewState> {
   return if (id.isNullOrEmpty()) {
         liveData {
             emit(
                 viewState.copy(
                     loading = false,
                     error = ErrorType.PET_ID_NULL_OR_EMPTY
                 )
             )
         }
        } else {
            petDetailsLiveData
        }
    }

    var petDetailsLiveData = petService.performPetAction(PetAction.GetPetDetails("2")).map {
            when (it) {
                // ...
            }
        }.asLiveData(Dispatchers.Default + viewModelScope.coroutineContext)

As you see in my ViewModel, I am at the moment hardcoding the id in PetAction.GetPetDetails("2") which is not correct.正如您在我的 ViewModel 中看到的,我目前正在对PetAction.GetPetDetails("2")中的 id 进行硬编码,这是不正确的。

How do I pass the id from my view to viewModel?如何将 id 从我的视图传递给 viewModel?

You have two options, if the petId (from the Fragment) does not change, you could create / inject your ViewModel and pass the petId via Constructor.您有两个选择,如果petId (来自 Fragment)没有改变,您可以创建/注入您的 ViewModel 并通过构造函数传递petId

Can your petId be null ?您的petId可以是null吗? If not you can then directly initialize your LiveData and observe it from your Fragment.如果没有,您可以直接初始化您的 LiveData 并从您的片段中观察它。

class PetViewModel(petId: String): ViewModel() {
  val petDetailsLiveData = petService.performPetAction(PetAction.GetPetDetails(petId)).map {
            // ...
        }.asLiveData(Dispatchers.Default + viewModelScope.coroutineContext)
} 

Second option, as you showed in your question, if petId can change, create the LiveData within the function getPetDetailsForId(id: String?) .第二个选项,正如您在问题中显示的那样,如果petId可以更改,请在 function getPetDetailsForId(id: String?)中创建 LiveData。

fun getPetDetailsForId(id: String?): LiveData<PetDetailsViewState> {
   return if (id.isNullOrEmpty()) {
         liveData {
             emit(
                 viewState.copy(
                     loading = false,
                     error = ErrorType.PET_ID_NULL_OR_EMPTY
                 )
             )
         }
        } else {
            petService.performPetAction(PetAction.GetPetDetails("2")).map {
              // ...
            }.asLiveData(Dispatchers.Default + viewModelScope.coroutineContext)
    }

After discussion讨论后

You can consider some caching of your petId and the PetDetailsViewState to avoid duplicate api calls.您可以考虑对您的petIdPetDetailsViewState进行一些缓存,以避免重复的 api 调用。 Take this aa very simple example of getting the idea.以这个非常简单的例子来理解这个想法。 There is much to improve here.这里有很多需要改进的地方。

class PetViewModel : ViewModel() {

 private val cachedPetDetailsViewState: PetDetailsViewState? = null
 private val cachedPetId: String = "" 

 fun getPetDetailsForId(id: String?): LiveData<PetDetailsViewState> {
   if (id == cachedPetId && cachedPetDetailsViewState != null) return MutableLiveData(cachedPetDetailsViewState)

   cachedPetId == id

   if (id.isNullOrEmpty() { ... }
   else { 
     val petIdViewState = // make the API call
 
     cachedPetDetailsViewState = petIdViewState
     
     return MutableLiveData(petIdViewState)
   }   
 }
}

Found a way to do with savedStateHandle -找到了一种方法来处理savedStateHandle -

Here is my Fragment -这是我的片段-

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

   viewModel.petDetailsViewData.observe(viewLifecycleOwner, {

   })
}

ViewModel -视图模型 -

class PetDetailsViewModel @ViewModelInject constructor(
    private val petService: PetService,
    @Assisted private val savedStateHandle: SavedStateHandle
) :
    ViewModel() {

    private val viewState = PetDetailsViewState()
    var petDetailsViewData =
        petService.performPetAction(PetAction.GetPetDetails(savedStateHandle.get<String>("petId")!!))
            .map {
                when (it) {
                    // ... 
                }
            }.asLiveData(Dispatchers.Default + viewModelScope.coroutineContext)
}

I basically use safeArgs key inside viewModel and access it via savedStateHandle.我基本上在 viewModel 中使用 safeArgs 键并通过 savedStateHandle 访问它。 This way I don't need to bother my view with accessing ids and also on configuration change, I only call my service once.这样我就不需要访问 id 和配置更改来打扰我的视图,我只调用我的服务一次。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM