NativeScript:显示 ActivityIndicator 时禁用所有控件
NativeScript: Disable all controls while ActivityIndicator is shown
假设有一个带有 username\password TextFields 和登录按钮的登录页面。当按钮被按下时,一个请求被设置到服务器并显示 ActivityIndicator。
目前,我将 StackLayout 放在所有其他控件之上,以免用户在处理请求时点击它们。但在某些情况下,TextField 保持焦点并且用户可以在那里键入。
我已经在使用一个组件来包装所有 TextField 以显示验证错误:
@Component({
selector: "field",
template: "<grid-layout><ng-content></ng-content>...</grid-layout>"
})
export class FieldComponent {
@ContentChild(NgModel) private input: NgModel;
...
}
我的问题是我可以将 ng-content
内的 TextField 上的 isEnabled 属性 设置为 false 从 FieldComponent
具有 NgModel
或其他方式吗?
如果不可能,在这种情况下,当应用程序繁忙时禁用输入的最佳做法是什么?
有几种方法可以做到这一点;
您可以使用 ngIf
或绑定 isEnabled
以基于数据绑定值禁用它。
您可以创建一个您调用的简单例程(我的首选方法)。
require("nativescript-dom");
function screenEnabled(isEnabled) {
runAgainstTagNames('TextEdit', function(e) { e.isEnabled = isEnabled; });
runAgainstTagNames('Button', function(e) { e.isEnabled = isEnabled; });
}
nativescript-dom
插件具有 runAgainst* 或 getElementBy* 包装器,可以像与 html dom 对话一样与本机层对话。
完全公开,我是 nativescript-dom
的作者,它是我几乎在每个 app/demo 中使用的插件之一。
这是我针对 NativeScript+Angular 的解决方案:
- setControlInteractionState() 是递归的。
- TextField 光标已隐藏(使用本机 android API)。
XML:
<GridLayout #mainGrid rows="*" columns="*">
<!-- Main page content here... -->
<GridLayout *ngIf="isBusy" rows="*" columns="*">
<GridLayout rows="*" columns="*" style="background-color: black; opacity: 0.35">
</GridLayout>
<ActivityIndicator width="60" height="60" busy="true">
</ActivityIndicator>
</GridLayout>
</GridLayout>
或
<GridLayout #mainGrid rows="*" columns="*">
<!-- Main page content here... -->
</GridLayout>
<GridLayout *ngIf="isBusy" rows="*" columns="*">
<GridLayout rows="*" columns="*" style="background-color: black; opacity: 0.35">
</GridLayout>
<ActivityIndicator width="60" height="60" busy="true">
</ActivityIndicator>
</GridLayout>
TypeScript:
import { Component, ViewChild, ElementRef } from "@angular/core";
import { View } from "ui/core/view";
import { LayoutBase } from "ui/layouts/layout-base";
import { isAndroid, isIOS } from "platform";
@Component({
templateUrl: "./SignIn.html"
})
export class SignInComponent {
@ViewChild("mainGrid")
MainGrid: ElementRef;
isBusy: boolean = false;
submit() : void {
try {
this.isBusy = true;
setControlInteractionState(<View>this.MainGrid.nativeElement, false);
//sign-in here...
}
finally {
this.isBusy = false;
setControlInteractionState(<View>this.MainGrid.nativeElement, true);
}
}
setControlInteractionState(view: View, isEnabled: boolean) : void {
view.isUserInteractionEnabled = isEnabled;
if (isAndroid) {
if (view.android instanceof android.widget.EditText) {
let control = <android.widget.EditText>view.android;
control.setCursorVisible(isEnabled);
}
}
if (view instanceof LayoutBase) {
let layoutBase = <LayoutBase>view;
for (let i = 0, length = layoutBase.getChildrenCount(); i < length; i++) {
let child = layoutBase.getChildAt(i);
setControlInteractionState(child, isEnabled);
}
}
}
}
NS 2.5.0
扩展@KTCO 的答案以获得与主网格完全相同的叠加层大小:
import { Size, View } from "tns-core-modules/ui/core/view";
import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout/grid-layout";
...
...
dialogSize: Size;
mainGrid: GridLayout;
...
submit() {
this.mainGrid = <GridLayout>this.MainGrid.nativeElement;
this.dialogSize = this.mainGrid.getActualSize();
.....
.....
<GridLayout *ngIf="isBusy" rows="auto" columns="auto">
<GridLayout rows="*" columns="*" [width]="dialogSize.width" [height]="dialogSize.height" style="background-color: black; opacity: 0.35">
</GridLayout>
<ActivityIndicator width="50" height="50" busy="true">
</ActivityIndicator>
</GridLayout>
我知道这有点老了,但有时我只是按自己的方式做事。如果您想以编程方式完成此操作:
const excludedView = someViewToBeExcluded;
const enabler = (parentView:View, enable:boolean) => {
parentView.eachChildView(childView => {
if (childView != excludedView) {
enabler(childView, enable);
childView.isEnabled = enable;
}
return true;
});
};
enabler(page, false);
注意:这不会 disable/enable 初始 parentView
(即本例中的 page
)
我在 Angular 中找到了一个最简单的解决方案,因此我在此处发布以供将来参考。首先在 app.component.html
文件中我添加了一个网格和 ScrollView
如下所示:
<GridLayout>
<page-router-outlet></page-router-outlet>
<!-- hack to block UI -->
<ScrollView isUserInteractionEnabled="false" *ngIf="isLoading">
<ActivityIndicator busy="true"></ActivityIndicator>
</ScrollView>
</GridLayout>
注意网格内的 page-router-outlet
。默认情况下,它将放置在 row="0" 处。接下来是 ScrollView
,它已将 isUserInteractionEnabled 设置为 false。
现在在你的 app.component.ts
文件中添加一个名为 isLoading
的变量并使用某种事件切换它,例如 RxJs Observable events
假设有一个带有 username\password TextFields 和登录按钮的登录页面。当按钮被按下时,一个请求被设置到服务器并显示 ActivityIndicator。 目前,我将 StackLayout 放在所有其他控件之上,以免用户在处理请求时点击它们。但在某些情况下,TextField 保持焦点并且用户可以在那里键入。
我已经在使用一个组件来包装所有 TextField 以显示验证错误:
@Component({
selector: "field",
template: "<grid-layout><ng-content></ng-content>...</grid-layout>"
})
export class FieldComponent {
@ContentChild(NgModel) private input: NgModel;
...
}
我的问题是我可以将 ng-content
内的 TextField 上的 isEnabled 属性 设置为 false 从 FieldComponent
具有 NgModel
或其他方式吗?
如果不可能,在这种情况下,当应用程序繁忙时禁用输入的最佳做法是什么?
有几种方法可以做到这一点;
您可以使用
ngIf
或绑定isEnabled
以基于数据绑定值禁用它。您可以创建一个您调用的简单例程(我的首选方法)。
require("nativescript-dom");
function screenEnabled(isEnabled) {
runAgainstTagNames('TextEdit', function(e) { e.isEnabled = isEnabled; });
runAgainstTagNames('Button', function(e) { e.isEnabled = isEnabled; });
}
nativescript-dom
插件具有 runAgainst* 或 getElementBy* 包装器,可以像与 html dom 对话一样与本机层对话。
完全公开,我是 nativescript-dom
的作者,它是我几乎在每个 app/demo 中使用的插件之一。
这是我针对 NativeScript+Angular 的解决方案:
- setControlInteractionState() 是递归的。
- TextField 光标已隐藏(使用本机 android API)。
XML:
<GridLayout #mainGrid rows="*" columns="*">
<!-- Main page content here... -->
<GridLayout *ngIf="isBusy" rows="*" columns="*">
<GridLayout rows="*" columns="*" style="background-color: black; opacity: 0.35">
</GridLayout>
<ActivityIndicator width="60" height="60" busy="true">
</ActivityIndicator>
</GridLayout>
</GridLayout>
或
<GridLayout #mainGrid rows="*" columns="*">
<!-- Main page content here... -->
</GridLayout>
<GridLayout *ngIf="isBusy" rows="*" columns="*">
<GridLayout rows="*" columns="*" style="background-color: black; opacity: 0.35">
</GridLayout>
<ActivityIndicator width="60" height="60" busy="true">
</ActivityIndicator>
</GridLayout>
TypeScript:
import { Component, ViewChild, ElementRef } from "@angular/core";
import { View } from "ui/core/view";
import { LayoutBase } from "ui/layouts/layout-base";
import { isAndroid, isIOS } from "platform";
@Component({
templateUrl: "./SignIn.html"
})
export class SignInComponent {
@ViewChild("mainGrid")
MainGrid: ElementRef;
isBusy: boolean = false;
submit() : void {
try {
this.isBusy = true;
setControlInteractionState(<View>this.MainGrid.nativeElement, false);
//sign-in here...
}
finally {
this.isBusy = false;
setControlInteractionState(<View>this.MainGrid.nativeElement, true);
}
}
setControlInteractionState(view: View, isEnabled: boolean) : void {
view.isUserInteractionEnabled = isEnabled;
if (isAndroid) {
if (view.android instanceof android.widget.EditText) {
let control = <android.widget.EditText>view.android;
control.setCursorVisible(isEnabled);
}
}
if (view instanceof LayoutBase) {
let layoutBase = <LayoutBase>view;
for (let i = 0, length = layoutBase.getChildrenCount(); i < length; i++) {
let child = layoutBase.getChildAt(i);
setControlInteractionState(child, isEnabled);
}
}
}
}
NS 2.5.0
扩展@KTCO 的答案以获得与主网格完全相同的叠加层大小:
import { Size, View } from "tns-core-modules/ui/core/view";
import { GridLayout } from "tns-core-modules/ui/layouts/grid-layout/grid-layout";
...
...
dialogSize: Size;
mainGrid: GridLayout;
...
submit() {
this.mainGrid = <GridLayout>this.MainGrid.nativeElement;
this.dialogSize = this.mainGrid.getActualSize();
.....
.....
<GridLayout *ngIf="isBusy" rows="auto" columns="auto">
<GridLayout rows="*" columns="*" [width]="dialogSize.width" [height]="dialogSize.height" style="background-color: black; opacity: 0.35">
</GridLayout>
<ActivityIndicator width="50" height="50" busy="true">
</ActivityIndicator>
</GridLayout>
我知道这有点老了,但有时我只是按自己的方式做事。如果您想以编程方式完成此操作:
const excludedView = someViewToBeExcluded;
const enabler = (parentView:View, enable:boolean) => {
parentView.eachChildView(childView => {
if (childView != excludedView) {
enabler(childView, enable);
childView.isEnabled = enable;
}
return true;
});
};
enabler(page, false);
注意:这不会 disable/enable 初始 parentView
(即本例中的 page
)
我在 Angular 中找到了一个最简单的解决方案,因此我在此处发布以供将来参考。首先在 app.component.html
文件中我添加了一个网格和 ScrollView
如下所示:
<GridLayout>
<page-router-outlet></page-router-outlet>
<!-- hack to block UI -->
<ScrollView isUserInteractionEnabled="false" *ngIf="isLoading">
<ActivityIndicator busy="true"></ActivityIndicator>
</ScrollView>
</GridLayout>
注意网格内的 page-router-outlet
。默认情况下,它将放置在 row="0" 处。接下来是 ScrollView
,它已将 isUserInteractionEnabled 设置为 false。
现在在你的 app.component.ts
文件中添加一个名为 isLoading
的变量并使用某种事件切换它,例如 RxJs Observable events