-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore(rating): rating does not support model-driven form #298
Comments
Yes, it doesn't by design |
I think the main issue was why the component need explict inject NgModel? class MyComp implements ControlValueAccessor
constructor(@Self() public cd:NgModel) { // here
cd.valueAccessor = this;
}
} It is really unnecessary and worst case will become cyclic dependency. Best way is lets framework to take over. You can check out the usage of selectValueAccessor/setUpControl in angular. Here is a custom select comonent base on dropdown directive, it work well with ngModel/ngControl/ngFormControl. import {Component, Directive, Renderer, ElementRef, Host, Self, Optional, Input, OnInit, Provider,
provide, forwardRef, HostListener, Inject} from 'angular2/core';
import {NG_VALUE_ACCESSOR, ControlValueAccessor, NgModel } from 'angular2/common';
import {CONST_EXPR} from 'angular2/src/facade/lang';
import {DROPDOWN_DIRECTIVES} from '../../ng2-bootstrap/ng2-bootstrap';
@Directive({ selector: '[select-item]' })
export class SelectItem implements OnInit {
/* tslint:disable:input-property-directive */
@Input('select-item') private value: any;
constructor(
@Host()
@Inject(forwardRef(() => SelectComponent))
private select: SelectComponent) {
}
ngOnInit() {
const getter = this.select.valueGetter;
if (getter && this.value) {
const controlValue = typeof getter === 'string' ? this.value[getter] : getter(this.value);
if (controlValue === this.select.value)
setTimeout(() => this.select.value = this.value);
}
}
@HostListener('click')
private onClick() {
return this.select.updateFromView(this.value);
}
}
const CUSTOM_VALUE_ACCESSOR = CONST_EXPR(new Provider(NG_VALUE_ACCESSOR,
{ useExisting: forwardRef(() => SelectComponent), multi: true }));
@Component({
selector: 'my-select',
template:
`<div class="btn-group" dropdown keyboardNav="true">
<button class="btn" [ngClass]="invalidNgClass" type="button" dropdownToggle>
{{text}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" dropdownMenu>
<li role="menuitem" *ngFor="#item of options">
<a class="dropdown-item" href="javascript:void(0)" [select-item]="item">
{{getItemText(item)}}
</a>
</li>
</ul>
</div>`,
providers: [CUSTOM_VALUE_ACCESSOR],
directives: [...DROPDOWN_DIRECTIVES, SelectItem]
})
export class SelectComponent implements ControlValueAccessor, OnInit {
public value: any;
@Input() public caption: string = 'Select ...';
@Input() public valueGetter: string | ((any) => any);
@Input() public textGetter: string | ((any) => string);
@Input() public invalidClass: string = '';
@Input() public set invalid(value: boolean) {
if (value) {
this.invalidNgClass.push(this.invalidClass);
} else {
this.invalidNgClass.splice(this.invalidNgClass.indexOf(this.invalidClass), 1);
}
}
@Input() public options: Array<any> = [];
private invalidNgClass: Array<string> = [];
constructor() {
}
ngOnInit() { }
public get text() {
const text = this.getItemText(this.value);
if (text !== undefined)
return text;
return this.caption;
}
/**
* (ControlValueAccessor.writeValue implement)
* model -> view
* @param {*} value (value to write)
*/
public writeValue(value: any) {
this.value = value;
}
/**
* (From view(UI event) writes back to model)
* view -> model
* @param value (value to write)
*/
public updateFromView(value) {
this.value = value;
this.onChange(this.convertToControlValue(value));
}
private onChange = (_: any) => { };
private onTouched = () => { };
public registerOnChange(fn: (_: any) => {}): void {
this.onChange = fn;
}
public registerOnTouched(fn: () => {}): void {
this.onTouched = fn;
}
private getItemText(value) {
const getter = this.textGetter;
if (getter && value) {
if (typeof getter === 'string') {
return value[getter];
} else {
return getter(value);
}
}
return undefined;
}
private convertToControlValue(value) {
const getter = this.valueGetter;
if (getter && this.value) {
if (typeof getter === 'string') {
return value[getter];
} else {
return getter(value);
}
}
return value;
}
} |
Yes, some components are implemented like this, good point for PR :) |
BTW better to extend default value accessor, as far as I remember |
created a PR but typeahead seems too complex need more work on refactor |
check typeahead related branch first, guys were trying to refactor it before already |
The way in sample works well:
<rating [(ngModel)]="rate" max="5"></rating>
But if change to model-driven form, it will show the error:
The text was updated successfully, but these errors were encountered: