在初始化之外绑定 ViewModel 事件
Posted
技术标签:
【中文标题】在初始化之外绑定 ViewModel 事件【英文标题】:Binding ViewModel events outside of the init 【发布时间】:2020-12-08 05:10:57 【问题描述】:我在我的项目中使用 RxSwift 和 Swinject。我绑定输入/输出的方式与 RxSwift 给出的示例并不完全相同。在RxExample/GitHubSignup 中,绑定是在init()
中完成的,对吧?但是我发现很难实现,因为我使用 Swinject+SwinjectStoryboard 对视图控制器进行依赖注入。因此,init()
不可用,因为实例化视图模型的是 Swinject 容器。那么,除了使用init()
之外,还有没有办法将视图控制器和视图模型绑定在一起呢?
我在想我可以使用 var
而不是 let
作为输出 observables 并创建一个函数 bind(observables: [Observable])
或者其他可以执行从输入到输出的绑定和转换的东西。但是因为它们将是 vars 而不是 let,这意味着我们似乎被允许在整个代码中更改绑定。与我们只使用 let 并将它们绑定到 init()
不同。而且,通过使用函数而不是初始化程序,我必须将依赖项存储到成员变量中。而如果我使用初始化器,我可以只转换 map
或 flatMap
内部的依赖关系。
还有一个问题。说,如果我有这个:
class MyViewController: UIViewController
@IBOutlet weak var refreshButton: UIButton!
@IBOutlet weak var tableView: UITableView!
var viewModel: MyViewModel!
private let disposeBag = DisposeBag()
override func viewDidLoad()
super.viewDidLoad()
viewModel = MyViewModel(refreshTap: refreshButton.rx.tap, dataProvider: ApiAdapter().getData)
private func setupEvents()
viewModel.tableDTOs.bind(to: tableView.rx.items(
cellIdentifier: reuseId, cellType: TableViewCell.self)) _, dto, cell in
cell.fill(with: dto)
.disposed(by: disposeBag)
final class MyViewModel
let tableDTOs: Observable<[TableDTO]>
init(refreshTap: Observable<Void>, dataProvider: () -> Observable<[TableDTO]>)
tableDTOs = Observable.merge(.just(), refreshTap) //Merge with .just to emit at once for initial values
.flatMapLatest dataProvider().asDriver()
那么在这种情况下,如果dataProvider返回complete
或error
,disposable就会被处理掉,对吧?因此场景将无响应,因为 UI 已经未绑定。知道如何解决这个问题吗?
谢谢。
【问题讨论】:
【参考方案1】:那么,除了使用
init()
之外,还有其他方法可以将视图控制器和视图模型绑定在一起吗?
是的,有。为视图模型提供一个函数,该函数接受输入并返回输出。
但是因为它们将是 vars 而不是 let,这意味着我们似乎可以在整个代码中更改绑定。
永远不要将 Observable(或 Subject 或 Observer)设为 var
始终使用 let
函数式响应式编程是一种函数式范例,因此没有 vars。
那么在这种情况下,如果 dataProvider 返回 complete 或 error,disposable 将被释放,对吗?因此场景将无响应,因为 UI 已经未绑定。知道如何解决这个问题吗?
是的,不是的。如果 dataProvider 发出完成的事件,则不会释放,因为 flatMapLatest
仅在 all 的输入完成时释放。由于refreshTap
尚未完成,flatMapLatest
将继续接受来自它的事件,并为每个事件调用它的闭包。
如果 dataProvider 发出错误事件,将处理,因为错误事件会使链短路。但是,由于您在 dataProvider 上使用了.asDriver()
,因此从闭包返回的 Driver 不可能发出错误事件。你很安全。
阻止错误中断链的其他方法是使用.materialize()
或任何.catchError
运算符。例如:
.flatMapLatest
dataProvider
.map Result<[TableDTO], Error>.success($0)
.catchError Observable.just(Result<[TableDTO], Error>.failure($0)
【讨论】:
我明白了。所以输入和输出的整个绑定将来自该函数,对吗?这意味着我不必将输出存储为成员变量,对吗?谢谢。 完全正确。如果你继续沿着这条路走下去,你会开始想知道为什么你一开始就把这个函数放在一个类中。 ?以上是关于在初始化之外绑定 ViewModel 事件的主要内容,如果未能解决你的问题,请参考以下文章
Prism程序入口View ViewModel关联数据绑定数据校验cmd
WPF ViewModel与多个View绑定后如何解决的问题
[WPF源码分析]ContentControl依赖项属性的双向绑定,two-way binding view's DependencyProperty and ViewModel's
Kendo UI - 如何使用 Kendo MVVM 将选中的属性(属性)和处理复选框的单击事件绑定到 viewModel