在Angular 2中填充动态表单中的下拉列表

时间:2018-06-16 12:39:16

标签: angular forms dynamic field dropdown

我有这种情况:

这是我的FieldDefinition

 export interface FieldDefinition {
    key: string,
    type: string,
    isId: boolean,
    label: string,
    required: boolean
 }

这是动态字段定义

<div *ngSwitchCase="'string'" class="form-group">
  <label [htmlFor]="field.key">{{ field.label }}</label>
  <textarea class="form-control" [formControlName]="field.key" [id]="field.key"
          [readonly]="operation == 'details' || field.isId"></textarea>
</div>

<div *ngSwitchCase="'number'" class="form-group">
  <label [htmlFor]="field.key">{{ field.label }}</label>
  <input class="form-control" [formControlName]="field.key" [id]="field.key" 
          [readonly]="operation == 'details' || field.isId"
          type="number">
</div>

<div *ngSwitchCase="'[]'" class="form-group">
  <label [htmlFor]="field.key">{{ field.label }}</label>
  <select [id]="field.key"
          [formControlName]="field.key">
    <option *ngFor="let opt of field.options"
            [value]="opt.key">
      {{opt.value}}
    </option>
  </select>
</div>

<div *ngIf="form.get(field.key).hasError('required') && (submitted || form.get(field.key).touched)"
      class="alert alert-danger">
  {{ field.label }} is required.
</div>

</div>
</div>

ts文件:

 import { Component, Input, OnInit } from '@angular/core';
 import { FormGroup } from '@angular/forms';
 import { FieldDefinition } from '../field-definition';

 @Component({
    selector: 'fw-dynamic-field',
    templateUrl: './dynamic-field.component.html',
    styleUrls: ['./dynamic-field.component.css']
})
export class DynamicFieldComponent {
   @Input() field: FieldDefinition;
   @Input() form: FormGroup;
   @Input() operation: string;
   @Input() submitted: boolean;

   get isValid() { return this.form.controls[this.field.key].valid; }

   constructor() { }
 }

动态表单html

  <form (ngSubmit)="onSubmit()" [formGroup]="form">
 <div *ngFor="let field of vmDefinition">
  <fw-dynamic-field [field]="field" 
                    [form]="form" 
                    [operation]="operation" 
                    [submitted]="submitted">
  </fw-dynamic-field>
</div>

<div *ngIf="errorMessage " class="alert alert-danger">
  {{ errorMessage }}
</div>

<div *ngIf="status != 'waiting'">
  <div *ngIf="operation === 'details'">
    <button type="button" class="btn" (click)="onBack()" >Back</button>
    <button type="button" class="btn btn-primary" (click)="onEdit()" >Edit</button>
  </div>
  <div *ngIf="operation === 'edit'">
    <button type="button" class="btn" (click)="onCancel()" >Cancel</button>
    <button type="button" class="btn btn-primary" (click)="onSave()" >Save</button>
  </div>
  <div *ngIf="operation === 'create'">
    <button type="button" class="btn" (click)="onBack()" >Back</button>
    <button type="button" class="btn btn-primary" (click)="onCreate()" >Create</button>
  </div>
</div>

</form>

和ts文件:

 export class DynamicFormComponent implements OnChanges, OnInit {

 @Input() vm: any;
 @Input() vmDefinition: Array<FieldDefinition>;
 @Input() operation: string;
 @Input() errorMessage: string;
 @Output() update: EventEmitter<any> = new EventEmitter();
 @Output() create: EventEmitter<any> = new EventEmitter();

 form: FormGroup;
 status: string;
 submitted = false;
 vmCopy: any;

  constructor(private router: Router,
          private route: ActivatedRoute,
          private location: Location) { }

  clearForm() {
    const group = {};
    this.vmCopy = Object.assign({}, this.vm);
    this.vmDefinition.forEach(field => {
      group[field.key] = field.required ? new FormControl(this.vmCopy[field.key], Validators.required)
    : new FormControl(this.vmCopy[field.key]);
   });
   this.form = new FormGroup(group);
 }

 ngOnChanges(changes: SimpleChanges) {
     if (changes['errorMessage'].currentValue && this.status === 'waiting') {
      this.status = '';
     }
 }

 ngOnInit() {
    this.clearForm();

    this.route.params.subscribe(params => {
       this.operation = params['operation'];
       this.clearForm();
     });
 }

我试图在我的图书详细信息页面中使用此动态表单,但我在填充下拉列表时遇到问题

 export class BookDetailComponent implements OnInit {
  book: Book;
  books: IBook[];
  authors: IAuthor[];
  categories: ICategory[];
  errorMessage: string;

  bookDefinition: Array<FieldDefinition> = [
   {
      key: 'bookId',
      type: 'number',
      isId: true,
      label: 'BookId',
      required: true
   },
  {
      key: 'title',
      type: 'string',
      isId: false,
     label: 'Title',
     required: true
  },
  {
     key: 'authors',
     type: '[]',
     isId: false,
     label: 'Authors',
     required: false
  },
  {
     key: 'categories',
     type: '[]',
     isId: false,
     label: 'Categories',
     required: false
  }
];
operation: string;

constructor(private route: ActivatedRoute,
  private router: Router,
  private bookService: BookService,
  private authorService: AuthorService,
  private categoryService: CategoryService) { }

 ngOnInit(): void {

   this.operation = this.route.snapshot.params['operation'];
   this.authorService.getAuthors()
  .subscribe((authors: IAuthor[]) => { this.authors = authors });

   this.categoryService.getCategories()
  .subscribe((categories: ICategory[]) => { this.categories = categories });

  if (this.operation === 'create') {
    this.book = { bookId: 0, title: '', authors: this.authors, categories: 
   this.categories };
  } else {
   this.bookService.getBook(this.route.snapshot.params['id'])
    .subscribe((book: Book) => { this.book = book });
 }
}

在book-detail-component.html中我有这个

 <h3>Book</h3>
<fw-dynamic-form *ngIf='book' [vm]="book" [vmDefinition]="bookDefinition"
[operation]="operation"
[errorMessage]="errorMessage"
(update)="updateBook($event)"
(create)="createBook($event)">  

它与其他字段一起工作正常,但我对作者和类别的下拉列表有问题。首先,如何在一个订阅中进行getAuthors和getCategories调用?然后我如何将值附加到下拉列表中?我应该创建一个dropdownField并将其设置为字段类型?因为我现在没有现场选项属性.. 非常感谢!

更新:

我创造了这个:

import { FieldDefinition } from './field-definition';

export class DropdownField extends FieldDefinition {
 type: 'dropdown';
 options: { key: number, value: string }[] = [];

constructor(options: {} = {}) {
  super();
 this.options = options['options'] || [];
 }
 }

我尝试在book-detail-component中使用它来填充作者的下拉列表和类别的下拉列表

ngOnInit(): void {

this.operation = this.route.snapshot.params['operation'];

this.authorService.getAuthors()
  .subscribe((authors: IAuthor[]) => {
    this.authors = authors,
      authors.forEach(function (author) {
      this.authorDropdown.options = [{ key: this.author.authorId, value: this.author.authorName }]
      });
  });

this.categoryService.getCategories()
  .subscribe((categories: ICategory[]) => { this.categories = categories, console.log(this.categories) });

if (this.operation === 'create') {
  this.book = { bookId: 0, title: '', authors: this.authorDropdown, categories: this.categories };
} else {
  this.bookService.getBook(this.route.snapshot.params['id'])
    .subscribe((book: Book) => { this.book = book });   
}
}

仍然无法工作..

更新2

export class BookDetailComponent implements OnInit {
  book: Book;
  author: Author;
  books: IBook[];
  authors: IAuthor[];
  categories: ICategory[];
  errorMessage: string;

  bookDefinition: Array<FieldDefinition> = [
     {
       key: 'bookId',
       type: 'number',
       isId: true,
       label: 'BookId',
       required: true
     },
     {
       key: 'title',
       type: 'string',
       isId: false,
       label: 'Title',
       required: true
     },
     {
       key: 'authors',
       type: '[]',
       isId: false,
       label: 'Authors',
       required: false
     },
     {
       key: 'categories',
       type: '[]',
       isId: false,
       label: 'Categories',
       required: false
     }
    ];
  operation: string;
  authorDropdown: DropdownField;
  categoryDropdown: DropdownField;

 constructor(private route: ActivatedRoute,
     private router: Router,
     private bookService: BookService,
     private authorService: AuthorService,
     private categoryService: CategoryService) { }


 ngOnInit(): void {
    this.operation = this.route.snapshot.params['operation'];
    this.authorService.getAuthors()
      .subscribe((authors: IAuthor[]) => {
      this.authors = authors,
      authors.forEach(function (author) {
      this.authorDropdown.options.push({ key: this.author.authorId, value: this.author.authorName });
      });
      console.log(this.authorDropdown);
   });

if (this.operation === 'create') {
  this.book = { bookId: 0, title: '', authors: this.authorDropdown, categories: [] };
} else {
  this.bookService.getBook(this.route.snapshot.params['id'])
    .subscribe((book: Book) => { this.book = book });;
}
}

1 个答案:

答案 0 :(得分:0)

这是正确的方法,您的代码只是在迭代作者数组时添加最后一个元素,现在您将使用{{1}在this.authorDropdown.options附加作者的每个元素}}

push