Skip to content

Proposal: extract core checkbox and radio behavior into separate @Directives #347

@shlomiassaf

Description

@shlomiassaf
  • Do you want to request a feature or report a bug? feature
  • What is the current behavior?
    A User can use md-checkbox / md-radio-X components to create chebox's and radio's.
  • What is the expected behavior?
    User can apply checkbox / radio behaviour to any element as well as use the currently available
    md-checkbox / md-radio-X components.
  • What is the motivation / use case for changing the behavior?
    By implement checkbox & radio group/button as directive's users can apply them on other elements.
    The current implementation of md-checkbox & md-radio-X will use the directives in their implementation so we are not duplicating code.
    Having this, user's can easily create a Toggle button or a group of toggle buttons as well as radio group made of buttons (as in bootstrap).

This will also enable #112 today and reduce the code for it.

  • Other information
    This is generally how the directive should look like
import {
    Directive,
    Provider,
    forwardRef,
    Input,
    Output,
    EventEmitter
} from 'angular2/core';

import {
    NG_VALUE_ACCESSOR,
    ControlValueAccessor
} from 'angular2/src/common/forms/directives/control_value_accessor';

let nextId = 0;

const CHECKBOX_CONTROL_VALUE_ACCESSOR = new Provider(
    NG_VALUE_ACCESSOR, {
        useExisting: forwardRef(() => AsCheckbox),
        multi: true
    });


@Directive({
    selector: '[asCheckbox]',
    host: {
        'role': 'checkbox',
        '[id]': 'id',
        '[class.md-checkbox-checked]': 'checked',
        '[class.md-checkbox-disabled]': 'disabled',
        '[attr.tabindex]': 'disabled ? null : tabindex',
        '[attr.aria-checked]': 'ariaChecked',
        '[attr.aria-disabled]': 'disabled',
        '(click)': 'onInteractionEvent($event)',
        '(keydown.space)': 'onKeyDown($event)',
        '(keyup.space)': 'onInteractionEvent($event)',
        '(blur)': 'onTouched()'
    },
    providers: [CHECKBOX_CONTROL_VALUE_ACCESSOR]
})
export class AsCheckbox implements ControlValueAccessor {
    @Input() id: string = `md-checkbox-${++nextId}`;
    @Input() disabled: boolean = false;
    @Input() tabindex: number = 0;

    @Input() get checked(): boolean {
         return this._checked;
     }

     set checked(checked: boolean) {
         if (checked === this._checked) return;
         this._checked = checked;
         this.change.emit(this._checked);
     }

    get ariaChecked(): 'true' | 'false' {
        return this.checked ? 'true' : 'false';
    }

    @Output() change: EventEmitter<boolean> = new EventEmitter();

    private _checked: boolean = false;
    private _changeSubscription: {unsubscribe: () => any} = null;

    toggle() {
        this.checked = !this.checked;
    }

    onInteractionEvent(event: Event) {
        if (this.disabled) {
            event.preventDefault();
            event.stopPropagation();
            return;
        }
        this.toggle();
    }

    onKeyDown(evt: Event) {
        evt.preventDefault();
    }

    writeValue(value: any) {
        this.checked = !!value;
    }

    registerOnChange(fn: any) {
        if (this._changeSubscription) {
            this._changeSubscription.unsubscribe();
        }
        this._changeSubscription = <{unsubscribe: () => any}>this.change.subscribe(fn);
    }

    registerOnTouched(fn: any) {
        this.onTouched = fn;
    }

    onTouched(): any {}

}

From here, creating a toggle button is as simple as

<span class="md-button-wrapper" asCheckbox>Toggle me</span>

The code above was writting on the fly so it's just for the idea.
I'v implemented it in some components I work with not related to material, I can post a PR if you like the idea.

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureThis issue represents a new feature or feature request rather than a bug or bug fix

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions