简体   繁体   中英

Polymer 3: How to implement two way data binding for radio input

I'm trying to understand/implement two way attribute binding in a Polymer 3 web component. I've got the following code:

import {html, PolymerElement} from '@polymer/polymer/polymer-element.js';

 class CustomInputComponent extends PolymerElement {
    static get template() {
        return html`        
        <div>
            <template is="dom-repeat" items="{{ratings}}">
                <input type="radio" 
                       name="group" 
                       id="item_{{item.id}}" 
                       value="{{item.checked}}" 
                       checked$="{{item.checked}}">                
            </template>
        </div>`;
    }

    static get properties() {
        return {
            ratings: {
                type: Array,
                value: [
                    { id: 1, checked: true },
                    { id: 2, checked: false }
                ]
            }
        };
    }
}
window.customElements.define('custom-input-component', CustomInputComponent);

As you can see, I have defined a Array property containing a default list of values. This property is a model from which I want to render a radio group. The initial state looks good. But when I click on the unchecked input, the DOM elements don't update correctly.

虽然我点击了第二个元素,但是浏览器也显示了第一个元素。

I'd bet I'm missing something really simple...

The main things are:

  • You are binding to the checked attribute ( $= ), however I don't think radio inputs dynamically update their checked attribute. AFAICT, the checked property is what changes when the input gets selected.

  • Also, native <input type="radio"> inputs will only fire their change and input events when they are selected, not when they are de-selected. Polymer relies on events to trigger property effects like data bindings; this means that an upwards data binding (from the input to your custom element) will only get processed when the checked property on an input changes from false to true . Effectively, once ratings[n].checked becomes true, it will never be made false because Polymer has no way to know that this has occurred.

    Incidentally, to perform two-way binding on a native HTML element, you would also need to include an annotation for the event that the radio input fires when it is selected . So if you did want to capture the changes on selection, it'd be something like checked="{{item.checked::change}}" .

A couple of options:

  • Use paper-radio-button s inside a paper-radio-group instead of native <input> s. These elements behave well for two-way data binding.

  • Listen for the change when a new item gets checked, and manually update ratings[n].checked to false for the previously selected item.

A couple more things about your code

  • (I don't think this is anything to do with your current problem, but it will help avoid future problems) when initializing a default value for an object or array property in a Polymer element, remember to use a function so that each element instance gets its own unique array or object. Eg:

    ratings: { type: Array, value: function(){ return [ { id: 1, checked: true }, { id: 2, checked: false } ]; } }

  • Normally I think, you wouldn't want to change the values of your radio inputs. Conventionally, when the <form> containing a radio input group is submitted, the value on the radio input that is currently checked gets included with the form data, and the other radio input values are ignored. Here's an example on W3Schools . So instead of value="{{item.checked}}" , something like value="[[item.data]]" .

So the whole thing might be something like

class CustomInputComponent extends PolymerElement {
  static get properties () {
    return {
      ratings: {
        type: Array,
        value: function(){ 
          return [
          { id: 1, checked: true, value: 'pie' },
          { id: 2, checked: false, value: 'fries' },
          { id: 3, checked: false, value: 'los dos' }
          ];
        }
      },
      selected: {
        // type: Number or Object, idk
        // Keep track of the selected <input>
      }
    };
  }
  static get template() {
    return html`        
      <p>do you want pie or fries?</p>
      <div id="mydiv">
        <template is="dom-repeat" items="{{ratings}}">
          <input 
            type="radio"
            name="testgroup"
            id="[[item.id]]"
            value="[[item.value]]"
            checked="{{item.checked::input}}"
            on-change="radioChanged"
            >[[item.value]]
          </input>
        </template>
      </div>
    `;
  }
  radioChanged(event){
    // update ratings[n].checked for selected input
    // set selected input to currently selected input
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM