7

I am writing this post after having read several threads concerning this topic but no one of them gives me what I need. This post seems to have the solution but I do not have to read the checked values from the json.

All I need is to:

  1. read countries from an array of objects
  2. build dinamically a list of checkbox representing each country
  3. user should check and uncheck each checkbox

    bonus:

    • get the value of the checked input and send it outside the component

    I know It might be really dumb to do but all I have accomplished untile now is to have a list of uncheckable checkboxes and nothing more.

Here is the code: Template:

<div class="form-group">
    <div *ngFor="let country of countries">
        <input type="checkbox"
                  name="countries"
                  value="{{country.id}}"
                  [(ngModel)]="country"/>

        <label>{{country.name}}</label>
    </div>
</div>

And TS:

countries = [
    {id: 1, name: 'Italia'},
    {id: 2, name: 'Brasile'},
    {id: 3, name: 'Florida'},
    {id: 4, name: 'Spagna'},
    {id: 5, name: 'Santo Domingo'},
]

I tried to use the reactive forms but that gave me more issues then template driven (surely because of bad implementation of mine). Please, help me, I do not know where to bump my head anymore

Nad G
  • 507
  • 2
  • 10
  • 21
  • Just some first remarks : you probably want a checkbox name generated from id, not using the same name for all your checkboxes. The ngModel for each checkbox will be the boolean value "checked" (true) or unchecked (false, or undefined/null maybe). So basically, you may want to bind the ngModel to a boolean that correspond to the particular country of the checkbox, for instance an additional field on your list of country, as Eliseo suggested in the answer posted. – Pac0 Aug 29 '18 at 09:51
  • I could put a 'checked: false' in the json as the link I provided and Eliseo suggested. I tried it but I couldn't then check them, so I am asking how can I do this – Nad G Aug 29 '18 at 09:58
  • you have to bind the ngModel of each checkbox to each country "checked" property.. I'm making a Stackblitz , I'll show you in an answer in a moment. – Pac0 Aug 29 '18 at 19:36

3 Answers3

13

Here is a working example, where you can observe that an additional 'checked' value is added to each country, and bound to the value of each checkbox with [(ngModel)].

Stackblitz live example

template:

<p>
Test checkboxes
</p>

<div *ngFor="let country of countries; let i = index;">
  <input type="checkbox" name="country{{country.id}}" [(ngModel)]="countries[i].checked">
  <label for="country{{country.id}}">{{country.name}}</label>
</div>

<button type="button" (click)="sendCheckedCountries()" *ngIf="countries">Click to send the selected countries (see your javascript console)</button>

<p *ngIf="!countries">loading countries, please wait a second...</p>
<p *ngIf="countries">Debug info : live value of the 'countries' array:</p>

<pre>{{ countries | json }}</pre>

component :

//...
export class AppComponent implements OnInit  {
  public countries: Country[];

  constructor(private countryService: CountryService) {}

  public ngOnInit(): void {
    // loading of countries, simulate some delay
    setTimeout(() => {
       this.countries = this.countryService.getCountries();
    }, 1000);
  }

  // this function does the job of sending the selected countried out the component
  public sendCheckedCountries(): void {
    const selectedCountries = this.countries.filter( (country) => country.checked );
    // you could use an EventEmitter and emit the selected values here, or send them to another API with some service

    console.log (selectedCountries);
  }
}

To use some proper TypeScript, I made an interface Country :

interface Country {
    id: number;
    name: string;
    checked?: boolean;
}

I hope you get the idea now.

Note : the checked value is not "automatically there" at the beginning, but it doesn't matter.

When not there, it is the same as undefined, and this will be treated as false both in the checkbox and in the function that reads which country is checked.

For the "sending value" part :

The button will output the selected value to the browser's console, with some filter similar to what @Eliseo's answer suggests (I just used full country objects instead of ids)

For "real usecase" situation, you could use Angular's EventEmitters and have your component "emit" the value to a parent component, or call some service function that will make a POST request of your values to another API.

Pac0
  • 21,465
  • 8
  • 65
  • 74
  • 1
    That's perfect, thank you a lot for spending time to resolve my problem! Accepted – Nad G Aug 30 '18 at 08:16
  • I googled around a little before discovering your solution.. I knew it had to be this easy. Thanks ( I know its not a 'good' comment - but felt it needed to be said). – terary Jun 28 '19 at 07:42
  • use id for the checkboxes instead of name else clicking on label won't work – Rishabh Sep 13 '19 at 13:54
  • @Pac0 How can you add select all button for the same? – Sai Manoj Mar 21 '20 at 10:37
  • @SaiManoj just create a function that loops through all the array and set the `checked` value. `this.countries.forEach(x => x.checked = true);` and call this function when the button is clicked. – Pac0 Mar 21 '20 at 10:47
  • @Pac0 I have used for loop to loop through all the arrays. But my bad some where it went wrong. I'm not able to pass the data after selection through select all button. Any chance for updating in the above solution or in stackblitz. TIA `allNonTrades(event) { const checked = event.target.checked; this.putAwayPurchaseOrderListDetailsData.forEach(item => item.checked = true); }` – Sai Manoj Mar 21 '20 at 10:55
  • @SaiManoj Maybe it's better if you create a new fresh question with a [mcve] containing what you tried. You can post a link to this new question here as well, of course. – Pac0 Mar 21 '20 at 10:58
  • 1
    @Pac0 Sorry for the delay. Yesterday there was power shutdown so not able to post. Here is my question https://stackoverflow.com/q/60800300/9380480 – Sai Manoj Mar 22 '20 at 14:06
3

Your countries like

{id: 1, name: 'Italia',checked:false},

Your html like

<div *ngFor="let country of countries">
  <input type="checkbox" [(ngModel)]="country.checked"/>
  <label>{{country.name}}</label>
</div>

You'll get an array like, e.g.

 [{id: 1, name: 'Italia',checked:false},{id: 2, name: 'Brasile',checked:tue}..]

you can do

result=this.countries.filter(x=>x.checked).map(x=>x.id)
//result becomes [2,...]
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • Good sketch/answer. As a remark : I think that a name is needed on an input, in order to use ngModel. – Pac0 Aug 29 '18 at 09:53
  • But I don't need to read which country is already checked from the json, I just have to let the user check or uncheck the boxes (unchecked for deafult) – Nad G Aug 29 '18 at 09:54
3

I had an error using [(ngModel)]

In case it serves anyone, I have solved the problem changing [(ngModel)] to:

[checked]="countries[i].checked" (change)="countries[i].checked= !countries[i].checked"
dvque
  • 469
  • 4
  • 13