【问题标题】:NativeScript: Disable all controls while ActivityIndicator is shownNativeScript:在显示 ActivityIndi​​cator 时禁用所有控件
【发布时间】:2016-12-06 04:59:07
【问题描述】:

假设有一个登录页面,其中包含用户名\密码文本字段和登录按钮。当按下按钮时,请求被设置到服务器并显示 ActivityIndi​​cator。 目前,我将 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 或其他方式吗? 如果不可能在这种情况下在应用程序繁忙时禁用输入的最佳做法是什么?

【问题讨论】:

  • 根据您的设计要求,您应该能够使用 nativescript-loading-indicator 插件来显示模态指示器。显示模式时,您将无法与屏幕上的元素进行交互。 github.com/NathanWalker/nativescript-loading-indicator
  • 谢谢,我去看看。但看起来它只是添加了一个额外的层,如果元素已经聚焦,我不确定它是否会禁用输入

标签: android angular nativescript angular2-nativescript


【解决方案1】:

这是我的 NativeScript+Angular 解决方案:

  1. setControlInteractionState() 是递归的。
  2. 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

【讨论】:

  • 这很好用。如果包含 ActivityIndi​​cator 的网格可以完全覆盖#mainGrid,那将是理想的。有什么想法吗?
【解决方案2】:

有几种方法可以做到这一点;

  1. 您可以使用ngIfisEnabled 上的绑定来根据数据绑定值禁用它。

  2. 您可以创建一个您调用的简单例程(我的首选方法)。

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 的作者,它是我在几乎所有应用程序/演示中使用的插件之一。

【讨论】:

    【解决方案3】:

    我在 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 事件)切换它

    【讨论】:

      【解决方案4】:

      扩展@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>
      

      【讨论】:

        【解决方案5】:

        我知道这有点老了,但有时我只能按照自己的方式做事。如果您想以编程方式完成此操作:

        
        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);
        
        

        注意:这不会禁用/启用初始 parentView(即本例中的 page

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-05
          • 2018-04-14
          相关资源
          最近更新 更多