locked
table with editable textboxes to insert new child records. HTML client RRS feed

  • Question

  • This may be obvious, but I want to make a screen table control to add multiple new child records of each record in a parent entity set. Users will enter many records and having them bounce in and out of screens for each item individually is a huge dissatisfier.

    This is to collect metric values (widget counts). Entity design is simple 1:m where MET_DEF (metric definition ie. name, active flag etc.) is the entity set of metric names and the child is MET_DET (metric details ie. date, value, comments)

    I add the entity to a new Browse screen, add the table to screen that displays the entity set names (MET_NM) and I add local properties for new date and values. I also add a toggle to indicate whether to add or skip the row.

    If you see below, the problem is prevalent. The local properties (date picker, textbox and toggle) are all screen level properties where the value applies the same for all rows. I type in '1' in the textbox of 'Metric Name 1', and it gets applied to all rows.

    I was going to add the records dynamically via a loop and addnew(). But I'm stuck. The number of rows varies too. So it may be 5..it may be 10..etc.

    Any ideas? Is my approach wrong from the start? I don't have a budget for 3rd party apps. Worst case I have to make a whole aspx page to get grid controls.

    Thanks!

    Thursday, February 16, 2017 4:19 AM

Answers

  • Figured it out. Needed to provide a unique PK. Since I'm passing to Oracle and looking to change the default value of null or 0 to the next sequence number...but it would not take ('per the error above'). Changed my table constraint to test for negative numbers in order to keep unique parameters to pass and not prevent passing an existing value.

    So to my OP, in order to create the table for inserting multiple records.

    1. Hi-jacked a table entity, added custom controls
    2. set custom control innerHTML to input boxes
    3. Parse the DOM to obtain the values
    4. Load values to an array
    5. Launch promises for related entity foreign keys
    6. Upon success, test if more promises to come
    7. Wait until all promises are in
    8. Load .addNew() and associated values to a new array
    9. saveChanges()

    Execute Code:

    myapp.ViewTSA_MET_MEA.QuickAdd_execute = function (screen) {
        var promisesToRun = 0;
        var promisesLeft = 0;
        var entries = 0;
        var newEntries = [];
        
        var recordSet = [];
        var $tblPrnt = $('#inputRows').find("tr");
        $.each($tblPrnt, function (e) {
            if (this.cells['4'].children['1'].children['checkbox-0'].checked == true) {
                var recordDetails = [];
                recordDetails.push(parseInt(this.cells['0'].children['1'].innerText)); // reference pk
                recordDetails.push(this.cells['2'].children['1'].children['0'].value); // entered date
                recordDetails.push(this.cells['3'].children['1'].children['0'].value); // entered metric value
                recordSet[promisesToRun] = recordDetails;
                ++promisesToRun;
            }
        })
        promisesLeft = promisesToRun;
        for (i = 0; i < promisesToRun; i++) {
            var filter_PK = "(MET_DEF_PK eq " + msls._toODataString(recordSet[i][0], ":Int32") + ")";
            var Definition = screen.details.dataWorkspace.myDb.TSA_MET_DEFs.filter(filter_PK).execute().then(function (results) {
                var QueriedDefinition = results.results[0]; 
                if (QueriedDefinition != undefined && QueriedDefinition != null) {
                    for (x = 0; x < recordSet.length; x++) {
                        if (recordSet[x][0] == QueriedDefinition.details._.MET_DEF_PK) {
                            recordSet[x][3] = QueriedDefinition;
                            --promisesLeft
                            break;
                        }
                    }
                }
                if (promisesLeft == 0) {
                    var newRecords = [];
                    for (z = 0; z != recordSet.length; z++) {
                        newRecords[z] = screen.details.dataWorkspace.myDb.TSA_MET_DETs.addNew();
                        newRecords[z].MET_DET_PK = -z-1;
                        newRecords[z].setTSA_MET_DEF(recordSet[z][3]);
                        newRecords[z].MET_DT = new Date(recordSet[z][1]);
                        newRecords[z].MET_VAL = parseInt(recordSet[z][2]);
                        var tstVal = recordSet[z][2];
                        var re = new RegExp("^[+-]{0,1}[0-9,]{0,99}[\.]{0,1}(?:[0-9]{0,99})$");
                        if (re.test(tstVal)) { 
                        } else {
                            msls.showMessageBox("A non-numeric value was detected.", { title: "Invalid Submission" });
                        }
                    }
                    screen.details.dataWorkspace.myDb.saveChanges().then(function success() {
                        // If success.
                        msls.showMessageBox(newRecords.length + ' Metrics Added', {
                            title: "Save Successful",
                            buttons: msls.MessageBoxButtons.ok
                        }).then(function (result) {
                            window.location.reload();
                        });
                        
                    }, function fail(e) {
                        // If error occurs,
                        msls.showMessageBox(e[0].message, { title: e.title }).then(function () {
                            // Cancel Changes
                            myapp.cancelChanges();
                        });
                    });
                }
            });
        }
    };


    • Marked as answer by FaithNoMore Saturday, February 25, 2017 7:44 AM
    • Edited by FaithNoMore Saturday, February 25, 2017 7:45 AM
    Saturday, February 25, 2017 7:43 AM

All replies

  • Hey FNM, I'd like trying to help but...sorry, not able to understand question point and design logic.

    I'm understanding that DEF/DET (DEFINITIONS and DETAILS) are respectively Master and Child?

    • "I add the entity to a new Browse screen" which entity? (btw you are showing an edit screen)
    • If you're trying to add/edit DETAILS inline in Table, why row contains a DEFINITION pk column?
    • Users will create DEFINITIONS or will they have to only add DETAILS to already existing DEFINITIONS?
    • Not clear what you mean with "toggle to indicate whether to add or skip the row" (If you see a row in a table its data is already there, how to skip it?)
    • Why your local properties (date and value) are inside a table row? They are obviously unique values so what are you trying to achieve exactly, setting them on all rows?
    • Where (and when) did you try to dynamically add DETAILS rows? Server side (say in DEFINITION_Inserting) or client side?

    For a plain Master-Detail input I'd use a DEFINITION add/edit screen with a DETAILS table and a button to add new rows (or some javascript automatic logic or server side while inserting Master):

    Button code to add new child row:

    myapp.AddEditMetric.AddDetail_execute = function (screen) {
        var newDetail = new myapp.Detail;
        //Set associated (master) entity
        newDetail.setMetric(screen.Metric);
    };


    Marco

    Sunday, February 19, 2017 9:56 AM
  • Hi Marco, your follow up questions are valid as my OP has observably confusing details. Let me clarify the challenge as I have made some advancements.

    LS is great for entering one record at a time. I have users entering in the 'Metric Details' regularly. But they have to do so through multiple screen navigations (select definition, addEdit details, save, select next definition, rinse wash repeat).

    They want to have a popup that lists all the MET_DEFs, and have a textbox to add new MET_DET values for each MET_DEF in one shot. Poping up an addedit screen for each definition is not what they want.

    The route this rabbit hole is taking me is to

    1. highjack the selected MET_DEFs collection, make a table object
    2. ADD custom controls in new columns with rendered textboxes for values and a checkbox (bool to skip the row or not; per your question) via element.innerHTML() (This is were I was stuck in the OP)
    3. I also added AnyTime_picker.js to a textbox for date selection (not shown in pic) since recreating the LS one looked like a nightmare.

    Users now have the MET_DEFs listed with textboxes for value entry (second image below).

    The next step is to write a js procedure to loop the row items and programmatically add the records to the dataworkspace with the values in the custom controls. The pictures below should have all my craziness make sense.

    As you can see, I have essentially taken the queried collection of metric defs, and layered addedit objects to take new values. But now I need to get the row objects of the table. This is where I am stuck. I think I should be able to get the values from each rows objects. Once I trace these down, I'm home free!

    Hopefully now that the goal is visualized, it makes sense and more importantly, I didn't take a flawed approach.

    Thanks!!

    myapp.ViewTSA_MET_MEA.QuickAdd_execute = function (screen) {
        //var newItems = screen.TSA_MET_DEFs.count;
    
        screen.TSA_MET_DEFs.data.forEach(function (dataRow) {
            if (dataRow[4].value == true) { //<<<How to get selected row? dataRow is 'undefined';
                var newMetric = screen.details.dataWorkspace.TSAWRKFLWDEVDB.TSA_MET_DETs.addNew();
                newMetric.TSA_MET_DEF.MET_DEF_PK = dataRow[0].value; 
                newMetric.MET_DT = dataRow[2].value;
                newMetric.MET_VAL = dataRow[3].value;
    
                myapp.saveChanges().then(function success() {
                    // If success.
                }, function fail(e) {
                    // If error occurs,
                    msls.showMessageBox(e.message, { title: e.title }).then(function () {
                        // Cancel Changes
                        myapp.cancelChanges();
                    });
                });
            }
        });
    };

    Monday, February 20, 2017 5:44 AM
  • I'm still missing something anyway...are you sure dataRow is 'undefined'?
    With TSA_MET_DEFs.data.forEach loop you are parsing one by one every MET_DEF in visual collection (non in table).
    If you are inside forEach I can't understand how it could be undefined, but keep in mind that dataRow would be a MET_DEF entity without any reference to your table's custom controls, and not an array so dataRow[n] will never be valid.

    I don't have examples at hand right now but I think you should want to inspect DOM table elements instead of MET_DEF visual collection items.
    Assuming you will be able to parse table elements to build your MET_DET entities I suggest to avoid executing myapp.SaveChanges for each one, go for building an array of entities and saving once at the end.

    Marco

    Monday, February 20, 2017 7:54 PM
  • Hi Marco,

    I'M SOOOOO CLOSE!!! But there is an error on save. I get the following message when I try to save more than one record. It works fine with one, but multiple it doesn't. Is there something I am missing in the array save method?

    "The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges."

    I haven't been able to figure out what is wrong. I have the array built containing two unique records.

    var newRecords = [];
    for (z = 0; z != recordSet.length; z++) {
        newRecords[z] = screen.details.dataWorkspace.myDb.TSA_MET_DETs.addNew();
        newRecords[z].MET_DET_PK = 0;
        newRecords[z].setTSA_MET_DEF(recordSet[z][3]);
        newRecords[z].MET_DT = new Date(recordSet[z][1]);
        newRecords[z].MET_VAL = recordSet[z][2];
        screen.details.dataWorkspace.myDb.saveChanges().then(function success() {
            // If success.
            msls.showMessageBox('Success!!!', { title: "success" });
        }, function fail(e) {
            // If error occurs,
            msls.showMessageBox(e[0].message, { title: e.title }).then(function () {
                // Cancel Changes
                myapp.cancelChanges();
            });
        });
    }



    • Edited by FaithNoMore Friday, February 24, 2017 9:57 PM
    Thursday, February 23, 2017 11:11 PM
  • Figured it out. Needed to provide a unique PK. Since I'm passing to Oracle and looking to change the default value of null or 0 to the next sequence number...but it would not take ('per the error above'). Changed my table constraint to test for negative numbers in order to keep unique parameters to pass and not prevent passing an existing value.

    So to my OP, in order to create the table for inserting multiple records.

    1. Hi-jacked a table entity, added custom controls
    2. set custom control innerHTML to input boxes
    3. Parse the DOM to obtain the values
    4. Load values to an array
    5. Launch promises for related entity foreign keys
    6. Upon success, test if more promises to come
    7. Wait until all promises are in
    8. Load .addNew() and associated values to a new array
    9. saveChanges()

    Execute Code:

    myapp.ViewTSA_MET_MEA.QuickAdd_execute = function (screen) {
        var promisesToRun = 0;
        var promisesLeft = 0;
        var entries = 0;
        var newEntries = [];
        
        var recordSet = [];
        var $tblPrnt = $('#inputRows').find("tr");
        $.each($tblPrnt, function (e) {
            if (this.cells['4'].children['1'].children['checkbox-0'].checked == true) {
                var recordDetails = [];
                recordDetails.push(parseInt(this.cells['0'].children['1'].innerText)); // reference pk
                recordDetails.push(this.cells['2'].children['1'].children['0'].value); // entered date
                recordDetails.push(this.cells['3'].children['1'].children['0'].value); // entered metric value
                recordSet[promisesToRun] = recordDetails;
                ++promisesToRun;
            }
        })
        promisesLeft = promisesToRun;
        for (i = 0; i < promisesToRun; i++) {
            var filter_PK = "(MET_DEF_PK eq " + msls._toODataString(recordSet[i][0], ":Int32") + ")";
            var Definition = screen.details.dataWorkspace.myDb.TSA_MET_DEFs.filter(filter_PK).execute().then(function (results) {
                var QueriedDefinition = results.results[0]; 
                if (QueriedDefinition != undefined && QueriedDefinition != null) {
                    for (x = 0; x < recordSet.length; x++) {
                        if (recordSet[x][0] == QueriedDefinition.details._.MET_DEF_PK) {
                            recordSet[x][3] = QueriedDefinition;
                            --promisesLeft
                            break;
                        }
                    }
                }
                if (promisesLeft == 0) {
                    var newRecords = [];
                    for (z = 0; z != recordSet.length; z++) {
                        newRecords[z] = screen.details.dataWorkspace.myDb.TSA_MET_DETs.addNew();
                        newRecords[z].MET_DET_PK = -z-1;
                        newRecords[z].setTSA_MET_DEF(recordSet[z][3]);
                        newRecords[z].MET_DT = new Date(recordSet[z][1]);
                        newRecords[z].MET_VAL = parseInt(recordSet[z][2]);
                        var tstVal = recordSet[z][2];
                        var re = new RegExp("^[+-]{0,1}[0-9,]{0,99}[\.]{0,1}(?:[0-9]{0,99})$");
                        if (re.test(tstVal)) { 
                        } else {
                            msls.showMessageBox("A non-numeric value was detected.", { title: "Invalid Submission" });
                        }
                    }
                    screen.details.dataWorkspace.myDb.saveChanges().then(function success() {
                        // If success.
                        msls.showMessageBox(newRecords.length + ' Metrics Added', {
                            title: "Save Successful",
                            buttons: msls.MessageBoxButtons.ok
                        }).then(function (result) {
                            window.location.reload();
                        });
                        
                    }, function fail(e) {
                        // If error occurs,
                        msls.showMessageBox(e[0].message, { title: e.title }).then(function () {
                            // Cancel Changes
                            myapp.cancelChanges();
                        });
                    });
                }
            });
        }
    };


    • Marked as answer by FaithNoMore Saturday, February 25, 2017 7:44 AM
    • Edited by FaithNoMore Saturday, February 25, 2017 7:45 AM
    Saturday, February 25, 2017 7:43 AM