ProgressNS / nativescript-ui-feedback

This repository is used for customer feedback regarding Telerik UI for NativeScript. The issues system here is used by customers who want to submit their feature requests or vote for existing ones.
Other
115 stars 21 forks source link

[Vue] RadListView bound to ObservableArray: Issue with virtualization #1179

Open tbozhikov opened 5 years ago

tbozhikov commented 5 years ago

Tell us about the problem

Consider this setup -> https://play.nativescript.org/?template=play-vue&id=y9si9q&v=4

In iOS and Android there is an issue with displaying the correct information on each item, maybe related to virtualization problem. To reproduce:

  1. add 10 new items (click add item button)
  2. change the values of the inputs randomly
  3. begin scrolling back and forth
  4. The result is now the values are not always displayed on the items they belong to

Which platform(s) does your issue occur on?

iOS and Android

Please provide the following version numbers that your is with:

msaelices commented 5 years ago

@tbozhikov I think there is an error in the example you linked, in this fragment:

<DockLayout stretch-last-child="true" :key="item.id"
                            class="scan-list-item">

Actually, item.id is undefined so we are using the same key for each element, which I guess give us random results.

Try to change the :key="item.id" with :key="index" and see what happens.

I've done this in a local sample I prepared with your code and it worked, at least with the tests I made:

observables-issue mov

msaelices commented 5 years ago

@tbozhikov BTW, the second column is not updated after the input fields are modified. Maybe this is the issue you are talking about.

msaelices commented 5 years ago

@tbozhikov I guess that the issue of stale values in the last column is caused by the recycling mechanism in the RADListView. See the next code I've tried:

import { ObservableArray } from "tns-core-modules/data/observable-array/observable-array";

const initialData = [{
  name: "first",
  value: 1
},
{
  name: "second",
  value: 2
},
{
  name: "third",
  value: 3
}];

export default {
  name: "Observables Issue",
  template: `
  <Page>
    <ActionBar :title="title">
    </ActionBar>
    <StackLayout>
      <GridLayout class="m-x-5" rows="auto, *, auto, auto">
        <Label text="RadListView:" class="m-r-10 text-center" />
        <RadListView ref="listView" row="1" for="(item, index) in itemList">
          <v-template>
            <DockLayout stretch-last-child="true" :key="index"
                        class="scan-list-item">
              <StackLayout orientation="horizontal" dock="right"
                           class="m-0 stepper-container-combo">
                <TextField v-model.number="item.value"
                           keyboardType="number" width="40" class="m-r-10 text-center" />
                <Label :text="item.value" width="40" class="m-r-10 text-center text-muted" />
              </StackLayout>

              <StackLayout class="p-10">
                <Label :text="item.name" />
              </StackLayout>
            </DockLayout>
          </v-template>
        </RadListView>
        <Label row="2" :text="'First item value: ' + itemList.getItem(0).value" class="m-r-10 text-center text-muted fa-icon" />
        <StackLayout row="3">
          <Button @tap="onBtnTap" text="add item" height="50"></Button>
          <Button @tap="onRefresh" text="refresh" height="50"></Button>
        </StackLayout>
      </GridLayout>
    </StackLayout>
  </Page>
  `,
  data() {
    return {
      itemList: new ObservableArray(initialData),
    };
  },
  methods: {
    onBtnTap() {
      this.itemList.push({
        name: "new one",
        value: 1
      });
    },

    onRefresh () {
      this.$refs.listView.refresh();
    },
  },
};
msaelices commented 5 years ago

Please see the screenshot which shows the issue:

observables-issue-2 mov

VladimirAmiorkov commented 5 years ago

@msaelices The way that views are recycled is that only their UI is recycled but their "binding context" or "context" is reset. This means that when a UI is reused for a different item it is impossible to show an incorrect data if the wrapper (in this case the directive for Vue) is setting it correctly. In RadListView this happens here which is called by onItemLoadingInternal which itself is called by the native component here while it is loading a "View" for a corresponding object from the items collection.

For me the main issue is why is the UI not updated immediately while the input is being changed, if we can force that update to be immediate we will not rely on the "recycling" functionality to update the entire View of the cell. Relying on that is not a good approach because that recycling is handled by the OS and we have no control over it.