You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular使用zxing-js扫码填充FormBuilder表单提交值为空

问题根因

直接通过原生JS操作DOM给input设置value的写法,完全绕过了Angular响应式表单(FormBuilder)的状态更新机制:

  • 键盘输入时,Angular内置的表单指令会监听输入事件,自动把输入内容同步到FormControl的内部状态,所以提交时能正常拿到值
  • 原生document.getElementById("codEtiqueta").value = xxx的写法只会修改DOM的显示内容,不会触发Angular表单的状态更新,FormControl存储的实际值还是空,提交自然拿不到扫码填充的内容
  • 额外存在两个隐患:一是在响应式表单上混用[(ngModel)]模板驱动写法,Angular官方明确不推荐这种用法,极易出现值不同步问题;二是提交按钮上绑定的onclick="location.href='#'"会触发页面跳转,可能打断正常的表单提交流程。
修复方案

核心原则

所有表单值操作优先使用Angular响应式表单提供的API,不要直接操作DOM修改输入框内容;移除游离在Angular组件外的原生全局JS函数,把扫码逻辑整合到组件内部。

具体修改步骤

  1. 移除原生全局的decodeOnce函数,把zxing扫码逻辑移到Angular组件内,扫码成功后直接调用表单控件的setValue/patchValue方法更新值,Angular会自动把值同步到输入框视图,同时更新表单内部状态
  2. 移除numRomaneio输入框上冗余的[(ngModel)]、name="name"属性,统一通过响应式表单API操作该字段值
  3. 删除提交按钮上的onclick="location.href='#'"属性,避免不必要的页面跳转
  4. 增加组件销毁逻辑,扫码结束后及时释放摄像头资源

修正后的组件TS核心代码

// 按实际安装的zxing包路径导入依赖
import { BrowserMultiFormatReader } from '@zxing/library';
import { OnInit, AfterViewInit, OnDestroy } from '@angular/core';

export class QrcodeScanComponent implements OnInit, AfterViewInit, OnDestroy {
  formulario!: FormGroup;
  // 实例化二维码读取器
  private codeReader = new BrowserMultiFormatReader();

  romaneio: Romaneio = {
    id: 0,
    status: ''
  }
  idRomaneio!: number;
  numcarga!: number;
  id!: string;
  itens!: Item[];
  displayedColumns = ['cod_fab', 'descricao', 'qac', 'qc', 'faltam']

  constructor(
    private qrcodeapiService: QrcodeapiService, 
    private fb: FormBuilder, 
    private router: Router, 
    private route: ActivatedRoute
  ) { }

  ngOnInit(): void {
    this.formulario = this.fb.group({
      codEtiqueta: ['', Validators.required],
      numRomaneio: [0, Validators.required]
    })
    this.romaneio.id = this.idRomaneio;
    // 初始化单号字段值,无需依赖ngModel
    this.formulario.get('numRomaneio')?.setValue(this.romaneio.id);
  }

  ngAfterViewInit(): void {
    // 视图渲染完成后启动扫码
    this.startScan();
  }

  // 整合原全局扫码逻辑到组件内部
  private startScan() {
    // 默认调起设备后置摄像头,如需选择设备可提前枚举摄像头列表
    this.codeReader.decodeFromVideoDevice(undefined, 'video', (result, err) => {
      if (result) {
        const scanText = result.getText();
        console.log('扫码结果:', scanText);
        // 核心操作:直接更新表单控件值,禁止操作DOM改input value
        this.formulario.get('codEtiqueta')?.setValue(scanText);
        // 如需扫到结果后停止扫码可放开下一行注释
        // this.codeReader.reset();
      }
      if (err) console.error('扫码异常:', err);
    })
  }

  buscarItens(){
    this.qrcodeapiService.listar(this.romaneio.id).subscribe(itens =>{
      this.itens = itens
      console.log(itens)
    })
  }

  submit(){
    if (this.formulario.invalid) return;
    const formValues = this.formulario.value;
    const etiqueta: Etiqueta = new Etiqueta(formValues.codEtiqueta, formValues.numRomaneio);
    this.qrcodeapiService.salvar(etiqueta).subscribe(() =>{
      this.qrcodeapiService.showMessage('Registrado')
      // 重置表单统一使用响应式API
      this.formulario.patchValue({
        codEtiqueta: '',
        numRomaneio: this.romaneio.id
      })
      this.formulario.get('codEtiqueta')?.markAsUntouched();
      this.buscarItens();
    });
  }

  ngOnDestroy(): void {
    // 组件销毁时释放摄像头资源
    this.codeReader.reset();
  }
}

修正后的表单模板核心代码

<form [formGroup]="formulario" (ngSubmit)="submit()" action="#">
  <div class="divEtiqueta">
    <mat-form-field class="inputEtq">
      <input
        formControlName="numRomaneio"
        matInput
        placeholder="Nº Romaneio"
      />
    </mat-form-field>
    <mat-form-field class="inputEtq">
      <input
        matInput
        id="codEtiqueta"
        formControlName="codEtiqueta"
        placeholder="Cód. Etiqueta"
      />
    </mat-form-field>
  </div>
  <section class="container" id="demo-content">
    <div>
      <video
        id="video"
        width="95%"
        height="300"
        style="border: 1px solid gray"
      ></video>
    </div>
    <div class="buttonConfirmar">
      <button
        type="submit" 
        class="btnConfirmar"
        id="confirmarButton"
        mat-raised-button
      >Confirmar
      </button>
    </div>
  </section>
</form>

内容的提问来源于stack exchange,提问作者Guilherme Marques

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 21:24:09