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

Formly自定义包装器+自定义类型:mat-autocomplete布局样式问题求助

问题描述

我需要在Formly表单中使用mat-autocomplete,并在每个自动完成组件末尾添加添加/删除按钮,核心要求是自动完成组件+按钮的组合占满全部可用宽度。

我已经实现了自定义的autocomplete类型,以及用于添加按钮的包装器(示例中仅保留删除按钮),但遇到两个核心问题:

  • 当包装器与自定义类型结合使用时,mat-form-field样式混乱。临时修复方式是:移除自定义类型声明中的"form-field"包装器,在autocomplete自定义类型组件中手动添加<mat-form-field>标签。
  • 修复后布局宽度异常:组合要么仅占约33%的可用宽度,要么给<mat-form-field>设置100%宽度后,按钮被挤到下一行,不符合预期。

相关代码

app.component.html

<form [formGroup]="form">
  <formly-form [model]="model" [fields]="fields" [options]="options" [form]="form"></formly-form>
</form>

<!-- Copyright 2021 Formly. All Rights Reserved.
    Use of this source code is governed by an MIT-style license that
    can be found in the LICENSE file at https://github.com/ngx-formly/ngx-formly/blob/main/LICENSE -->

app.component.ts

import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FormlyFormOptions, FormlyFieldConfig } from '@ngx-formly/core';
import { of } from 'rxjs';

const states = [
  'Alabama',
  'Alaska',
  'American Samoa',
  'Arizona',
  'Arkansas',
  'California',
  'Colorado',
  'Connecticut',
  'Delaware',
  'District Of Columbia',
  'Federated States Of Micronesia',
  'Florida',
  'Georgia',
  'Guam',
  'Hawaii',
  'Idaho',
  'Illinois',
  'Indiana',
  'Iowa',
  'Kansas',
  'Kentucky',
  'Louisiana',
  'Maine',
  'Marshall Islands',
  'Maryland',
  'Massachusetts',
  'Michigan',
  'Minnesota',
  'Mississippi',
  'Missouri',
  'Montana',
  'Nebraska',
  'Nevada',
  'New Hampshire',
  'New Jersey',
  'New Mexico',
  'New York',
  'North Carolina',
  'North Dakota',
  'Northern Mariana Islands',
  'Ohio',
  'Oklahoma',
  'Oregon',
  'Palau',
  'Pennsylvania',
  'Puerto Rico',
  'Rhode Island',
  'South Carolina',
  'South Dakota',
  'Tennessee',
  'Texas',
  'Utah',
  'Vermont',
  'Virgin Islands',
  'Virginia',
  'Washington',
  'West Virginia',
  'Wisconsin',
  'Wyoming',
];

@Component({
  selector: 'formly-app-example',
  templateUrl: './app.component.html',
})
export class AppComponent {
  form = new FormGroup({});
  model: any = {};
  options: FormlyFormOptions = {};
  fields: FormlyFieldConfig[] = [
    {
      key: 'Autocomplete',
      type: 'autocomplete',
      wrappers: ['buttonWrapper'],
      props: {
        required: true,
        label: 'Autocomplete',
        placeholder: 'Placeholder',
        filter: (term: string) =>
          of(term ? this.filterStates(term) : states.slice()),
      },
    },
  ];

  filterStates(name: string) {
    return states.filter(
      (state) => state.toLowerCase().indexOf(name.toLowerCase()) === 0
    );
  }
}

/**  Copyright 2021 Formly. All Rights Reserved.
    Use of this source code is governed by an MIT-style license that
    can be found in the LICENSE file at https://github.com/ngx-formly/ngx-formly/blob/main/LICENSE */

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ReactiveFormsModule } from '@angular/forms';
import { FormlyModule } from '@ngx-formly/core';
import { FormlyMaterialModule } from '@ngx-formly/material';

import { MatInputModule } from '@angular/material/input';
import { MatAutocompleteModule } from '@angular/material/autocomplete';

import { AppComponent } from './app.component';
import { AutocompleteTypeComponent } from './autocomplete-type.component';
import { AutcompleteButtonWrapperComponent } from './custom-wrapper.component';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';

@NgModule({
  imports: [
    BrowserAnimationsModule,
    ReactiveFormsModule,
    MatInputModule,
    MatButtonModule,
    MatIconModule,
    MatAutocompleteModule,
    FormlyMaterialModule,
    FormlyModule.forRoot({
      types: [
        {
          name: 'autocomplete',
          component: AutocompleteTypeComponent,
          wrappers: ['form-field'], // 注释该行并在autocomplete类型中手动添加mat-form-field标签以调整样式
        },
      ],
      wrappers: [
        {
          name: 'buttonWrapper',
          component: AutcompleteButtonWrapperComponent,
        },
      ],
      validationMessages: [
        { name: 'required', message: 'This field is required' },
      ],
    }),
  ],
  bootstrap: [AppComponent],
  declarations: [
    AutcompleteButtonWrapperComponent,
    AutocompleteTypeComponent,
    AppComponent,
  ],
})
export class AppModule {}

/**  Copyright 2021 Formly. All Rights Reserved.
    Use of this source code is governed by an MIT-style license that
    can be found in the LICENSE file at https://github.com/ngx-formly/ngx-formly/blob/main/LICENSE */

custom-wrapper.component.ts

import { Component } from '@angular/core';
import { FieldWrapper } from '@ngx-formly/core';

@Component({
  selector: 'formly-wrapper-button',
  template: `
    <div style="display: flex; align-items: center; width: 100%;">
      <ng-container #fieldComponent></ng-container>
      <button mat-icon-button color="warn"><mat-icon>remove</mat-icon></button>
    </div>
  `,
})
export class AutcompleteButtonWrapperComponent extends FieldWrapper {}

/**  Copyright 2021 Formly. All Rights Reserved.
    Use of this source code is governed by an MIT-style license that
    can be found in the LICENSE file at https://github.com/ngx-formly/ngx-formly/blob/main/LICENSE */

(注:用户提供的autocomplete-type.component.ts代码与app.module.ts重复,推测为复制错误,正确组件代码应包含<mat-form-field>相关模板)

解决思路

针对你的两个问题,给出以下具体修改方案:

1. 修复mat-form-field样式混乱

保留你提到的临时修复方向,调整层级与样式:

  • 在app.module.ts的自定义类型声明中,移除'form-field'包装器:
    types: [
      {
        name: 'autocomplete',
        component: AutocompleteTypeComponent,
        // 移除 wrappers: ['form-field']
      },
    ],
    
  • 在autocomplete-type.component.ts的模板中,手动添加<mat-form-field>并设置flex: 1,让它在flex容器中占据剩余空间:
    <mat-form-field style="flex: 1; margin: 0;">
      <input type="text" matInput [formControl]="formControl" [matAutocomplete]="auto" [placeholder]="props.placeholder">
      <mat-label>{{ props.label }}</mat-label>
      <mat-autocomplete #auto="matAutocomplete">
        <mat-option *ngFor="let option of filteredOptions | async" [value]="option">
          {{ option }}
        </mat-option>
      </mat-autocomplete>
      <mat-error *ngIf="showError">{{ errorMessage }}</mat-error>
    </mat-form-field>
    

2. 修复布局宽度异常、按钮换行问题

调整自定义包装器的flex布局配置:
修改custom-wrapper.component.ts的模板,给自动完成组件添加容器并设置flex:1,同时添加间距避免元素紧贴:

<div style="display: flex; align-items: center; width: 100%; gap: 8px;">
  <div style="flex: 1;">
    <ng-container #fieldComponent></ng-container>
  </div>
  <button mat-icon-button color="warn"><mat-icon>remove</mat-icon></button>
</div>
  • gap:8px:给输入框和按钮之间添加固定间距
  • 包裹fieldComponent的<div>设置flex:1:让内部的mat-form-field占满该区域
  • 按钮保持默认宽度:不会挤压输入框,也不会被挤到下一行

额外优化

可以把内联样式提取到CSS类中,便于维护:
在包装器组件的CSS文件中定义:

.wrapper-container {
  display: flex;
  align-items: center;
  width: 100%;
  gap: 8px;
}
.field-container {
  flex: 1;
}

模板中替换为类名:

<div class="wrapper-container">
  <div class="field-container">
    <ng-container #fieldComponent></ng-container>
  </div>
  <button mat-icon-button color="warn"><mat-icon>remove</mat-icon></button>
</div>

内容的提问来源于stack exchange,提问作者Martin Carre

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:34:52