简体   繁体   中英

Strange behavior in Polymer data-binding to an attribute

Using Polymer 1.0 I'm trying to bind to an attribute of a custom element, and just display it.

The custom element is in fact an <iron-input> list, that has an add and a delete button. I'd like to reflect any change in that list to the host. It also has a minItemSize attribute meaning it has at least this many elements. So I added a check to the observer, adding extra elements in case it goes under this number.

But when I bind to the attribute that holds the list, things get out of sync, and I can delete all of the inputs from the ui.

I have two <dyn-inputlist> elements. In one of them I don't bind to the data attribute, in the other I do.

The first one behaves as expected: adds and removes on the button click.

The other doesn't work, because you can remove all input boxes. Even though the data itself is updated, and filled with extra items, for some reason the UI doesn't reflect this. (Checking the data property of the element does show that it has the correct number of items)

I also expect that if I set data={{myData}} on both dyn-inputlist element, they always display the same thing. But pressing add/remove buttons randomly on either component gets them out of sync.

Am I missing something? Thanks in advance.

index.html :

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="bower_components/webcomponentsjs/webcomponents.js"></script>
    <link rel="import" href="components/dyn-inputlist.html"/>
</head>
<body>

<template is="dom-bind">
    <dyn-inputlist min-item-size="4"></dyn-inputlist>
    <div>{{mydata}}</div>
    <dyn-inputlist min-item-size="4" data="{{mydata}}"></dyn-inputlist>
</template>

</body>
</html>

dyn-inputlist.html :

<link rel="import" href="../../polymer/polymer.html">
<link rel="import" href="../../iron-input/iron-input.html">

<dom-module id="dyn-inputlist">
    <template>
        <button on-click="removeItem">x</button>
        <button on-click="addItem">+</button>
        <template is="dom-repeat" items="{{data}}">
            <div>
                <span>{{index}}</span>
                <input is="iron-input" bind-value="{{item.content}}">
            </div>
        </template>
    </template>
    <script>
        Polymer({
            is: 'dyn-inputlist',
            properties: {
                minItemSize: {
                    type: Number,
                    notify: true,
                    value: 1
                },
                data: {
                    type: Array,
                    reflectToAttribute: true,
                    notify: true,
                    value: function () {
                        return []
                    }
                }
            },

            observers: ['_dataChanged(data.*)'],

            addItem: function (e) {
                this.unshift('data', {content: ""});
                this.reflectPropertyToAttribute('data')
            },

            removeItem: function (e) {
                this.shift('data');
                this.reflectPropertyToAttribute('data')
            },

            _dataChanged: function (e) {
                if (this.data != null) {
                    while (this.data.length < this.minItemSize) {
                        this.push('data', {content: ""})
                    }
                } else {
                    this.data = [{content: ""}];
                }
                this.reflectPropertyToAttribute('data');
            }

        });
    </script>
</dom-module>

EDIT: This is the live code: http://jsbin.com/poquke/1/edit?html,output

I have played around a bit with your code and I noticed that it will work if you wrap the code in your changed handler in an async function. This fixed both issues that you described.

_dataChanged: function (e) {
    this.async(function(){
        if (this.data != null) {

            while (this.data.length < this.minItemSize) {
                this.push('data', {content: ""})
            }
        } else {
            this.data = [{content: ""}];
        }
    });
}

I don't have a perfect explanation for this behaviour. I assume it is related somehow to the way Polymer handles the observation for changes. Each time you push to the data array in the changed handler, this in fact changes data and should in turn trigger the handler again.

No async is required if you simplify.

Here is the simplified code, this removes the repeated calls to _dataChanged when you push the minimum values, and allows polymer's built-in eventing system to take care of updating and notifying the other elements. A function: _createNewItem() is for creating an object. This simplifies where item object creation is handled.

http://jsbin.com/vemita/6/edit?html,output

The link and URL references have changed from the sample code in the question above to conform to the polymer element and demo page standards to be used with polyserve.

I've commented on your original code for why each line should or shouldn't be there. this includes the reason for the changes to _dataChanged

http://jsbin.com/ponafoxade/1/edit?html,output

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