- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
使用 ViewModel 和数据绑定(bind)验证表单数据的最佳方法是什么?
我有一个简单的注册 Activity ,它链接绑定(bind)布局和 ViewModel
class StartActivity : AppCompatActivity() {
private lateinit var binding: StartActivityBinding
private lateinit var viewModel: SignUpViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this, SignUpViewModelFactory(AuthFirebase()))
.get(SignUpViewModel::class.java);
binding = DataBindingUtil.setContentView(this, R.layout.start_activity)
binding.viewModel = viewModel;
signUpButton.setOnClickListener {
}
}
}
ViewModel
带有 4 个 ObservableFields
和 signUp()
方法,应该在向服务器提交数据之前验证数据。
class SignUpViewModel(val auth: Auth) : ViewModel() {
val name: MutableLiveData<String> = MutableLiveData()
val email: MutableLiveData<String> = MutableLiveData()
val password: MutableLiveData<String> = MutableLiveData()
val passwordConfirm: MutableLiveData<String> = MutableLiveData()
fun signUp() {
auth.createUser(email.value!!, password.value!!)
}
}
我想我们可以为每个输入添加四个 bool ObservableFields,在 signUp()
中我们可以检查输入并更改 bool ObservableField 的状态,这将在布局上产生出现错误
val isNameError: ObservableField<Boolean> = ObservableField()
fun signUp() {
if (name.value == null || name.value!!.length < 2 ) {
isNameError.set(true)
}
auth.createUser(email.value!!, password.value!!)
}
但我不确定 ViewModel
是否应该负责验证并向用户显示错误,我们会有样板代码
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<variable
name="viewModel"
type="com.maximdrobonoh.fitnessx.SignUpViewModel" />
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorGreyDark"
android:orientation="vertical"
android:padding="24dp">
<TextView
android:id="@+id/appTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/app_title"
android:textColor="@color/colorWhite"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/screenTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appTitle">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:text="@string/sign"
android:textColor="@color/colorWhite"
android:textSize="26sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/up"
android:textColor="@color/colorWhite"
android:textSize="26sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="24dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/screenTitle">
<android.support.v7.widget.AppCompatEditText
style="@style/SignUp.InputBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/sign_up_name"
android:inputType="textPersonName"
android:text="@={viewModel.name}" />
<android.support.v7.widget.AppCompatEditText
style="@style/SignUp.InputBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/sign_up_email"
android:inputType="textEmailAddress"
android:text="@={viewModel.email}"
/>
<android.support.v7.widget.AppCompatEditText
style="@style/SignUp.InputBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/sign_up_password"
android:inputType="textPassword"
android:text="@={viewModel.password}" />
<android.support.v7.widget.AppCompatEditText
style="@style/SignUp.InputBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/sign_up_confirm_password"
android:inputType="textPassword"
android:text="@={viewModel.passwordConfirm}" />
<Button
android:id="@+id/signUpButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/button_gradient"
android:text="@string/sign_up_next_btn"
android:textAllCaps="true"
android:textColor="@color/colorBlack" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
</layout>
最佳答案
有很多方法可以实现这一点。我告诉你两个解决方案,都很好,你可以使用你认为适合你的。
我使用 extends BaseObservable
因为我发现这比将所有字段转换为 Observers
容易。你也可以使用 ObservableFields
。
BindingAdapter
)在 xml 中
<variable
name="model"
type="sample.data.Model"/>
<EditText
passwordValidator="@{model.password}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={model.password}"/>
Model.java
public class Model extends BaseObservable {
private String password;
@Bindable
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
notifyPropertyChanged(BR.password);
}
}
DataBindingAdapter.java
public class DataBindingAdapter {
@BindingAdapter("passwordValidator")
public static void passwordValidator(EditText editText, String password) {
// ignore infinite loops
int minimumLength = 5;
if (TextUtils.isEmpty(password)) {
editText.setError(null);
return;
}
if (editText.getText().toString().length() < minimumLength) {
editText.setError("Password must be minimum " + minimumLength + " length");
} else editText.setError(null);
}
}
afterTextChanged
)在 xml 中
<variable
name="model"
type="com.innovanathinklabs.sample.data.Model"/>
<variable
name="handler"
type="sample.activities.MainActivityHandler"/>
<EditText
android:id="@+id/etPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:afterTextChanged="@{(edible)->handler.passwordValidator(edible)}"
android:text="@={model.password}"/>
MainActivityHandler.java
public class MainActivityHandler {
ActivityMainBinding binding;
public void setBinding(ActivityMainBinding binding) {
this.binding = binding;
}
public void passwordValidator(Editable editable) {
if (binding.etPassword == null) return;
int minimumLength = 5;
if (!TextUtils.isEmpty(editable.toString()) && editable.length() < minimumLength) {
binding.etPassword.setError("Password must be minimum " + minimumLength + " length");
} else {
binding.etPassword.setError(null);
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setModel(new Model());
MainActivityHandler handler = new MainActivityHandler();
handler.setBinding(binding);
binding.setHandler(handler);
}
}
更新
你也可以替换
android:afterTextChanged="@{(edible)->handler.passwordValidator(edible)}"
与
android:afterTextChanged="@{handler::passwordValidator}"
因为 android:afterTextChanged
和 passwordValidator
的参数相同。
关于android - 如何使用 ViewModel 和 Databinding 实现验证?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52371374/
Glenn Block 和我一直在研究 ViewModel 模式。我们一直在尝试找出与该模式相关的最大痛点,目标是添加框架支持以减轻痛苦。 今晚,格伦发帖,“View Model” – the mov
如果我使用 Views 创建 ViewModel 实例且 ViewModel 没有对 View 的引用的方法,我不知道创建 viewmodel 关系的正确方法是什么。 假设我们有 ChildView
当从缓存中重新加载 ViewModel 时,我需要能够拦截框架并执行重新初始化。由于没有重新创建 ViewModel,我既不能使用 Init()、MvxViewModel.InitFromBundle
我的业务模型(实际上它用于使用 Entity Framework 6 读取数据)看起来像: class Profile : NameDescriptionBase { public virtu
我在swift中玩MVVM遇到了这种情况:我为 tableView 创建了模型,其中包含对象列表和对象计数。有点像 class TableViewViewModel { var count :
我正在研究由以下部分组成的应用程序区域: Explorer - 包含 TreeView PropertyInspector - 包含一个 PropertyGrid 编辑器 - 包含一个 Explore
我正在使用 MVVM-Light,并且我有一个列出了销售人员的 DataGrid 工作。用户可以双击打开一个子窗口,该窗口将在网格上列出他们的销售,用户将能够在该网格下填写一些文本框以添加新的销售。
是否有适当的方法来创建包含 subViewModel 的 C#/WPF ViewModel ? 目标是: 我有一个主窗口。该窗口用于读取/创建图像。窗口上有一个按钮,可以在 2 个 UserContr
首先,如果这很简单,我必须道歉。我对 WPF 和 MVVM 非常陌生,我想确保我没有违反任何 WPF 或 MVVM 概念。此外,对于下面的冗长解释(试图提供所有细节): 我目前正在引用具有所有业务逻辑
我有一个架构问题,以及一个我想提出意见的可能解决方案。 我习惯于 WP7 的 MVVM 架构(尽可能但不幸的是,有时 sdk 似乎朝着相反的方向发展)。 WP7 强制采用 ViewFirst 方法,我
鉴于以下情况: ViewModelA 启动 ViewModelB(当然,通过一个通用 Controller ,它使用 Ioc 和 DI 来解析所需的类型)。 ViewModelB 需要在 ViewMo
在我的 WPF MVVM 应用程序中,使用 Caliburn.Micro,我有一个 ViewModel,CreateServiceViewModel,在单击按钮时,它会在单独的窗口中打开一个 Grid
假设我有一个采用特定 ViewModel 的页面( View ): @model IEnumerable 在这个页面中,我有一个通过另一个 ViewModel(我们称之为 PostModel)发布数据
我有两个相似的 ViewModel,我需要将一个转换为另一个。 这是第一个: using System; using System.Collections.Generic; using System.
我有两个 View 共享来自某个 View 模型的一个可观察集合,但具有不同的 Collection View 参数。在 MVVM Light 中实现它的正确方法是什么?是否支持非静态虚拟机?我如何管
我有 2 个 View 模型 - 主视图模型** 存储 View 模型 StorageViewModel.kt class StorageViewModel @ViewModelInject cons
我有一个 AddressesViewModel 保存用户最喜欢的地址,另一个 SearchViewModel 保存搜索到的地址。当用户搜索地址时,我必须通过检查收藏夹数组来检查该地址是否是收藏夹。正确
首先,我看过this post并没有找到我的问题的答案。 我不确定这是否是汇总 型号 类或聚合 查看型号 类,但这就是我所拥有的: 在我的 WPF(使用 Prism)应用程序中,我有一个 View “
我的 View 中有这些过滤器,它们都会更新 FilterViewModel,然后由它负责过滤数据。其中一个 View ,SearchAddressView 需要 PlacemarkViewModel
依赖的 ViewModel 通过构造函数(IoC 容器)注入(inject)。 示例:ProductSelectionViewModel 使用 ShoppingBasketViewModel。 这是一
我是一名优秀的程序员,十分优秀!