{ "id": "145412", "key": "TIMOB-18680", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [ { "id": "16723", "description": "Windows Platform Support, ListView updates, Vector overlays in maps", "name": "Release 4.1.0", "archived": false, "released": true, "releaseDate": "2015-07-08" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2015-03-17T21:21:11.000+0000", "created": "2015-03-04T23:10:06.000+0000", "priority": { "name": "Critical", "id": "1" }, "labels": [ "ListItem", "ListView", "index" ], "versions": [ { "id": "16711", "description": "Release 3.5.1", "name": "Release 3.5.1", "archived": false, "released": true, "releaseDate": "2015-03-06" } ], "issuelinks": [], "assignee": { "name": "vduggal", "key": "vduggal", "displayName": "Vishal Duggal", "active": false, "timeZone": "America/Los_Angeles" }, "updated": "2015-06-15T08:31:41.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10206", "name": "iOS", "description": "iOS Platform" } ], "description": "One of our Titanium apps uses ListViews extensively, including for building forms, where each ListItem contains a form control (TextField, Picker, etc...) If the form has more ListItems than the size of the screen, as you scroll down it causes issues where the values from certain TextFields are copied into other ListItems' TextFields.\r\n\r\nThis is clearly connected to the ListView's row caching, it's expected that it would cache the list rows so that you only have as many as are on the screen in memory, however it's clearly not setting the values correctly for each row as scrolling occurs.\r\n\r\nI've boiled it down to a simple reproduction case below, simply input values into the first few rows (e.g. \"test 1\", \"test 2\", \"test 3\") then scroll down and you'll see those 3 values populated into other rows.\r\n\r\n{code:javascript}\r\nvar win = Ti.UI.createWindow({ backgroundColor: 'white' });\r\n\r\nvar defaultTemplate = {\r\n childTemplates: [\r\n {\r\n type: 'Ti.UI.Label',\r\n bindId: 'label',\r\n properties: {\r\n top: '5dp', left: '5dp'\r\n }\r\n },\r\n {\r\n type: 'Ti.UI.TextField',\r\n bindId: 'value',\r\n properties: {\r\n top: '30dp', left: '5dp', right: '5dp', bottom: '5dp',\r\n backgroundColor: 'gray', color: 'black', height: '40dp'\r\n }\r\n }\r\n ]\r\n};\r\n\r\nvar listView = Ti.UI.createListView({\r\n top: '20dp',\r\n templates: { 'default': defaultTemplate },\r\n defaultItemTemplate: 'default',\r\n rowHeight: '70dp',\r\n backgroundColor: '#eff3fa',\r\n showVerticalScrollIndicator: true,\r\n separatorColor: '#e3ece7', separatorInsets: { left: 0, right: 0 }\r\n});\r\n\r\nvar items = [];\r\nfor (var k=0;k<40;k++) {\r\n var label = 'Item ' + (k + 1);\r\n items.push({\r\n label: { text: label },\r\n value: { text: '' }\r\n });\r\n}\r\n\r\nvar sections = [\r\n Ti.UI.createListSection({\r\n headerTitle: 'Only Section',\r\n items: items\r\n })\r\n];\r\n\r\nlistView.setSections(sections);\r\n\r\nwin.add(listView);\r\nwin.open();\r\n{code}", "attachment": [], "flagged": false, "summary": "iOS - Incorrect Caching of ListItems containing Textfields", "creator": { "name": "jtoth", "key": "jtoth", "displayName": "Justin Toth", "active": true, "timeZone": "America/New_York" }, "subtasks": [], "reporter": { "name": "jtoth", "key": "jtoth", "displayName": "Justin Toth", "active": true, "timeZone": "America/New_York" }, "environment": "iOS, Ti SDK 3.5.1", "closedSprints": [ { "id": 362, "state": "closed", "name": "2015 Sprint 06 SDK", "startDate": "2015-03-14T13:56:53.868Z", "endDate": "2015-03-28T00:00:00.000Z", "completeDate": "2015-03-30T17:32:33.590Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "345747", "author": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Hi Justin,\r\n\r\nWhat you are seeing is the default behavior. To update the content of the textfield, you need to update the data. There's documentation here: http://docs.appcelerator.com/titanium/latest/#!/guide/ListViews\r\n\r\nFor example, I changed the template to make it more readable, value is now called textField. I also added an event to the textfield.\r\n{code:javascript}\r\nvar defaultTemplate = {\r\n childTemplates: [\r\n {\r\n type: 'Ti.UI.Label',\r\n bindId: 'label',\r\n properties: {\r\n top: '5dp', left: '5dp'\r\n }\r\n },\r\n {\r\n type: 'Ti.UI.TextField',\r\n bindId: 'textField',\r\n properties: {\r\n top: '30dp', left: '5dp', right: '5dp', bottom: '5dp',\r\n backgroundColor: 'gray', color: 'black', height: '40dp'\r\n },\r\n events: {\r\n \tblur: onTextfieldBlur\r\n }\r\n }\r\n ]\r\n};\r\n{code}\r\nThen declare the event function to be called, and there update the listview data:\r\n{code:javascript}\r\nfunction onTextfieldBlur(e) {\r\n var item = e.section.getItemAt(e.itemIndex);\r\n item.textField.value = e.value;\r\n e.section.updateItemAt(e.itemIndex, item); \r\n}\r\n{code}\r\n\r\nResolving, this is not a bug", "updateAuthor": { "name": "penrique", "key": "penrique", "displayName": "Pedro Enrique", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2015-03-11T22:40:18.000+0000", "updated": "2015-03-11T22:41:39.000+0000" }, { "id": "345755", "author": { "name": "jtoth", "key": "jtoth", "displayName": "Justin Toth", "active": true, "timeZone": "America/New_York" }, "body": "It seems like you didn't test your code, as it still has the same issue as in my original sample... The issue is that rows are cached and reused, simply updating the textfield's value manually on blur does nothing to address that issue. \r\n\r\nIt also leaves a nasty side effect where if you click from one textfield to another, the second one won't gain focus, as the section.updateItemAt will recreate the section and focus is lost.\r\n\r\nPlease reopen the ticket.\r\n\r\n{code}\r\nvar win = Ti.UI.createWindow({ backgroundColor: 'white' });\r\n\r\nvar defaultTemplate = {\r\n childTemplates: [\r\n {\r\n type: 'Ti.UI.Label',\r\n bindId: 'label',\r\n properties: {\r\n top: '5dp', left: '5dp'\r\n }\r\n },\r\n {\r\n type: 'Ti.UI.TextField',\r\n bindId: 'textField',\r\n properties: {\r\n top: '30dp', left: '5dp', right: '5dp', bottom: '5dp',\r\n backgroundColor: 'gray', color: 'black', height: '40dp'\r\n },\r\n events: {\r\n blur: onTextfieldBlur\r\n }\r\n }\r\n ]\r\n};\r\n\r\nvar listView = Ti.UI.createListView({\r\n top: '20dp',\r\n templates: { 'default': defaultTemplate },\r\n defaultItemTemplate: 'default',\r\n rowHeight: '70dp',\r\n backgroundColor: '#eff3fa',\r\n showVerticalScrollIndicator: true,\r\n separatorColor: '#e3ece7', separatorInsets: { left: 0, right: 0 }\r\n});\r\n\r\nvar items = [];\r\nfor (var k=0;k<40;k++) {\r\n var label = 'Item ' + (k + 1);\r\n items.push({\r\n label: { text: label },\r\n textField: { text: '' }\r\n });\r\n}\r\n\r\nvar sections = [\r\n Ti.UI.createListSection({\r\n headerTitle: 'Only Section',\r\n items: items\r\n })\r\n];\r\n\r\nlistView.setSections(sections);\r\n\r\nwin.add(listView);\r\nwin.open();\r\n\r\nfunction onTextfieldBlur(e) {\r\n var item = e.section.getItemAt(e.itemIndex);\r\n item.textField.value = e.value;\r\n e.section.updateItemAt(e.itemIndex, item);\r\n}\r\n{code}", "updateAuthor": { "name": "jtoth", "key": "jtoth", "displayName": "Justin Toth", "active": true, "timeZone": "America/New_York" }, "created": "2015-03-11T23:43:55.000+0000", "updated": "2015-03-11T23:44:11.000+0000" }, { "id": "345876", "author": { "name": "vduggal", "key": "vduggal", "displayName": "Vishal Duggal", "active": false, "timeZone": "America/Los_Angeles" }, "body": "A couple of issues with the sample code.\r\n\r\nOne the property to be used with the `textField` bindId is `value` not `text`.\r\nSecond the reason the keyboard disappears on selecting a second text field is because the updateItemAt does not specify an animated parameter which implicitly sets animated to false. In this scenario we have to reload the whole table.\r\n\r\nEither ways there is an issue with ListView's reuse of Proxies and underlying views. Will setup a PR to fix this.\r\nTest case\r\n{code}\r\nvar win = Ti.UI.createWindow({ backgroundColor: 'white' });\r\n\r\n \r\n\r\nvar defaultTemplate = {\r\n\r\n childTemplates: [\r\n\r\n {\r\n\r\n type: 'Ti.UI.Label',\r\n\r\n bindId: 'label',\r\n\r\n properties: {\r\n\r\n top: '5dp', left: '5dp'\r\n\r\n }\r\n\r\n },\r\n\r\n {\r\n\r\n type: 'Ti.UI.TextField',\r\n\r\n bindId: 'textField',\r\n\r\n properties: {\r\n\r\n top: '30dp', left: '5dp', right: '5dp', bottom: '5dp',\r\n\r\n backgroundColor: 'gray', color: 'black', height: '40dp'\r\n\r\n },\r\n\r\n events: {\r\n\r\n blur: onTextfieldBlur,\r\n\r\n }\r\n\r\n }\r\n\r\n ]\r\n\r\n};\r\n\r\n \r\n\r\nvar listView = Ti.UI.createListView({\r\n\r\n top: '20dp',\r\n\r\n templates: { 'default': defaultTemplate },\r\n\r\n defaultItemTemplate: 'default',\r\n\r\n rowHeight: '70dp',\r\n\r\n backgroundColor: '#eff3fa',\r\n\r\n showVerticalScrollIndicator: true,\r\n\r\n separatorColor: '#e3ece7', separatorInsets: { left: 0, right: 0 }\r\n\r\n});\r\n\r\n \r\n\r\nvar items = [];\r\n\r\nfor (var k=0;k<40;k++) {\r\n\r\n var label = 'Item ' + (k + 1);\r\n\r\n items.push({\r\n\r\n label: { text: label },\r\n\r\n textField: { value: '' }\r\n\r\n });\r\n\r\n}\r\n\r\n \r\n\r\nvar sections = [\r\n\r\n Ti.UI.createListSection({\r\n\r\n headerTitle: 'Only Section',\r\n\r\n items: items\r\n\r\n })\r\n\r\n];\r\n\r\n \r\n\r\nlistView.setSections(sections);\r\n\r\n \r\n\r\nwin.add(listView);\r\n\r\nwin.open();\r\n\r\n \r\n\r\nfunction onTextfieldBlur(e) {\r\n var item = e.section.getItemAt(e.itemIndex);\r\n\r\n item.textField.value = e.value;\r\n\r\n e.section.updateItemAt(e.itemIndex, item, {animated:true});\r\n\r\n}\r\n{code}", "updateAuthor": { "name": "vduggal", "key": "vduggal", "displayName": "Vishal Duggal", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2015-03-12T20:14:14.000+0000", "updated": "2015-03-12T20:14:14.000+0000" }, { "id": "345877", "author": { "name": "vduggal", "key": "vduggal", "displayName": "Vishal Duggal", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Pull pending\r\nmaster - https://github.com/appcelerator/titanium_mobile/pull/6718", "updateAuthor": { "name": "vduggal", "key": "vduggal", "displayName": "Vishal Duggal", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2015-03-12T20:24:36.000+0000", "updated": "2015-03-12T20:24:36.000+0000" }, { "id": "347657", "author": { "name": "jtoth", "key": "jtoth", "displayName": "Justin Toth", "active": true, "timeZone": "America/New_York" }, "body": "I've tested and confirmed that the row caching issue is resolved when using master and the two changes that Vishal suggested (value instead of text and animated: true). Thanks Pedro and Vishal for looking into the issue!", "updateAuthor": { "name": "jtoth", "key": "jtoth", "displayName": "Justin Toth", "active": true, "timeZone": "America/New_York" }, "created": "2015-03-29T14:50:13.000+0000", "updated": "2015-03-29T14:50:13.000+0000" }, { "id": "355108", "author": { "name": "ssekhri", "key": "ssekhri", "displayName": "Satyam Sekhri", "active": true, "timeZone": "America/Los_Angeles" }, "body": "The issue is fixed. No caching of list items.\r\nVerified using the code mentioned by Vishal.\r\n\r\nVerified on:\r\nMac OS 10.10.1\r\nTi SDK: 4.1.0.v20150614181235\r\nAppc Studio: 4.1.0.201506120823\r\nAppc CLI Registry: 4.0.2\r\nAppc CLI NPM: 4.0.1\r\nDevice: iPhone 6plus (v8.3), iPhone 5S simulator (v7.1)", "updateAuthor": { "name": "ssekhri", "key": "ssekhri", "displayName": "Satyam Sekhri", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2015-06-15T08:31:41.000+0000", "updated": "2015-06-15T08:31:41.000+0000" } ], "maxResults": 7, "total": 7, "startAt": 0 } } }