Bind angular 2 model to polymer dropdown

2019-01-19 05:11发布

I decided to take some time this weekend to look at Angular 2 and Polymer. I'm really interested in angular 2 and would really like to start building something with it. One downside with starting with Angular 2 now is that there is no good component library yet. However, since Angular 2 claims that it should work so good together with Web Components I thought of giving Polymer a try. I have succeeded to bind data to simple components like an input field. What I'm stuck at for the moment is how to bind a model to the selected object of a paper-dropdown-menu. Since I'm very new into both I don't really know how to do it but this is what I have tried so far. Has anyone accomplished to bind an angular 2 model to a polymer dropdown?

<paper-dropdown-menu  >
   <paper-menu class="dropdown-content" valueattr="id" [(ng-model)]="model">
       <paper-item *ng-for="#m of options" id="{{m.id}}" (click)="onClick()">{{m.name}}</paper-item>
   </paper-menu>
</paper-dropdown-menu>

EDIT: I have now created a ValueAccessor which seems to work acceptable with one exception. I try to get the dropdown to have a pre-selected value by setting the selected attribute in the writeValue method. At first this seemed to work but after I made this change I can no longer change the selected value. It works if I hardcode the value in the template so it seems to have something to do with angular together with polymer. I tried to follow the stacktrace and compare the difference between the two are. When I hardcode the value a setter method for selected is executed which trigger an item-select event. When I follow the same trace when I set the property in the valueAccessor there the setter method is no longer executed. Seems to be a problem with the interaction between angular 2 and polymer.

import {Directive, ControlValueAccessor, ElementRef, Renderer, NG_VALUE_ACCESSOR, Provider, forwardRef} from "angular2/angular2"
import {isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
import {setProperty} from "angular2/src/common/forms/directives/shared"

const PAPER_DROPDOWN_VALUE_ACCESSOR = CONST_EXPR(new Provider(
    NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => PaperDrowpdownMenuAccessor), multi: true}));

@Directive({
  selector: 'paper-menu[ng-model]',
  bindings: [PAPER_DROPDOWN_VALUE_ACCESSOR]
})
export class PaperDrowpdownMenuAccessor implements ControlValueAccessor {

  onChange = (_) => {};
  onTouched = () => {};

  constructor(private _renderer: Renderer, private _elementRef: ElementRef) {
    var self = this;
    this._elementRef.nativeElement.addEventListener('iron-select', function(e, v, s){
      console.log(e.target.selected);
      self.onChange(e.target.selected);
    });
  }

  writeValue(value: any): void {
    if(value){
      if(this._elementRef.nativeElement.select) {
        this._elementRef.nativeElement.select(value);
      }
      else {
        //this._elementRef.nativeElement.attributes["selected"]
        setProperty(this._renderer, this._elementRef, 'selected', value);
      }
    }       
  }

  registerOnChange(fn: () => any): void { 
    this.onChange = fn; 
  }
  registerOnTouched(fn: () => any): void { this.onTouched = fn; }
}

1条回答
三岁会撩人
2楼-- · 2019-01-19 05:36

I finally solved this by my own by implementing a custom Value Accessor, mainly by looking at how the default value accesssor is implmented. https://github.com/angular/angular/blob/2.0.0-alpha.46/modules/angular2/src/common/forms/directives/default_value_accessor.ts

I struggled a bit with this since paper-menu wants the pre-selected value to be set as an attribute in the rendered html. In my first attempt I used angulars internal setProperty to set the selected value. However, this sets the DOM property and not the HTML attribute and resulted in that polymer didn't create a get,set property of selected which prevented the menu to trigger iron-select event which the dropdown menu listens for. Lesson learned, remember the difference between HTML and DOM.

import {Directive, ControlValueAccessor, ElementRef, Renderer, NG_VALUE_ACCESSOR, Provider, forwardRef} from "angular2/angular2"
import {CONST_EXPR} from 'angular2/src/facade/lang';

const PAPER_MENU_VALUE_ACCESSOR = CONST_EXPR(new Provider(
    NG_VALUE_ACCESSOR, {useExisting: forwardRef(() => PaperMenuAccessor), multi: true}));

@Directive({
  selector: 'paper-menu[ng-model]',
  bindings: [PAPER_MENU_VALUE_ACCESSOR]
})
export class PaperMenuAccessor implements ControlValueAccessor {

  onChange = (_) => {};
  onTouched = () => {};

  constructor(private _renderer: Renderer, private _elementRef: ElementRef) {
    this._elementRef.nativeElement.addEventListener('iron-select', (e) => {
      this.onChange(e.target.selected);
    });
  }

  writeValue(value: any): void {
    if(this._elementRef.nativeElement.select) {
      this._elementRef.nativeElement.select(value);
    }
    else {
      this._elementRef.nativeElement.setAttribute("selected", value);
    }   
  }

  registerOnChange(fn: () => any): void { this.onChange = fn; }
  registerOnTouched(fn: () => any): void { this.onTouched = fn; }
}
查看更多
登录 后发表回答