Show Checkbox Alert with "All Items" Option

I’m showing an alert (AlertController) in Ionic2 with checkboxes. Each checkbox represent a subcategory, except the first one that is All Subcategories. I want that when I check the first option, all others lose the checked flag, and, on the other hand, if I check another option the first one loses the checked flag.

From what I see in the docs http://ionicframework.com/docs/v2/components/#alert-checkbox there’s nothing like that.

The object with the alert information is an AlertOptions that has an inputs property that is an array of AlertInputOptions. Both can be imported from ionic-angular. The AlertInputOptions interface is:

interface AlertInputOptions {
    type?: string;
    name?: string;
    placeholder?: string;
    value?: string;
    label?: string;
    checked?: boolean;
    disabled?: boolean;
    id?: string;
}

I don’t see any property to handle that, like an onChange: (checked: boolean) => void event handler function, an Observable or something like that. I also don’t see it in the AlertController class nor in the AlertOptions interface.

From what I see, I think that it isn’t possible yet, but I want to know if this will be implemented and when (at least vaguely), because otherwise I can’t implement the functionality that I want.

Thanks in advance.

2 Likes

have a look at this post:

does that help?

1 Like

Hi, @fishgring, thanks for the reply!

Unfortunately that is not my problem. I don’t have the checkboxes in my component HTML. Ionic creates it dynamically when it creates the alert, like in docs the example (http://ionicframework.com/docs/v2/components/#alert-checkbox):

  showCheckbox() {
    let alert = this.alertCtrl.create();
    alert.setTitle('Which planets have you visited?');

    alert.addInput({
      type: 'checkbox',
      label: 'Alderaan',
      value: 'value1',
      checked: true
    });

    alert.addInput({
      type: 'checkbox',
      label: 'Bespin',
      value: 'value2'
    });

    alert.addButton('Cancel');
    alert.addButton({
      text: 'Okay',
      handler: data => {
        console.log('Checkbox data:', data);
        this.testCheckboxOpen = false;
        this.testCheckboxResult = data;
      }
    });
    alert.present();
  }

My case is about creating an alert similar to the one in the code above. The inputs are created by Ionic dynamically with the data you pass like type, label, value and checked. But I see no way to change the checked flag programatically (only the initial value) nor to listen to the checkboxes events like change.

Maybe I can do that accessing the DOM directly, but that is hack, isn’t a good approach, is not simple and is error prone, so I don’t think in going this way.

3 Likes

I have the same problem. Sadly, AlertController dont provide an event on change item (like in checkbox).

1 Like

Same here. Could you solve it?

@Colo9311 No, you can use a modal though, creating your own component to behave the way you want, but it occupies all the space in the screen (I would like to show as the alert, occuping the screen partially).

There’s a showBackdrop modal option, and in browser with a larger page it works fine, but for pages with smaller width it doesn’t work (it seems it is hardcoded with media queries in css to work with more than 768px of width and 600px of height).

If you don’t care about the modal occupying all the screen it should work fine.

You can see about modals here.

You can see a discussion about changing the modal size here.

1 Like

Thanks a lot @lucasbasquerotto

Not 100% sure if I’m on the same page, but I often create modals and programmatically assign a ‘checked’ property based on data I store in an array, i.e.,

checked={{isTrue[i]}}.

Maybe it can be applied to an alert.

For example.

Let myAlert = this.alertCtrl.create();

For (let i = 0; i < this.keywords.length; i++) {
myAlert.addInput({
  type: 'checkbox',
  label: this.keywords[i],
  value: i.toString(),
  checked: this.isTrue[i] as boolean
})

Of course, I fill this.isTrue by pushing true or false to it based on some condition until all the conditions have been checked, then pass that to modalOptions. Again, might be a way to apply that to alertCtrl, and it MIGHT change the checked values dynamically. Then again, it might not…

Also, by adding inputs dynamically, you can attach a handler to each checkbox such as,

For (let i = 0; i < this.keywords.length; i++) {
myAlert.addInput({
  type: 'checkbox',
  label: this.keywords[i],
  value: i.toString(),
  checked: this.isTrue[i]
  handler: (data) =>  { if (this.isTrue[data]   ===   true) {
  this.isTrue[data] = false;
  for (let i = 0; i < myAlert['instance']['d']['inputs'].length; i++) {
  if (i !== data.value) {
      myAlert['instance']['d']['inputs'][i]['checked'] = false
    } else {
      this.isTrue[data] = true;
  })
})

With the handler modified to fit what you need.
The method below does achieve what you want (very loosely, and somewhat messily). In this case, if you click on all the options, they’ll end up being checked as normal. After that, clicking on any option turns all checkboxes to unchecked except for the first one, which retains the checked property no matter what. Not exactly what you want, but I suppose it shows it can be done with a little work

for (let i = 0; i < this.myArray.length; i++ ) {
     galleryChart.addInput({
       type: 'checkbox',
       label: this.titleArray[i].toString(),
       value: i.toString(),
       checked: this.checkedArray[i] as boolean,
       handler: (data) => {
         if ( myAlert['instance']['d']['inputs'][data.value]['checked'] === false ) {
           for (let i = 0; i < myAlert['instance']['d']['inputs'].length; i++){
           myAlert['instance']['d']['inputs'][i]['checked'] = false;
              }
           myAlert['instance']['d']['inputs'][0]['checked'] = true;
            };
       }
     })
1 Like

I find loops like this to be harder to read than their Array utility counterparts map, forEach, and filter, because when I see map I instantly think “array is being transformed into another array”, when I see forEach I think “operation is being done on each element”, when I see filter I think “array is being winnowed down”. When I see a generic loop, it’s not immediately clear which of those sorts of operations is occurring unless you carefully read the body of the loop.

Agreed, @rapropos. I, however, haven’t mastered map and filter whatsoever. But, I think if my suggestion was executed with your technique, there may be one less problem in the world of Alert Controllers.

If you’re feeling charitable, @rapropos, perhaps you could write out a quick example of how I would execute that process using map… for some reason, the simplest of things can be the hardest for me, and map is one of those things…and not for lack of trying.

In this case, I would probably just replace your loop with a forEach, because we’re calling addInput() on each element of the array. MDN has some good examples of map().

Right on. I’ll take another look. I’ve used MDN as a resource and it’s very good, but at the the time I was looking into map() and forEach(), I was VERY new to programming. A revisit may help

A little bit cleaner of a ‘solution’, without being set up for OP’s purposes. But a decent foundation to get there.

for (let i = 0; i < this.titleArray.length; i++ ) {

     galleryChart.addInput({
       type: 'checkbox',
       label: this.titleArray[i].toString(),
       value: i.toString(),
       handler: () => {
         
         let inputArray = <Array<object>>galleryChart.data.inputs;
         let myValue = inputArray[i]['value'];

         inputArray.map((input) => {
           if (input['value'] !== myValue){
             input['checked'] = false;
           }
          })
         }
       })
   }
1 Like

let inputArray = <Array>galleryChart.data.inputs;

i’m sorry but what is the galleryChart.data.inputs on this context ?

Replace that with alert.data.inputs and let alert = this.alertCtrl.create().

So that alert.data.inputs is the alert controller’s inputs (checkboxes)

galleryChart was the name for an instance of alertController

Here what the OP I think was after …
image

Then when users click on option they get an alert with prompts matching the current settings

Here is the array I started with but you can have lots of variations (and you can build it from JSON and/or storage data)

// 20180402
const PauseAfterOptions = [
    // Alert Inputs will be augmented to look like this first one dynamically (via code) :)  
    { type: 'radio', label: '0 (no pause)', value: '0', checked: false },
  
    { type: 'radio', label: '1 second', value: '1' },
    { type: 'radio', label: '2 seconds', value: '2' },
    { type: 'radio', label: '3 seconds', value: '3' },
]

In my OptionsPage.html I call OptionsPages.ts method…

@Component({
    templateUrl: 'OptionsPage.html'
})
// 20180401
export class OptionsPage {
    //
...
    // 20180402
    PauseAfterSelection() { 
        // -) Loop and set the array item checked to match current setting
        let AlertInputs = PauseAfterOptions.map((Item) => {
            // add 'checked' to each object in our array
            Item['checked'] = Item['label'] === this.Options.PauseAfter
            return Item
        })
        // -) Setup alert
        let Alert = this.AlertController.create({
            title: 'Pause After Selection',
            message: 'Make a selection...',
            // https://ionicframework.com/docs/components/#alert-radio
            inputs:
                AlertInputs,
            buttons: [{
                text: 'Ok',
                handler: (data) => {
                    // INOTE: storing the label, but you can store the index :)
                    this.Options.PauseAfter = PauseAfterOptions[data].label
                }
            },
            ]
        });
        // Show the alert
        Alert.present();
    }
}

For completion here is the View template chunk…

        <ion-item-divider color="primary"
            (click)="PlaybackOptionsReset()">
            Playback Settings
            <button ion-button
                item-end
                outline
                color="light">Reset</button>
        </ion-item-divider>
        <ion-list-header>Pause After Selection</ion-list-header>
        <ion-item (click)="PauseAfterSelection()">
            <ion-icon name="pause"
                item-start></ion-icon>
            <ion-label>Time to pause after selection is: {{Options.PauseAfter}}</ion-label>
        </ion-item>

BTW, I despise CaMelCaSe, and hate anything case sensitive :slight_smile:
Enjoy!

Actually I have no problems with radio buttons, only with checkboxes in alerts (because there can be only 1 radio checked, but with checkboxes I allow the user to check several, but if he/she selects the one with the “All items” option, this will be checked and all others will be unchecked).

I was answering your second post really

But I see no way to change the checked flag programatically (only the initial value) nor to listen to the checkboxes events like change”

I now understand that you would like to code some custom interaction while the alert is displayed. As you mentioned it does not appear that presetting those “rules” with Alerts inputs is a possibility today.


I see nothing documented in the Alert API about events in above link, and I doubt it would dynamically re-evaluate the inputs while you change them from the current handler.
So unless there is a guru out there, and I am not one of them, you might have to custom code your popup. You probably did that by now :slight_smile:

1 Like

@lucasbasquerotto I’ve added a button “Select all” and it works. Maybe this will help you:

        alert.addButton({
          text: 'Select All',
          handler: data => {
            alert.data.inputs.forEach((input) => {input.checked = true;});
            return false; // if you don't want to close the alert
          }
        });