Angular如何在表单提交上获取按钮元素

时间:2019-04-29 18:12:33

标签: javascript angular

我的页面上有多种形式。每个表单上都有许多按钮。我想在按钮按下后实现加载微调器。当我使用常规点击事件时,我可以传递按钮:

HTML

<button #cancelButton class="button-with-icon" type="button" *ngIf="hasCancel" mat-raised-button color="warn" [disabled]="isLoading" (click)="clickEvent(cancelButton)">
    <mat-spinner *ngIf="isLoading" style="display: inline-block;" diameter="24"></mat-spinner>
    Cancel
</button>

TS

clickEvent(button: MatButton) {
    console.log(button);
}

在这种情况下,button元素被传递了,您可以对其进行访问以便将加载类添加到按钮。

但是,如果您使用“提交”按钮尝试相同的操作,则会显示为undefined

HTML

<form (ngSubmit)="save(saveButton)">
    <button #saveButton class="button-with-icon" type="submit" *ngIf="hasSave" mat-raised-button color="primary" [disabled]="isLoading">
        <mat-spinner *ngIf="isLoading" style="display: inline-block;" diameter="24"></mat-spinner>
        Save
    </button>
</form>

TS

save(button: MatButton) {
    console.log(button);
}

在这种情况下,按钮为undefined,因为按钮上的*ngIf阻止了它进入表单范围。我可以删除*ngIf并仅隐藏按钮,但这会将按钮留在DOM上,我不想这样做。

这是一个堆叠闪电战:https://stackblitz.com/edit/angular-zh7jcw-mrqaok?file=app%2Fform-field-overview-example.html

我曾尝试在保存按钮上添加一个额外的单击事件,以将按钮设置为加载,但是在阻止提交事件被调用之前,单击事件会先触发并将按钮设置为禁用。

我已经仔细查看了Submit事件,但是看不到任何链接到所单击按钮的内容。

有什么建议吗?

5 个答案:

答案 0 :(得分:3)

更新,我现在了解您的特定问题。问题是按钮上的*ngIf。它打破了参考。将其更改为[hidden],它应该可以工作。这是更新的StackBlitz:https://stackblitz.com/edit/angular-zh7jcw-pcuuyv

关于发生这种情况的原因,我认为这可以很好地描述问题:https://stackoverflow.com/a/36651625/64279

原始答案:

我什至都不会传递它。只需在您的班级中添加它即可:

 @ViewChild('saveButton') button;

然后您可以在保存方法中引用它:

save() {
    console.log(this.saveButton);
}

但是我还要补充一点,从插值调用函数不是一个好主意,因为它可能会导致性能问题。相反,您应该订阅一个设置属性的事件,然后在视图中引用该属性。这样,仅在事件触发时才调用该函数,而不是页面的每次呈现。

答案 1 :(得分:3)

tl; dr 这是我的解决方案:https://stackblitz.com/edit/angular-zh7jcw-ythpdb


编辑:正如我刚刚注意到的那样,@ AJT_82很好地解释了OP的原始问题,其中引用元素非常好。因此,我现在删除了先前的答案。


但是,我建议以下解决方案在关注点分离以有角度的方式做事方面更合适,即引入两个新成员{ {1}}和cancelActive,您将能够更灵活地处理表单的不同状态。

在这里您可以看到它的运行情况:https://stackblitz.com/edit/angular-zh7jcw-ythpdb

submitActive
export class FormFieldOverviewExample {
  cancelActive = false;
  submitActive = false;

  // this is for submit event
  saveEvent(event: Event) {
    // additional logic can be added
    // if(!this.cancelActive) return;

    this.submitActive = true;
    this.cancelActive = false;
  }

  // this is for cancel event
  cancelEvent(event: Event) {
    // additional logic can be added
    // if(!this.submitActive) return;

    this.submitActive = true;
    this.cancelActive = false;
  }

}

这样,您还将拥有更多的游乐场,例如:

<form (ngSubmit)="saveEvent($event)">
  <button  class="button-with-icon"  type="button"  *ngIf="hasCancel"  mat-raised-button color="warn"  [disabled]="cancelActive"  (click)="cancelEvent($event)">
    <mat-spinner *ngIf="cancelActive" class="spinner" diameter="15"></mat-spinner>
    Cancel
  </button>
  <br><br>
  <button class="button-with-icon" type="submit" *ngIf="hasSave" mat-raised-button color="primary" [disabled]="submitActive">
    <mat-spinner *ngIf="submitActive" class="spinner" diameter="15"></mat-spinner>
    Save
  </button>
</form>

如果您出于某些原因想要执行此操作,则只需禁用<button type="submit" [disabled]="submitActive || cancelActive">...</button> 按钮而不显示其加载动画...

此外,假设您的表单中有一个submit元素。用户切换到该元素(通过单击或按下Tab键),并在完成编辑后按Enter键,在这种情况下,浏览器也会自动提交表单;因此这会使现有代码结构中的事情变得更加复杂。

答案 2 :(得分:2)

在表单上使用ngSubmit事件处理程序,而不是在表单上使用click事件处理程序。将类型保留为submit,以便当用户在输入控件上使用Enter键提交表单时,该类型仍然有效。

<form #formElement="ngForm">
    ...
    <button #saveButton (click)="save(saveButton, formElement)" type="submit" ...>
        <mat-spinner ...></mat-spinner>
        Save
    </button>
</form>

通过使用一个对象来跟踪哪个按钮处于加载状态,您还可以避免将loading类添加到DOM。工作示例here。这也使您可以拥有许多按钮,而不必为每个按钮创建一个固定变量。

class FormFieldOverviewExample {
  
  hasCancel = true;
  hasSave = true;
  isLoading = {};

  saveEvent(event, buttonName, form) {
    console.log(buttonName);
    if(form.valid) {
      this.isLoading[buttonName]=true;
      // ...
    } else {
      this.isLoading[buttonName]=false;
      console.warn("Form not submitted because it contains errors");
    }
  }

  clickEvent(event, buttonName) {
    console.log(buttonName);
    this.isLoading[buttonName]=true;
    // ...
  }
}
form { display: none; }
<form #formElement="ngForm">

  ...

  <button (click)="clickEvent($event, 'cancelButton')" [disabled]="isLoading['cancelButton']" *ngIf="hasCancel" type="button" ...>
    <mat-spinner *ngIf="isLoading['cancelButton']" ...></mat-spinner>
    Cancel
  </button> 
  
  <br><br>
  
  <button (click)="saveEvent($event, 'saveButton', formElement)"  [disabled]="isLoading['saveButton']" *ngIf="hasSave" type="submit" ...>
    <mat-spinner *ngIf="isLoading['saveButton']" ...></mat-spinner>
    Save
  </button>
</form>

答案 3 :(得分:2)

这实际上与Submit函数无关,而与angular和structure指令有关。当您应用结构指令(ngForngIf ...)时,幕后的作用是创建一个ng-template节点。这意味着,在该节点中可以使用在结构指令的范围内定义的模板引用变量。

那么您在这里拥有什么

<button #saveButton *ngIf="hasSave">
  Save
</button>

意味着saveButton模板引用仅在为按钮创建的ng-template内部可用,因此在提交功能中不可用。

我们在这里拥有什么

<button #cancelButton *ngIf="hasCancel" (click)="clickEvent(cancelButton)">
  Cancel
</button>

是单击事件在按钮上,因此cancelButton模板引用变量可用于该事件。

使用hidden代替其他答案中建议的ngIf,可以解决此问题,因为我们只是隐藏元素。

答案 4 :(得分:1)

针对您的情况,您不应将元素传递给函数,然后在其上操作类,而只需维护一个变量以加载状态。在您的组件中,将其初始化为

loading: boolean = false;

然后在您的模板中,将此类绑定为

[class.loading]="loading"

在您的方法中,首先将该属性设置为true,然后在异步调用完成后将其设置为false。类似地,使用相同的变量将禁用属性设置为

[disabled]="loading"