Angular ReactiveForms: การสร้างอาร์เรย์ของค่าช่องทำเครื่องหมาย?


109

ด้วยรายการของช่องทำเครื่องหมายที่ผูกไว้กับช่องเดียวกันformControlNameฉันจะสร้างอาร์เรย์ของค่าช่องทำเครื่องหมายที่ผูกไว้กับformControlแทนที่จะเป็นเพียงtrue/ false?

ตัวอย่าง:

<form [formGroup]="checkboxGroup">
    <input type="checkbox" id="checkbox-1" value="value-1" formControlName="myValues" />
    <input type="checkbox" id="checkbox-2" value="value-2" formControlName="myValues" />
    <input type="checkbox" id="checkbox-3" value="value-2" formControlName="myValues" />
</form>

checkboxGroup.controls['myValues'].value ปัจจุบันผลิต:

true or false

สิ่งที่ฉันต้องการให้ผลิต:

['value-1', 'value-2', ...]

คุณพบวิธีแก้ปัญหาหรือไม่?
CP

นี่อาจเป็นวิธีที่ออกแบบมากเกินไปในการทำช่องทำเครื่องหมายในแบบฟอร์ม นี่ไม่ตรงไปตรงมาเลย
mwilson

8
เชิงมุม. สิ่งที่ฉันพยายามทำคือรับ mat-radio-group เพื่อผูกในรูปแบบปฏิกิริยาของฉัน ฉันจำไม่ได้ว่าต้องดิ้นรนกับเชิงมุมมากขนาดนี้ บทความทั้งหมดชี้ไปที่สิ่งเดียวกัน แค่ไม่สามารถใช้งานได้ อย่างอื่นตรงไปตรงมาสุด ๆ ฉันคงมองมานานเกินไป ยังคงรู้สึกเหมือนมีความซับซ้อนมากเกินไปสำหรับค่าอาร์เรย์ในรูปแบบ
mwilson

3
ใช่มันก็น่ากลัวเมื่อฉันถามนี้ในปี 2016 และจะยังคงน่ากลัวใน 2019
ReactingToAngularVues

3
ฉันไม่ได้เพิ่มจำนวนให้กับคำถามนี้ แต่ฉันอยากให้คนอื่นรู้ว่าฉันรู้สึกแบบเดียวกัน สิ่งนี้เป็นส่วนที่ยากที่สุดในการเรียนรู้รูปแบบปฏิกิริยาเชิงมุม ฉันรู้สึกว่ามันไม่น่าจะยากขนาดนี้เลย ฉันมีความสุขที่เห็นว่าฉันไม่ได้อยู่คนเดียวในการต่อสู้แม้ว่า ขอบคุณสำหรับการโพสต์คำถาม
NorthStarCode

คำตอบ:


55

ด้วยความช่วยเหลือของคำตอบ silentsod ฉันเขียนวิธีแก้ปัญหาเพื่อรับค่าแทนสถานะใน formBuilder ของฉัน

ฉันใช้วิธีการเพิ่มหรือลบค่าใน formArray อาจเป็นแนวทางที่ไม่ดี แต่ได้ผล!

component.html

<div *ngFor="let choice of checks; let i=index" class="col-md-2">
  <label>
    <input type="checkbox" [value]="choice.value" (change)="onCheckChange($event)">
    {{choice.description}}
  </label>
</div>

component.ts

// For example, an array of choices
public checks: Array<ChoiceClass> = [
  {description: 'descr1', value: 'value1'},
  {description: "descr2", value: 'value2'},
  {description: "descr3", value: 'value3'}
];

initModelForm(): FormGroup{
  return this._fb.group({
    otherControls: [''],
    // The formArray, empty 
    myChoices: new FormArray([]),
  }
}

onCheckChange(event) {
  const formArray: FormArray = this.myForm.get('myChoices') as FormArray;

  /* Selected */
  if(event.target.checked){
    // Add a new control in the arrayForm
    formArray.push(new FormControl(event.target.value));
  }
  /* unselected */
  else{
    // find the unselected element
    let i: number = 0;

    formArray.controls.forEach((ctrl: FormControl) => {
      if(ctrl.value == event.target.value) {
        // Remove the unselected element from the arrayForm
        formArray.removeAt(i);
        return;
      }

      i++;
    });
  }
}

เมื่อฉันส่งแบบฟอร์มของฉันตัวอย่างเช่นโมเดลของฉันมีลักษณะดังนี้:

  otherControls : "foo",
  myChoices : ['value1', 'value2']

มีเพียงสิ่งเดียวที่ขาดหายไปคือฟังก์ชันสำหรับกรอก formArray หากโมเดลของคุณได้ตรวจสอบค่าแล้ว


ฉันจะตรวจสอบได้อย่างไรว่าช่องทำเครื่องหมายของฉันถูกตรวจสอบเมื่อฉันโหลดข้อมูลหลังจากใช้ตัวอย่างของคุณเพื่อเข้าสู่ db
Devora

ในโซลูชันนี้แบบฟอร์มจะถูกต้องเสมอแม้ว่าจะไม่ได้เลือกช่องทำเครื่องหมาย
Teja

myChoices: new FormArray([], Validators.required)
Bikram Nath

"แต่มันได้ผล!" หนี้เทคโนโลยีเริ่มต้นอย่างไร นี่ไม่ใช่รูปแบบปฏิกิริยาในการทำ ประโยชน์อย่างหนึ่งของการใช้ formcontrols ในทุกช่องทำเครื่องหมายคือพวกเขาสามารถจำสถานะของตนได้แม้ว่าคุณจะเพิ่มเข้าไปใหม่ใน DOM ก็ตาม
MIWMIB

51

นี่เป็นสถานที่ที่ดีในการใช้FormArray https://angular.io/docs/ts/latest/api/forms/index/FormArray-class.html

ในการเริ่มต้นเราจะสร้างอาร์เรย์ของการควบคุมของเราด้วยFormBuilderaFormArray

FormBuilder

this.checkboxGroup = _fb.group({
  myValues: _fb.array([true, false, true])
});

FormArray ใหม่

let checkboxArray = new FormArray([
  new FormControl(true),
  new FormControl(false),
  new FormControl(true)]);

this.checkboxGroup = _fb.group({
  myValues: checkboxArray
});

ง่ายพอที่จะทำ แต่เราจะเปลี่ยนเทมเพลตของเราและปล่อยให้เครื่องมือสร้างเทมเพลตจัดการวิธีที่เราผูกมัดกับการควบคุมของเรา:

template.html

<form [formGroup]="checkboxGroup">
    <input *ngFor="let control of checkboxGroup.controls['myValues'].controls"
    type="checkbox" id="checkbox-1" value="value-1" [formControl]="control" />     
  </form>

ที่นี่เรากำลังทำซ้ำชุดของFormControlsเราในของเราmyValues FormArrayและสำหรับแต่ละการควบคุมเราจะผูกมัด[formControl]กับการควบคุมนั้นแทนที่จะเป็นการFormArrayควบคุมและ<div>{{checkboxGroup.controls['myValues'].value}}</div>สร้างtrue,false,trueในขณะเดียวกันก็ทำให้ไวยากรณ์ของเทมเพลตของคุณใช้งานน้อยลงเล็กน้อย

คุณสามารถใช้ตัวอย่างนี้: http://plnkr.co/edit/a9OdMAq2YIwQFo7gixbj?p=previewเพื่อโผล่รอบ ๆ


1
คุณควรเอา id = "xxx" ออกไป id ควรจะไม่ซ้ำกันใช่ไหม
PeiSong Xiong

1
สำหรับ id สามารถใช้ดัชนี *ngFor="let control of checkboxGroup.controls['myValues'].controls ; let i=index""
Mirza

9
นี่เป็นเรื่องที่ยอดเยี่ยม แต่สร้างช่องทำเครื่องหมายอาร์เรย์ทั่วไปโดยสิ้นเชิง สันนิษฐานว่าคุณกำลังโหลดในอาร์เรย์หรืออย่างอื่นและเชื่อมโยงช่องทำเครื่องหมายแต่ละช่องกับค่าอื่น ๆ ตัวอย่างเช่นคุณจะเพิ่มสตริงข้อความเพื่อใช้ในป้ายกำกับฟอร์มให้กับตัวควบคุมแต่ละฟอร์มได้อย่างไร
Askdesigners

NM ฉันเพิ่งแมปมันถัดจากอาร์เรย์ภายนอก: p
Askdesigners

@ นักออกแบบคุณสามารถโพสต์โซลูชันของคุณเพื่อให้มีช่องทำเครื่องหมายและป้ายกำกับได้หรือไม่?
eddygeek

27

การทำสิ่งนี้ใน Angular 6 ทำได้ง่ายกว่าในเวอร์ชันก่อนหน้าอย่างมากแม้ว่าข้อมูลในช่องทำเครื่องหมายจะถูกเติมข้อมูลแบบอะซิงโครนัสจาก API ก็ตาม

สิ่งแรกที่ต้องตระหนักคือต้องขอบคุณkeyvalueท่อAngular 6 ที่เราไม่จำเป็นต้องใช้FormArrayอีกต่อไปและสามารถทำรัง a FormGroup.

ขั้นแรกให้ส่ง FormBuilder ไปยังตัวสร้าง

constructor(
    private _formBuilder: FormBuilder,
) { }

จากนั้นเริ่มต้นแบบฟอร์มของเรา

ngOnInit() {

    this.form = this._formBuilder.group({
        'checkboxes': this._formBuilder.group({}),
    });

}

เมื่อข้อมูลตัวเลือกช่องทำเครื่องหมายของเราพร้อมใช้งานให้ทำซ้ำและเราสามารถผลักดันข้อมูลนั้นไปยังที่ซ้อนกันFormGroupเป็นชื่อได้FormControlโดยตรงโดยไม่ต้องพึ่งพาอาร์เรย์การค้นหาที่จัดทำดัชนีด้วยตัวเลข

const checkboxes = <FormGroup>this.form.get('checkboxes');
options.forEach((option: any) => {
    checkboxes.addControl(option.title, new FormControl(true));
});

สุดท้ายในเทมเพลตเราเพียงแค่ต้องวนซ้ำkeyvalueช่องทำเครื่องหมาย: ไม่มีเพิ่มเติมlet index = iและช่องทำเครื่องหมายจะเรียงตามตัวอักษรโดยอัตโนมัติ: สะอาดกว่า

<form [formGroup]="form">

    <h3>Options</h3>

    <div formGroupName="checkboxes">

        <ul>
            <li *ngFor="let item of form.get('checkboxes').value | keyvalue">
                <label>
                    <input type="checkbox" [formControlName]="item.key" [value]="item.value" /> {{ item.key }}
                </label>
            </li>
        </ul>

    </div>

</form>

1
ใช้งานได้ดีมากเช่นกันในกรณีของค่ากล่องกาเครื่องหมายอาร์เรย์ที่เข้ารหัสอย่างง่าย จากนั้นคุณสามารถเพิ่มตัวควบคุมแบบฟอร์มโดยใช้ลูปที่คล้ายกันได้ทันทีใน ngOnInit () และช่องทำเครื่องหมายในแบบฟอร์มของคุณจะแสดงอาร์เรย์ค่าช่องทำเครื่องหมายแบบไดนามิก
Arjan

3
สิ่งนี้ยังคงตัดตอน [key1 = true, key2 = false, key3 = true] เราต้องการ ['key1', 'key3']
f.khantsis

@ f.khantsis คุณสามารถทำได้ดังนี้: `const value = {key1: true, key2: false, key3: true}; const list = Object.entries (value) .filter (([_, isSelected]) => isSelected) .map (([คีย์]) => คีย์); console.log (รายการ); `
zauni

2
ทางออกที่ดีที่สุด imho คุณสามารถมอบหมายงานconst checkboxes = ..นอกส่วนหน้าได้;)
Bernoulli IT

จะเกิดอะไรขึ้นเมื่อคีย์รายการเท่ากับสิ่งเดียวกับฟิลด์อื่นในแบบฟอร์ม ตัวอย่างเช่นฉันมีอาร์เรย์ช่องทำเครื่องหมายสองอันที่แตกต่างกันแต่ละอันมีคีย์ 'Small', 'Medium' และ 'Large'?
Newclique

9

หากคุณกำลังมองหาค่าช่องทำเครื่องหมายในรูปแบบ JSON

{ "name": "", "countries": [ { "US": true }, { "Germany": true }, { "France": true } ] }

ตัวอย่างเต็มรูปแบบที่นี่

ฉันขออภัยที่ใช้ชื่อประเทศเป็นค่าในช่องทำเครื่องหมายแทนค่าในคำถาม คำอธิบายเพิ่มเติม -

สร้าง FormGroup สำหรับแบบฟอร์ม

 createForm() {

    //Form Group for a Hero Form
    this.heroForm = this.fb.group({
      name: '',
      countries: this.fb.array([])
    });

    let countries=['US','Germany','France'];

    this.setCountries(countries);}
 }

ให้แต่ละช่องทำเครื่องหมายเป็น FormGroup ที่สร้างขึ้นจากวัตถุที่มีคุณสมบัติเดียวคือค่าของช่องทำเครื่องหมาย

 setCountries(countries:string[]) {

    //One Form Group for one country
    const countriesFGs = countries.map(country =>{
            let obj={};obj[country]=true;
            return this.fb.group(obj)
    });

    const countryFormArray = this.fb.array(countriesFGs);
    this.heroForm.setControl('countries', countryFormArray);
  }

อาร์เรย์ของ FormGroups สำหรับช่องทำเครื่องหมายถูกใช้เพื่อตั้งค่าการควบคุมสำหรับ 'ประเทศ' ในแบบฟอร์มหลัก

  get countries(): FormArray {
      return this.heroForm.get('countries') as FormArray;
  };

ในเทมเพลตใช้ไปป์เพื่อรับชื่อสำหรับตัวควบคุมช่องทำเครื่องหมาย

  <div formArrayName="countries" class="well well-lg">
      <div *ngFor="let country of countries.controls; let i=index" [formGroupName]="i" >
          <div *ngFor="let key of country.controls | mapToKeys" >
              <input type="checkbox" formControlName="{{key.key}}">{{key.key}}
          </div>
      </div>
  </div>

7

ฉันไม่เห็นวิธีแก้ปัญหาที่ตอบคำถามโดยใช้รูปแบบปฏิกิริยาได้อย่างสมบูรณ์ดังนั้นนี่คือคำตอบของฉันสำหรับสิ่งเดียวกัน


สรุป

นี่คือส่วนสำคัญของคำอธิบายโดยละเอียดพร้อมกับตัวอย่าง StackBlitz

  1. ใช้FormArrayสำหรับช่องทำเครื่องหมายและเริ่มต้นฟอร์ม
  2. ค่าที่valueChangesสังเกตได้นี้เหมาะสำหรับเมื่อคุณต้องการให้ฟอร์มแสดงบางสิ่ง แต่เก็บอย่างอื่นไว้ในส่วนประกอบ แมปค่าtrue/ falseกับค่าที่ต้องการที่นี่
  3. กรองfalseค่าในเวลาที่ส่ง
  4. Unsubscribe from valueChangesสังเกตได้.

ตัวอย่าง StackBlitz


คำอธิบายโดยละเอียด

ใช้ FormArray เพื่อกำหนดแบบฟอร์ม

ตามที่กล่าวไว้แล้วในคำตอบที่ทำเครื่องหมายว่าถูกต้อง FormArrayเป็นวิธีที่จะไปในกรณีเช่นนี้ซึ่งคุณต้องการรับข้อมูลในอาร์เรย์ สิ่งแรกที่คุณต้องทำคือสร้างแบบฟอร์ม

checkboxGroup: FormGroup;
checkboxes = [{
    name: 'Value 1',
    value: 'value-1'
}, {
    name: 'Value 2',
    value: 'value-2'
}];

this.checkboxGroup = this.fb.group({
    checkboxes: this.fb.array(this.checkboxes.map(x => false))
});

falseเพียงแค่นี้ก็จะตั้งค่าเริ่มต้นของช่องทำทั้งหมดเพื่อ

ต่อไปเราต้องลงทะเบียนตัวแปรฟอร์มเหล่านี้ในเทมเพลตและทำซ้ำบนcheckboxesอาร์เรย์ (ไม่ใช่ข้อมูลFormArrayแต่เป็นข้อมูลช่องทำเครื่องหมาย) เพื่อแสดงในเทมเพลต

<form [formGroup]="checkboxGroup">
    <ng-container *ngFor="let checkbox of checkboxes; let i = index" formArrayName="checkboxes">
        <input type="checkbox" [formControlName]="i" />{{checkbox.name}}
    </ng-container>
</form>

ใช้ประโยชน์จากมูลค่าเปลี่ยนแปลงที่สังเกตได้

นี่คือส่วนที่ฉันไม่เห็นกล่าวถึงในคำตอบใด ๆ ที่ให้ไว้ที่นี่ ในสถานการณ์เช่นนี้ที่เราต้องการแสดงข้อมูลดังกล่าว แต่เก็บไว้เป็นอย่างอื่นสิ่งที่valueChangesสังเกตได้จะมีประโยชน์มาก ใช้valueChangesเราสามารถสังเกตการเปลี่ยนแปลงในcheckboxesแล้ว/ ค่าที่ได้รับจากข้อมูลที่ต้องการ โปรดทราบว่าสิ่งนี้จะไม่เปลี่ยนการเลือกช่องทำเครื่องหมายเนื่องจากค่าที่แท้จริงใด ๆ ที่ส่งไปยังช่องทำเครื่องหมายจะทำเครื่องหมายว่าเลือกไว้และในทางกลับกันmaptruefalseFormArray

subscription: Subscription;

const checkboxControl = (this.checkboxGroup.controls.checkboxes as FormArray);
this.subscription = checkboxControl.valueChanges.subscribe(checkbox => {
    checkboxControl.setValue(
        checkboxControl.value.map((value, i) => value ? this.checkboxes[i].value : false),
        { emitEvent: false }
    );
});

นี้โดยทั่วไปแผนที่FormArrayค่าไปที่เดิมcheckboxesอาร์เรย์และส่งกลับvalueในกรณีที่ช่องทำเครื่องหมายเป็นอื่นก็จะส่งกลับtrue falseสิ่งemitEvent: falseนี้มีความสำคัญเนื่องจากการตั้งFormArrayค่าโดยที่ไม่ทำให้เกิด valueChangesเหตุการณ์ที่สร้างการวนซ้ำที่ไม่สิ้นสุด ด้วยการตั้งค่าemitEventเป็นfalseเราตรวจสอบให้แน่ใจว่าสิ่งที่valueChangesสังเกตได้ไม่เปล่งออกมาเมื่อเราตั้งค่าที่นี่

กรองค่าเท็จออก

เราไม่สามารถกรองfalseค่าได้โดยตรงFormArrayเนื่องจากการทำเช่นนี้จะทำให้เทมเพลตยุ่งเหยิงเนื่องจากถูกผูกไว้กับช่องทำเครื่องหมาย ดังนั้นวิธีแก้ปัญหาที่ดีที่สุดคือกรองfalseค่าระหว่างการส่ง ใช้ตัวดำเนินการกระจายเพื่อดำเนินการนี้

submit() {
    const checkboxControl = (this.checkboxGroup.controls.checkboxes as FormArray);
    const formValue = {
        ...this.checkboxGroup.value,
        checkboxes: checkboxControl.value.filter(value => !!value)
    }
    // Submit formValue here instead of this.checkboxGroup.value as it contains the filtered data
}

โดยพื้นฐานแล้วสิ่งนี้จะกรองค่าที่เป็นเท็จออกจากไฟล์checkboxes.

ยกเลิกการสมัครสมาชิกจาก valueChanges

สุดท้ายนี้อย่าลืมยกเลิกการสมัคร valueChanges

ngOnDestroy() {
    this.subscription.unsubscribe();
}

หมายเหตุ:มีกรณีพิเศษที่มีมูลค่าไม่สามารถกำหนดให้เป็นFormArrayในเช่นถ้าค่าช่องมีการตั้งค่าไปยังหมายเลขที่valueChanges 0สิ่งนี้จะทำให้ดูเหมือนว่าไม่สามารถเลือกช่องทำเครื่องหมายได้เนื่องจากการเลือกช่องทำเครื่องหมายจะตั้งค่าFormControlเป็นตัวเลข0(ค่าที่เป็นเท็จ) และด้วยเหตุนี้จึงไม่ต้องเลือก มันจะเป็นที่ต้องการที่จะไม่ใช้จำนวน0เป็นค่า แต่ถ้ามันเป็นสิ่งจำเป็นที่คุณจะต้องตั้งเงื่อนไข0กับค่า truthy บางพูดสตริง'0'หรือเพียงแค่ธรรมดาและจากนั้นในการส่งแปลงกลับไปยังหมายเลขที่true0

ตัวอย่าง StackBlitz

นอกจากนี้ StackBlitz ยังมีรหัสสำหรับเมื่อคุณต้องการส่งผ่านค่าเริ่มต้นไปยังช่องทำเครื่องหมายเพื่อให้ถูกทำเครื่องหมายว่าเลือกใน UI


สิ่งนี้ยังคงต้องมีการบำรุงรักษาอาร์เรย์สองอาร์เรย์และทำให้ซิงค์กัน ยังไม่สะอาดเท่าที่ฉันหวังไว้ บางทีเราอาจได้รับการควบคุมแบบฟอร์มเพื่อเก็บค่าที่ซับซ้อนแทนที่จะมีสองอาร์เรย์
MIWMIB

1
ค่าที่ซับซ้อนใช้ไม่ได้เนื่องจากค่าจะต้องเป็นจริงหรือเท็จสำหรับช่องทำเครื่องหมาย ดังนั้นโซลูชันนี้ยังคงดูดีที่สุด
MIWMIB

6

TL; ดร

  1. ฉันชอบใช้ FormGroup เพื่อเติมรายการช่องทำเครื่องหมาย
  2. เขียนตัวตรวจสอบความถูกต้องที่กำหนดเองสำหรับการตรวจสอบอย่างน้อยหนึ่งช่องทำเครื่องหมายถูกเลือก
  3. ตัวอย่างการทำงานhttps://stackblitz.com/edit/angular-validate-at-least-one-checkbox-was-selected

บางครั้งสิ่งนี้ก็ทำให้ฉันรู้สึกเช่นกันดังนั้นฉันจึงลองใช้ทั้ง FormArray และ FormGroup

โดยส่วนใหญ่แล้วรายการช่องทำเครื่องหมายจะปรากฏบนเซิร์ฟเวอร์และฉันได้รับผ่าน API แต่บางครั้งคุณจะมีชุดช่องทำเครื่องหมายคงที่พร้อมค่าที่กำหนดไว้ล่วงหน้า ในแต่ละกรณีการใช้งานจะใช้ FormArray หรือ FormGroup ที่เกี่ยวข้อง

โดยทั่วไปFormArrayเป็นตัวแปรของFormGroup. ความแตกต่างที่สำคัญคือข้อมูลจะถูกทำให้เป็นอนุกรมเป็นอาร์เรย์ (ตรงข้ามกับการทำให้เป็นอนุกรมเป็นวัตถุในกรณีของ FormGroup) สิ่งนี้อาจมีประโยชน์อย่างยิ่งเมื่อคุณไม่รู้ว่าจะมีตัวควบคุมจำนวนเท่าใดในกลุ่มเช่นฟอร์มไดนามิก

เพื่อความเรียบง่ายลองจินตนาการว่าคุณสร้างรูปแบบผลิตภัณฑ์ง่ายๆด้วย

  • กล่องข้อความชื่อผลิตภัณฑ์ที่ต้องการหนึ่งกล่อง
  • รายการหมวดหมู่ที่จะเลือกต้องเลือกอย่างน้อยหนึ่งรายการ สมมติว่ารายการจะถูกดึงมาจากเซิร์ฟเวอร์

ก่อนอื่นฉันตั้งค่าแบบฟอร์มด้วยชื่อผลิตภัณฑ์ formControl เท่านั้น เป็นฟิลด์บังคับ

this.form = this.formBuilder.group({
    name: ["", Validators.required]
});

เนื่องจากหมวดหมู่นี้แสดงผลแบบไดนามิกฉันจะต้องเพิ่มข้อมูลเหล่านี้ลงในแบบฟอร์มในภายหลังหลังจากที่ข้อมูลพร้อม

this.getCategories().subscribe(categories => {
    this.form.addControl("categoriesFormArr", this.buildCategoryFormArr(categories));
    this.form.addControl("categoriesFormGroup", this.buildCategoryFormGroup(categories));
})

มีสองวิธีในการสร้างรายการหมวดหมู่

1. ฟอร์มอาร์เรย์

  buildCategoryFormArr(categories: ProductCategory[], selectedCategoryIds: string[] = []): FormArray {
    const controlArr = categories.map(category => {
      let isSelected = selectedCategoryIds.some(id => id === category.id);
      return this.formBuilder.control(isSelected);
    })
    return this.formBuilder.array(controlArr, atLeastOneCheckboxCheckedValidator())
  }
<div *ngFor="let control of categoriesFormArr?.controls; let i = index" class="checkbox">
  <label><input type="checkbox" [formControl]="control" />
    {{ categories[i]?.title }}
  </label>
</div>

สิ่งนี้buildCategoryFormGroupจะคืน FormArray ให้ฉัน นอกจากนี้ยังใช้รายการค่าที่เลือกเป็นอาร์กิวเมนต์ดังนั้นหากคุณต้องการใช้แบบฟอร์มนี้ซ้ำเพื่อแก้ไขข้อมูลอาจเป็นประโยชน์ สำหรับวัตถุประสงค์ในการสร้างรูปแบบผลิตภัณฑ์ใหม่ยังไม่สามารถใช้งานได้

สังเกตว่าเมื่อคุณพยายามเข้าถึงค่า formArray จะมีลักษณะ[false, true, true]ดังนี้ ในการรับรายการ id ที่เลือกต้องใช้งานอีกเล็กน้อยในการตรวจสอบจากรายการ แต่ขึ้นอยู่กับดัชนีอาร์เรย์ ฟังดูไม่ดีสำหรับฉัน แต่มันใช้งานได้

get categoriesFormArraySelectedIds(): string[] {
  return this.categories
  .filter((cat, catIdx) => this.categoriesFormArr.controls.some((control, controlIdx) => catIdx === controlIdx && control.value))
  .map(cat => cat.id);
}

นั่นเป็นเหตุผลที่ฉันใช้FormGroupสำหรับเรื่องนั้น

2. กลุ่มฟอร์ม

formGroup ที่แตกต่างกันคือจะเก็บข้อมูลฟอร์มเป็นวัตถุซึ่งต้องใช้คีย์และตัวควบคุมฟอร์ม ดังนั้นจึงเป็นความคิดที่ดีที่จะตั้งค่าคีย์เป็น categoryId แล้วเราสามารถเรียกดูได้ในภายหลัง

buildCategoryFormGroup(categories: ProductCategory[], selectedCategoryIds: string[] = []): FormGroup {
  let group = this.formBuilder.group({}, {
    validators: atLeastOneCheckboxCheckedValidator()
  });
  categories.forEach(category => {
    let isSelected = selectedCategoryIds.some(id => id === category.id);
    group.addControl(category.id, this.formBuilder.control(isSelected));
  })
  return group;
}
<div *ngFor="let item of categories; let i = index" class="checkbox">
  <label><input type="checkbox" [formControl]="categoriesFormGroup?.controls[item.id]" /> {{ categories[i]?.title }}
  </label>
</div>

ค่าของกลุ่มฟอร์มจะมีลักษณะดังนี้:

{
    "category1": false,
    "category2": true,
    "category3": true,
}

แต่ส่วนใหญ่เรามักจะต้องการที่จะได้รับเพียงรายการ categoryIds ["category2", "category3"]เป็น ฉันต้องเขียนวิธีรับข้อมูลเหล่านี้ด้วย ฉันชอบวิธีนี้มากกว่าเมื่อเปรียบเทียบกับ formArray เพราะฉันสามารถรับค่าจากแบบฟอร์มได้

  get categoriesFormGroupSelectedIds(): string[] {
    let ids: string[] = [];
    for (var key in this.categoriesFormGroup.controls) {
      if (this.categoriesFormGroup.controls[key].value) {
        ids.push(key);
      }
      else {
        ids = ids.filter(id => id !== key);
      }
    }
    return ids;
  }

3. กำหนดตัวตรวจสอบความถูกต้องเพื่อตรวจสอบอย่างน้อยหนึ่งช่องทำเครื่องหมายถูกเลือก

ฉันทำให้ตัวตรวจสอบความถูกต้องเพื่อตรวจสอบอย่างน้อยช่องทำเครื่องหมาย X ถูกเลือกโดยค่าเริ่มต้นจะตรวจสอบกับช่องทำเครื่องหมายเดียวเท่านั้น

export function atLeastOneCheckboxCheckedValidator(minRequired = 1): ValidatorFn {
  return function validate(formGroup: FormGroup) {
    let checked = 0;

    Object.keys(formGroup.controls).forEach(key => {
      const control = formGroup.controls[key];

      if (control.value === true) {
        checked++;
      }
    });

    if (checked < minRequired) {
      return {
        requireCheckboxToBeChecked: true,
      };
    }

    return null;
  };
}

4

สร้างเหตุการณ์เมื่อมีการคลิกจากนั้นเปลี่ยนค่า true เป็นชื่อของสิ่งที่กล่องกาเครื่องหมายแสดงถึงด้วยตนเองจากนั้นชื่อหรือ true จะประเมินค่าเดียวกันและคุณจะได้รับค่าทั้งหมดแทนที่จะเป็นรายการจริง / เท็จ เช่น:

component.html

<form [formGroup]="customForm" (ngSubmit)="onSubmit()">
    <div class="form-group" *ngFor="let parameter of parameters"> <!--I iterate here to list all my checkboxes -->
        <label class="control-label" for="{{parameter.Title}}"> {{parameter.Title}} </label>
            <div class="checkbox">
              <input
                  type="checkbox"
                  id="{{parameter.Title}}"
                  formControlName="{{parameter.Title}}"
                  (change)="onCheckboxChange($event)"
                  > <!-- ^^THIS^^ is the important part -->
             </div>
      </div>
 </form>

component.ts

onCheckboxChange(event) {
    //We want to get back what the name of the checkbox represents, so I'm intercepting the event and
    //manually changing the value from true to the name of what is being checked.

    //check if the value is true first, if it is then change it to the name of the value
    //this way when it's set to false it will skip over this and make it false, thus unchecking
    //the box
    if(this.customForm.get(event.target.id).value) {
        this.customForm.patchValue({[event.target.id] : event.target.id}); //make sure to have the square brackets
    }
}

สิ่งนี้จะจับเหตุการณ์หลังจากที่มันถูกเปลี่ยนเป็นจริงหรือเท็จโดย Angular Forms ถ้าเป็นจริงฉันเปลี่ยนชื่อเป็นชื่อของสิ่งที่ช่องทำเครื่องหมายแสดงซึ่งหากจำเป็นจะประเมินเป็นจริงด้วยหากมีการตรวจสอบว่าเป็นจริง / เท็จเป็น ดี.


สิ่งนี้ทำให้ฉันมาถูกทางแล้วฉันลงเอยด้วยการทำ this.customForm.patchValue ({[event.target.id]: event.target.checked});
Demodave

4

หากคุณต้องการใช้รูปแบบปฏิกิริยาเชิงมุม ( https://angular.io/guide/reactive-forms )

คุณสามารถใช้การควบคุมฟอร์มเดียวเพื่อจัดการค่าที่ส่งออกของกลุ่มช่องทำเครื่องหมาย

ส่วนประกอบ

import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { flow } from 'lodash';
import { flatMap, filter } from 'lodash/fp';

@Component({
  selector: 'multi-checkbox',
  templateUrl: './multi-checkbox.layout.html',
})
export class MultiChecboxComponent  {

  checklistState = [ 
      {
        label: 'Frodo Baggins',
        value: 'frodo_baggins',
        checked: false
      },
      {
        label: 'Samwise Gamgee',
        value: 'samwise_gamgee',
        checked: true,
      },
      {
        label: 'Merry Brandybuck',
        value: 'merry_brandybuck',
        checked: false
      }
    ];

  form = new FormGroup({
    checklist : new FormControl(this.flattenValues(this.checklistState)),
  });


  checklist = this.form.get('checklist');

  onChecklistChange(checked, checkbox) {
    checkbox.checked = checked;
    this.checklist.setValue(this.flattenValues(this.checklistState));
  }

  flattenValues(checkboxes) {
    const flattenedValues = flow([
      filter(checkbox => checkbox.checked),
      flatMap(checkbox => checkbox.value )
    ])(checkboxes)
    return flattenedValues.join(',');
  }
}

html

<form [formGroup]="form">
    <label *ngFor="let checkbox of checklistState" class="checkbox-control">
    <input type="checkbox" (change)="onChecklistChange($event.target.checked, checkbox)" [checked]="checkbox.checked" [value]="checkbox.value" /> {{ checkbox.label }}
  </label>
</form>

checklistState

จัดการโมเดล / สถานะของอินพุตรายการตรวจสอบ โมเดลนี้ช่วยให้คุณสามารถแมปสถานะปัจจุบันกับรูปแบบค่าใดก็ได้ที่คุณต้องการ

รุ่น:

{
   label: 'Value 1',
   value: 'value_1',
   checked: false
},
{
  label: 'Samwise Gamgee',
  value: 'samwise_gamgee',
  checked: true,
},
{
  label: 'Merry Brandybuck',
  value: 'merry_brandybuck',
  checked: false
}

checklist การควบคุมแบบฟอร์ม

การควบคุมนี้เก็บค่าที่ต้องการบันทึกเป็นเช่น

เอาท์พุทมูลค่า: "value_1,value_2"

ดูการสาธิตได้ที่https://stackblitz.com/edit/angular-multi-checklist


เป็นทางออกที่ดีที่สุดสำหรับฉันอย่างง่ายดาย ขอบคุณมาก.
Newclique

2

วิธีแก้ปัญหาของฉัน - แก้ไขสำหรับ Angular 5 ด้วย Material View
การเชื่อมต่อผ่านไฟล์

formArrayName = "การแจ้งเตือน"

(เปลี่ยน) = "updateChkbxArray (n.id, $ event.checked, 'notification')"

วิธีนี้สามารถใช้ได้กับอาร์เรย์ช่องทำเครื่องหมายหลายช่องในรูปแบบเดียว เพียงตั้งชื่อของอาร์เรย์ควบคุมที่จะเชื่อมต่อในแต่ละครั้ง

constructor(
  private fb: FormBuilder,
  private http: Http,
  private codeTableService: CodeTablesService) {

  this.codeTableService.getnotifications().subscribe(response => {
      this.notifications = response;
    })
    ...
}


createForm() {
  this.form = this.fb.group({
    notification: this.fb.array([])...
  });
}

ngOnInit() {
  this.createForm();
}

updateChkbxArray(id, isChecked, key) {
  const chkArray = < FormArray > this.form.get(key);
  if (isChecked) {
    chkArray.push(new FormControl(id));
  } else {
    let idx = chkArray.controls.findIndex(x => x.value == id);
    chkArray.removeAt(idx);
  }
}
<div class="col-md-12">
  <section class="checkbox-section text-center" *ngIf="notifications  && notifications.length > 0">
    <label class="example-margin">Notifications to send:</label>
    <p *ngFor="let n of notifications; let i = index" formArrayName="notification">
      <mat-checkbox class="checkbox-margin" (change)="updateChkbxArray(n.id, $event.checked, 'notification')" value="n.id">{{n.description}}</mat-checkbox>
    </p>
  </section>
</div>

ในตอนท้ายคุณจะได้รับการบันทึกแบบฟอร์มด้วยอาร์เรย์ของรหัสระเบียนดั้งเดิมเพื่อบันทึก / อัปเดต มุมมอง UI

ส่วนที่เกี่ยวข้องกับ json ของแบบฟอร์ม

ยินดีที่จะมีข้อสังเกตเพื่อการปรับปรุง


0

ส่วนเทมเพลต: -

    <div class="form-group">
         <label for="options">Options:</label>
         <div *ngFor="let option of options">
            <label>
                <input type="checkbox"
                   name="options"
                   value="{{option.value}}"
                   [(ngModel)]="option.checked"
                                />
                  {{option.name}}
                  </label>
              </div>
              <br/>
         <button (click)="getselectedOptions()"  >Get Selected Items</button>
     </div>

ส่วนควบคุม: -

        export class Angular2NgFor {

          constructor() {
             this.options = [
              {name:'OptionA', value:'first_opt', checked:true},
              {name:'OptionB', value:'second_opt', checked:false},
              {name:'OptionC', value:'third_opt', checked:true}
             ];


             this.getselectedOptions = function() {
               alert(this.options
                  .filter(opt => opt.checked)
                  .map(opt => opt.value));
                }
             }

        }

1
สวัสดี @EchoLogic .. โปรดแจ้งให้เราทราบในกรณีที่มีข้อสงสัย
Abhishek Srivastava

1
นี่ไม่ได้ใช้ ReactiveForms แต่เป็นรูปแบบปกติจึงไม่ตอบคำถาม
Guillaume

0

เพิ่ม 5 เซ็นต์ของฉัน) แบบจำลองคำถามของฉัน

{
   name: "what_is_it",
   options:[
     {
      label: 'Option name',
      value: '1'
     },
     {
      label: 'Option name 2',
      value: '2'
     }
   ]
}

template.html

<div class="question"  formGroupName="{{ question.name }}">
<div *ngFor="let opt of question.options; index as i" class="question__answer" >
  <input 
    type="checkbox" id="{{question.name}}_{{i}}"
    [name]="question.name" class="hidden question__input" 
    [value]="opt.value" 
    [formControlName]="opt.label"
   >
  <label for="{{question.name}}_{{i}}" class="question__label question__label_checkbox">
      {{opt.label}}
  </label>
</div>

component.ts

 onSubmit() {
    let formModel = {};
    for (let key in this.form.value) {
      if (typeof this.form.value[key] !== 'object') { 
        formModel[key] = this.form.value[key]
      } else { //if formgroup item
        formModel[key] = '';
        for (let k in this.form.value[key]) {
          if (this.form.value[key][k])
            formModel[key] = formModel[key] + k + ';'; //create string with ';' separators like 'a;b;c'
        }
      }
    }
     console.log(formModel)
   }

0

คำตอบที่เกี่ยวข้องกับ @ nash11 นี่คือวิธีที่คุณจะสร้างอาร์เรย์ของค่าช่องทำเครื่องหมาย

และ

มีช่องทำเครื่องหมายที่เลือกช่องทำเครื่องหมายทั้งหมดด้วย:

https://stackblitz.com/edit/angular-checkbox-custom-value-with-selectall

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.