Re-inserting a Record into an extJS Store

14,722

Solution 1

Looks to me like it's not constructing the tce object correctly. This line is where you should set your breakpoint:

 tce = new timecardEntry(currentRecord.data);

It seems like it's successfully constructing a timecardEntry that somehow isn't a proper record. Having a poke at just what it is constructing may help.

If it's not apparent from poking at it that way why it's up the spout, try doing it like this, like @timdev suggests:

var store = grid.store,
  currentRecord = store.getAt(rowIndex),
  tce;
tce = currentRecord.copy();
tce.set('start_time', new Date().format('Y-m-d H:i:s'));

if (tce) {
  store.add(tce);
}

(You should be able to call grid.store.add(tce) instead of insert as you're inserting at the end.)

Solution 2

Really well-written question. Good bit of relevant code, and nice explanation of what you're stuck on. Unfortunately, I don't see anything that stands out.

Your script looks mostly right. You're close to being where you need to be.

Below is an answer that I just typed out, and then reread your question (and code), and thought a little better of. You probably know this stuff already, but it's here for anyone else. It's also relevant, because I don't see any errors in the relevant part of what you did, so you probably blew it somewhere else. The questions are: where and how?

Hopefully someone less exhausted than i am will come along and find the obvious problem, in the meantime, here's my scrawl about how to debug in Ext and why:


You've left something important out, or overlooked it. That error message you mentioned: Uncaught TypeError: Cannot read property 'data' of undefined -- where is that happening? It looks like it's not happening in the code you posted, it might very well be happening down in the bowels of ExtJS.

So, fire up FireBug, and turn on the "break on error" feature. Make your error happen, and start looking at the "stack" pane over on the right (usually). The stack will show you how you got to where you are.

Unless I'm missing something (and since I'm just running your code in my head, I very well may be), there's probably something misconfigured elsewhere that's causing your bug.

But as with any program (and especially with ExtJS, in my experience), the debugger is your friend.

Do this:

  • Use the -debug version of ext-base.js and ext-all.js (until it all works)
  • Use firebug, and "break on errors"
  • Learn to use the debugger to step through code, and to watch the data you're operating on.
  • Don't give up when you find yourself deep in the bowels of ExtJS. If you try, you'll start to get a sense of WTF is going on, and even if you don't understand it all, it will start to give you hints about where you screwed up.
Share:
14,722
Levi Hackwith
Author by

Levi Hackwith

Web developer. Github: http://www.github.com/opnsrce

Updated on July 26, 2022

Comments

  • Levi Hackwith
    Levi Hackwith almost 2 years

    The code

    Ext.onReady(
        function() {
             Ext.QuickTips.init();
             Ext.namespace('TimeTracker');
             TimeTracker.dataStore = new Ext.data.JsonStore(
                {
                    root: 'timecardEntries',
                    url: 'php/scripts/timecardEntry.script.php',
                    storeId: 'timesheet',
                    autoLoad: true,
                    autoSave: true,
                    writer: new Ext.data.JsonWriter(
                        {
                          encode: true
                        }
                    ),
                    fields: [
                        {name: 'id', type: 'integer'},
                        {name: 'user_id', type: 'integer'},
                        {name: 'ticket_id', type: 'integer'},
                        {name: 'description', type: 'string'},
                        {name: 'start_time', type: 'date', dateFormat: 'Y-m-d H:i:s'},
                        {name: 'stop_time', type: 'date', dateFormat: 'Y-m-d H:i:s'},
                        {name: 'client_id', type: 'integer'},
                        {name: 'is_billable', type: 'integer'}
                    ]
                }
            );
            TimeTracker.timeEntryGrid = new Ext.grid.EditorGridPanel(
                {
                    renderTo: Ext.getBody(),
                    store: TimeTracker.dataStore,
                    autoFit: true,
                    height: 500,
                    title: 'Timesheet Entries',
                    tbar: [
                        {
                            xtype: 'button',
                            text: 'Add Record',
                            iconCls: 'silk-add',
                            handler: function() {
                                var timecardEntry = TimeTracker.timeEntryGrid.getStore().recordType;
                                var tce = new timecardEntry(
                                    {
                                        description: 'New Timesheet Entry',
                                        start_time: new Date().format('m/d/Y H:i:s'),
                                        is_billable: 0
                                    }
                                )
                                    TimeTracker.timeEntryGrid.stopEditing();
                                var newRow = TimeTracker.dataStore.getCount();
                                TimeTracker.dataStore.insert(newRow, tce);
                                TimeTracker.timeEntryGrid.startEditing(newRow, 0);
                            }
                        }
                    ],
                    view: new Ext.grid.GridView(
                        {
                            autoFill: true
                        }
                    ),
                    colModel: new Ext.grid.ColumnModel(
                        {
                            defaults: {
                                sortable: true,
                                editable: true
                            },
                            columns: [
                                {
                                    id: 'ticket_number',
                                    header: 'Ticket #',
                                    dataIndex: 'ticket_number',
                                    editor: new Ext.form.TextField({allowBlank: true}),
                                    renderer: function(value) {
                                        return (!value) ? 'N/A' : value;
                                    }
                                },
                                {
                                    id: 'description',
                                    header: 'Description',
                                    dataIndex: 'description',
                                    editor: new Ext.form.TextField({allowBlank: false})
                                },
                                {
                                    id: 'start_time',
                                    header: 'Start',
                                    dataIndex: 'start_time',
                                    renderer: Ext.util.Format.dateRenderer('m/d/Y h:i A'),
                                    editor: new Ext.form.DateField({allowBlank: false})
                                },
                                {
                                    id: 'stop_time',
                                    header: 'Stop',
                                    dataIndex: 'stop_time',
                                    renderer: Ext.util.Format.dateRenderer('m/d/Y h:i A'),
                                    editor: new Ext.form.DateField({allowBlank: false})
                                },
                                {
                                    id: 'client',
                                    header: 'Client',
                                    dataIndex: 'client_id',
                                    renderer: function(value) {
                                        return (!value) ? 'N/A' : value;
                                    }
                                },
                                {
                                    id: 'billable',
                                    header: 'Billable',
                                    dataIndex: 'is_billable',
                                    renderer: function(value) {
                                        return (!value) ? 'No' : 'Yes';
                                    }                     
                                },
                                {
                                    id: 'actions',
                                    header: null,
    
                                    xtype: 'actioncolumn',
                                    items: [
                                       {
                                           icon: 'assets/images/silk_icons/page_copy.png',
                                           iconCls: 'action_icon',
                                           handler: function(grid, rowIndex, columnIndex) {
                                                // THE PROBLEM STARTS HERE
                                                grid.stopEditing();
                                                var newRow = TimeTracker.dataStore.getCount();
                                                recordClone = grid.store.getAt(rowIndex);
                                                recordClone.data.start_time = new Date().format('Y-m-d H:i:s');
                                                grid.store.insert(newRow, recordClone);
                                                grid.startEditing(newRow, 0);
                                           }
                                       },
                                       {
                                           icon: 'assets/images/silk_icons/page_delete.png',
                                           handler: function(grid, rowIndex, columnIndex) {
                                               alert('called');
                                           }
                                       }
                                    ]
                                }
                            ]
                        }
                    )
                }
            );
        }
    );
    

    The Goal

    When the user clicks the 'copy' button, that store record is stored into memory, its 'start_time' is set to the current date and time, and it is re-inserted into the store as a new record

    The Current Result

    I receive the following JS error: Uncaught TypeError: Cannot read property 'data' of undefined

    My Question(s)

    For starters, I'm not even sure if I'm grabbing the currently selected row's data record properly. Second, I have no idea what the error message I'm getting means.

    Any help is, as always, highly appreciated.

    Thanks.

    Update 1

    After some tweaking, here's what I came up with (this modified code for the copy button handler)

                        {
                            id: 'actions',
                            header: null,
    
                            xtype: 'actioncolumn',
                            items: [
                           {
                                   icon: 'assets/images/silk_icons/page_copy.png',
                                   iconCls: 'action_icon',
                                   handler: function(grid, rowIndex, columnIndex) {
                                        grid.stopEditing();
                                        var newRow = TimeTracker.dataStore.getCount();
                                        var currentRecord = grid.store.getAt(rowIndex);
                                        var timecardEntry = grid.store.recordType;
                                        tce = new timecardEntry(currentRecord.data);
                                        tce.data.start_time = new Date().format('Y-m-d H:i:s');
                                        grid.store.insert(newRow, tce);
                                   }
                               },
                               {
                                   icon: 'assets/images/silk_icons/page_delete.png',
                                   handler: function(grid, rowIndex, columnIndex) {
                                       alert('called');
                                   }
                               }
                            ]
                        }
    

    Here's what I'm doing:

    1. Stop editing the grid
    2. Get the number of records currently in the store
    3. Grab the currently selected record and store it in memory
    4. Grab the record type from the store
    5. Make a new instance of the store record type, and pass in the data object from the selected record. The data object is equivalent to the object literal if you were making a new record by hand (see my original 'add button' code for details)
    6. Alter the start_time value of the new object that was created to today's date and time
    7. Insert record into grid
    8. Happy time!

    Please critique this code and let me know if there's a better way to do it. Thanks!

    Update 2:

                                   handler: function(grid, rowIndex, columnIndex) {
                                        grid.stopEditing();
                                        var recordClone = grid.store.getAt(rowIndex).copy();
                                        Ext.data.Record.id(recordClone);
                                        if(recordClone) {
                                            grid.store.add(recordClone);
                                        }
                                   }
    

    I updated the code to use the copy and add methods and it does work. However, when I call the add() method I get a 'e is undefined error' but when I refresh the page, the record is inserted despite the error message. Ideas?

  • Levi Hackwith
    Levi Hackwith over 13 years
    @timdev, thanks for the answer. Here's the exact line that causes it to crash: grid.store.insert(newRow, recordClone); I've firebugged the hell of out of this thing and the only thing I've learned is that the error is occurring deep within extJS.
  • timdev
    timdev over 13 years
    It's probably because you're not properly cloning your record. Try changing recordClone = grid.store.getAt(rowIndex); to this: recordClone = grid.store.getAt(rowIndex).copy(). Possibly followed by Ext.data.Record.id(recordClone);
  • Drasill
    Drasill over 13 years
    I was going to say this, indeed, you're not cloning the record.
  • Levi Hackwith
    Levi Hackwith over 13 years
    @timdev and @Drasill, I posted some updated code. Take a look. Thanks!
  • timdev
    timdev over 13 years
    @Levi Hackith - You're probably still having the same problem as before. What if you try setting the record with Ext.data.Record.id(tce) before trying to insert() it into the store? I'm not exactly sure of the internal details of the Record() constructor -- and I'm not sure why you don't just copy() the record like I suggested yesterday -- this is exactly the use case that copy() was deisgned for.
  • Levi Hackwith
    Levi Hackwith over 13 years
    @timdev, sorry for the confusion. The updated code I posted actually works. However, I'll give your solution a try and see what happens.