Javascript: model.createRows function?

Mark LMark L ✭✭✭✭
edited June 29, 2020 in Ideas
As far as I'm aware, there is a very helpful updateRows function that lets you update many rows at once in a model preventing screen rendering lag issues and in general is much more efficient than running through a loop of individual updateRow calls. Also as I understand it this more easily allows you to manage potentially asynchronous operations better by knowing when your update on all rows is complete ($when.done for updateRows runs after updating all rows, whereas $when.done for updateRow just happens on that individual row and it's more difficult to figure out when you've completed updating all rows).

There doesn't appear to be an equivalent createRows function, forcing you to run createRow in a loop which usually isn't a problem but in some cases, particularly where a table for a given model has already (at some point) been rendered on the screen and you're running through a loop of createRow, it can cause an incredible amount of lag / freezing of your page.

Is there indeed not an equivalent createRows function (similar to updateRows) that I'm missing? If not I'd highly recommend this as a very useful function to add to the API.

Thanks!


Possible documentation for createRows:

createRows(inserts[, options])

Creates the given rows and fields, then notifies all registered UI elements that changes took place.

Changes made will not be sent to the server until the save( ) method is called.

Arguments:
  • inserts (object[]) – An array of row objects. Each row object is a map of field names to field values.
  • options (object) –

    OPTIONAL. A simple object map of additional settings to pass in to this method call. Supported options include:

    • doAppend (boolean): Optional. By default, createRows() will prepend created rows to the Model’s existing data rows. if doAppend is set to true, however, then the rows will be appended to the end of the model’s data rows.
    • editModeForNewItems (boolean): Optional, defaults to false. If true, then any skuid.ui.Items created in response to the creation of new rows will be placed into edit mode initially, rather than read mode.
Returns:

The created row objects.

Return type:

object

EXAMPLE:

Create new rows specifying their values in myModel:
var rowsToCreate = [];
for(i=0;i<100;i++){
    rowsToCreate.push({Name: 'New Name 1', CustomField__c: 'New Value 1'});
}
myModel.createRows( rowsToCreate );


1
1 votes

Awaiting Review · Last Updated

Comments

  • Mark LMark L ✭✭✭✭
    edited June 29, 2020
    So I did some experimenting and discovered something pretty amazing.

    I had previously assumed that model.adoptRows() required you to pass it rows from another model of the same object type, but it turns out that adoptRows actually just takes an array of field value pairs, so you can use adoptRows to effectively operate like "createRows".

    In other words, this actually works:

    var rowsToCreate = [];
    for(i=0;i<100;i++){
        rowsToCreate.push({Name: 'New Name 1', CustomField__c: 'New Value 1'});
    }
    myModel.adoptRows( rowsToCreate );

    This is great because it allows creation of new rows in a model all at once without invoking a rendering update with each individual row inserted. Much more efficient, and prevents page freezes.
  • Arne-Per HeurbergArne-Per Heurberg ✭✭✭✭
    edited April 8, 2020
    That is awesome
  • Mark LMark L ✭✭✭✭
    edited April 8, 2020
    One thing to note on this:

    If you're creating brand new rows (they don't have Salesforce IDs) the rows will get created with SKUID temporary IDs as expected, but you will not be able to save those rows to the database (gives Invalid ID error on save).

    Quiet unfortunate. Not sure why this happens this way. Haven't figured out a workaround for this yet. If anyone has suggestions that would be very helpful.

    For UI only models or models you don't intend to save to the database this works, it's just saving that is the issue.

    Thanks!
  • Arne-Per HeurbergArne-Per Heurberg ✭✭✭✭
    edited May 1, 2020
    Hope you are doing well Mark. I was messing around with other adoptRows issues but decided to check on the basics. Remembered you had success with this. It will add row but doesn't assign a temp Skuid Id for me. WOuld you mind confirming that this still works for you. Cheers and thanks!
    AP

  • Mark LMark L ✭✭✭✭
    edited May 1, 2020
    When trying to create new rows via the adoptRows function using manually created 'row objects', I've discovered they do get a temp SKUID ID (it's hidden but it's there), but they cannot save to the database because SKUID flags these with some hidden metadata that treats the rows as if they already exist in the database so when you try to save the row it treats it as an update rather than an insert.

    I've found that it is possible to trick SKUID into thinking it's an insert rather than an update, but the workaround to do this is very messy and trying to hack some deeper level SKUID metadata / functions so I don't necessarily recommend it.

    This also happens when you try to create a row in a model for an object that already exists in the database. You can create a row with that item's Salesforce ID but you need to trick SKUID into thinking it needs to be an update rather than an insert because despite specifying the Salesforce ID on create, SKUID still thinks it needs to insert the row rather than update it.

    For example, you have a model that has a lookup relationship to another object and you want to make updates to the related object; you can have a model for that related object specifically to do updates, but normally you'd need to query the model to get the row to update. Instead, we can just create a row with the ID that we know from the lookup relationship in the other model using createRow, but createRow inherently treats that row as a new row to be inserted even though we specified a pre-existing Salesforce ID on the row, so we have to trick SKUID into thinking it is a row to update rather than a row to insert.

    Here's an example of tricking SKUID to do an update rather than an insert on create row that I've tested and is working in APIv1 and SKUID 12.1.7 .. you may need to have 'process model client side' checked for this to work, not sure:

    // Trick SKUID into thinking the created row should be updated rather than inserted // (which is actually the appropriate behavior in this instance)  // Add to model let ourModel = skuid.$M('OurModel'); // Salesforce ID for Item we want to update from a lookup relationship, // but we don't want to waste time / resources querying that item let lookupId = 'SalesforceID From Lookup Relationship In Other Model'; ourModel.createRow({doAppend: true, additionalConditions: [     {field: 'Id', value: lookupId},     {field: 'OtherFieldToUpdate__c', value: 'UpdateValue'} ]}); // Store temporary ID of the row that got created for updating the row in the map of the model let tempId = ourModel.data[ourModel.data.length-1].Id; // Overwrite the tempID with the Salesforce ID ourModel.data[ourModel.data.length-1].Id = lookupId; // Overwrite the metadata ID with the salesforce ID ourModel.data[ourModel.data.length-1]['__skuid_record__']['__id'] = lookupId; // Overwrite the flag for this being a new row to false so we force an update rather than an insert ourModel.data[ourModel.data.length-1]['__skuid_record__']['__new'] = false; // Fix the maps to mirror our changes in the array let tempRow = ourModel.dataMap[tempId]; // Set the map to key on Salesforce ID rather than tempID and delete the row at tempId ourModel.dataMap[lookupId] = tempRow; delete ourModel.dataMap[tempId]; tempRow = ourModel.recordsMap[tempId]; // Also need to update records map with SF ID ourModel.recordsMap[lookupId] = tempRow; delete ourModel.recordsMap[tempId]; // Also need to update the changes map with SF ID tempRow = ourModel.changes[tempId]; ourModel.changes[lookupId] = tempRow; delete ourModel.changes[tempId];

    To hack SKUID to work the other way around (do an insert where it thinks it needs to do an update) I'd imagine it's similar to above except tweaking it to trick SKUID into thinking it's an insert rather than an update. I don't have a direct tested example of that working just yet. I'll need to do some experimenting to put that together, but I imagine it's sort of like the code above just switching certain aspects around so it forces an insert instead of an update. Maybe it's as simple as setting the __new flag to true? Need to test it out..
  • Arne-Per HeurbergArne-Per Heurberg ✭✭✭✭
    edited May 1, 2020
    Thanks mark. I will give this a try. I wasn't able to discover the hidden temp-Id with adopt. We will see that the crew says. I am using v2 so, maybe I will try v1 and compare. (Table construction is totally different so maybe there are similar differences with the db interactions). Thanks again and I will ping after I give this a run.
  • Arne-Per HeurbergArne-Per Heurberg ✭✭✭✭
    edited May 2, 2020
    Hi Mark,  I dug into it. I see what you are doing in there. with __id and when it is registered as __new: true. I am not sure you will have access to do that in v2 at least I wasn't able to in the dev tools. there is a change in how __skuid record__ is served up (eg. Symbol obfuscating (Record, Id etc).
    Thanks again for the detailed insight. lmk if I am just missing something obvious!
  • Mark LMark L ✭✭✭✭
    edited May 2, 2020
    Hi Arne,

    I did some more testing to discover that my previous statement about it creating a temp ID is incorrect. It creates what appears to be a temp ID as a key in the dataMap, but this appears to function differently than an actual tempId that a createRow function would make. It would likely require a lot of hacking / trickery to make an adoptRows sort of hack to make a createRows function operate properly.

    Since that will take a lot of time to try and figure out.. in the meantime I've reworked and created simpler functions for creating pre-existing rows to update in a model (the example I showed in a previous reply)

    // skuid.custom.createUpdateExistingRow(ourModel,row)
    //  Create a row for an already existing Salesforce object
    //  createRow will normally assume the newly created row needs to be inserted, but if this is an existing row
    //  we will instead be updating the row when saving the model
    //  row is an object of field value pairs and must include a salesforce Id field
    skuid.custom.createUpdateExistingRow = function (model, row) {
        if (row === undefined) {
            return false;
        }
        if (row.Id === undefined) {
            return false;
        }
        const retRows = model.adoptRows([{ Id: row.Id }], { doAppend: true });
        let retRow;
        if (retRows !== undefined && retRows.length > 0) {
            retRow = retRows[0];
        }
        const upd = row;
        delete upd.Id;
        if (upd !== {}) {
            model.updateRow(model.data[model.data.length - 1], upd);
        }
        return retRow;
    }
    // skuid.custom.createUpdateExistingRows(ourModel,rows)
    //  Create rows for an already existing Salesforce objects
    //  createRow will normally assume the newly created row needs to be inserted, but if this is an existing row
    //  we will instead be updating the row when saving the model
    //  rows is an array of objects of field value pairs and must include a salesforce Id field
    skuid.custom.createUpdateExistingRows = function (model, rows) {
        if (rows === undefined) {
            return false;
        }
        
        // Construct our adoptrows and updaterows array / object
        const rowsIds = [];
        const rowsUpdates = {};
        for (let i = 0; i < rows.length; i++) {
            const row = rows[i];
            if (row.Id === undefined) {
                return false;
            }
            const id = row.Id;
            rowsIds.push({ Id: id });
            delete row.Id;
            rowsUpdates[id] = row;
        }
        let retRows = model.adoptRows(rowsIds, { doAppend: true });
        if (retRows === undefined) {
            return false;
        }
        retRows = model.updateRows(rowsUpdates);
        return retRows;
    }


Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!