Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Proper technique for re-loading and displaying client only data in a grid.

    Support (Feature Explorer and Safari Version 9.0.1 (11601.2.7.2) ),

    I need a little guidance on the proper technique for (re-)loading new data in an existing grid.

    The following is my observation illustrated in my attached feature explorer example:

    1. I am using a clientOnly datasource and calling setCacheData.

    2. I am calling fetchData on the grid.

    3. It works.

    4. If I repeat steps 1 and 2, it does not work.

    Therefore, I tried subsequent loads using invalidate instead of fetchData, which seems to work, but may not be proper use.

    Questions:

    1. Is the loading technique different when loading the first time versus subsequent times?

    2. What is the correct use of the framework on this topic?

    3. In my test case, it gets worse if a filter criteria is present. What is the correct use when criteria may or may not be present?

    4. In my example, dataArrived seems to not be called for the initial load and therefore, the tree does not expand. What is the correct technique for always expanding?

    Thank you in advance,
    Mitch.

    PASTE THIS IN FEATURE EXPLORER
    ==============================[/FONT]

    var myData1 = [
    { EmployeeId:"4", ReportsTo:"1", Name:"Charles Madigen 1" },
    { EmployeeId:"188", ReportsTo:"4", Name:"Rogine Leger 1" },
    { EmployeeId:"189", ReportsTo:"4", Name:"Gene Porter 1" },
    { EmployeeId:"265", ReportsTo:"189", Name:"Olivier Doucet 1" },
    { EmployeeId:"264", ReportsTo:"189", Name:"Cheryl Pearson 1" }
    ];

    var myData2 = [
    { EmployeeId:"4", ReportsTo:"1", Name:"Charles Madigen 2" },
    { EmployeeId:"188", ReportsTo:"4", Name:"Rogine Leger 2" },
    { EmployeeId:"189", ReportsTo:"4", Name:"Gene Porter 2" },
    { EmployeeId:"265", ReportsTo:"189", Name:"Olivier Doucet 2" },
    { EmployeeId:"264", ReportsTo:"189", Name:"Cheryl Pearson 2" }
    ];

    var myData3 = [
    { EmployeeId:"4", ReportsTo:"1", Name:"Charles Madigen 3" },
    { EmployeeId:"188", ReportsTo:"4", Name:"Rogine Leger 3" },
    { EmployeeId:"189", ReportsTo:"4", Name:"Gene Porter 3" },
    { EmployeeId:"265", ReportsTo:"189", Name:"Olivier Doucet 3" },
    { EmployeeId:"264", ReportsTo:"189", Name:"Cheryl Pearson 3" }
    ];

    var myData4 = [
    { EmployeeId:"4", ReportsTo:"1", Name:"Charles Madigen 4" },
    { EmployeeId:"188", ReportsTo:"4", Name:"Rogine Leger 4" },
    { EmployeeId:"189", ReportsTo:"4", Name:"Gene Porter 4" },
    { EmployeeId:"265", ReportsTo:"189", Name:"Olivier Doucet 4" },
    { EmployeeId:"264", ReportsTo:"189", Name:"Cheryl Pearson 4" }
    ];

    isc.setAutoDraw( false );

    var buttons = isc.HLayout.create({
    height:30,
    members:[

    isc.Button.create({
    width: "*",
    title: "#1 - Set data 1 and fetch",
    click: function() {
    MyTree.dataSource.setCacheData( myData1 );
    MyTree.fetchData();
    }
    }),

    isc.Button.create({
    width: "*",
    title: "#2 - Set data 2 and fetch",
    click: function() {
    MyTree.dataSource.setCacheData( myData2 );
    MyTree.fetchData();
    }
    }),

    isc.Button.create({
    width: "*",
    title: "#3 - Set data 3 and Invalidate",
    click: function() {
    MyTree.dataSource.setCacheData( myData3 );
    MyTree.invalidateCache();
    }
    }),

    isc.Button.create({
    width: "*",
    title: "#4 - Set data 4 and Invalidate",
    click: function() {
    MyTree.dataSource.setCacheData( myData4 );
    MyTree.invalidateCache();
    }
    })
    ]
    });

    var tree = isc.TreeGrid.create({

    ID: "MyTree",

    autoFetchData: false,
    dataFetchMode: "local",
    loadDataOnDemand: false,
    showFilterEditor: true,
    filterOnKeypress: true,
    filterLocalData: true,
    keepParentsOnFilter: true,

    initWidget: function(arguments) {
    this.Super( "initWidget", arguments);
    this.dataSource = isc.DataSource.create( {
    clientOnly: true,
    fields: [
    { name: "Name" },
    { name: "EmployeeId", primaryKey:true },
    { name: "ReportsTo", foreignKey:"EmployeeId" }
    ]
    });
    },

    dataProperties:{
    dataArrived:function (parentNode) {
    this.openAll();
    }
    },

    fields: [
    { name: "Name", canFilter:true }
    ]

    });


    isc.VLayout.create({
    autoDraw:true, width:"100%", height:"100%",
    members:[ buttons, tree ]
    });
    Last edited by mitch; 14 Dec 2015, 11:51.

    #2
    Argh... very sorry for the [font debris!

    Comment


      #3
      See the docs for listGrid/treeGrid.fetchData() - with no change in criteria, it's expected that no server fetch will occur. We can't guess what is meant by "it gets worse if a filter criteria is present", but if you look at the docs, you'll see techniques for detecting whether or not a server fetch will occur when you apply new criteria.

      As far as DataArrived not firing, we can't tell what you mean by the "initial load", as a button press is required to load data in your test case, but, we're seeing correct behavior with the latest patched 10.0.

      As far as the best approach for refreshing data, this depends on what you are trying to achieve, so we don't have enough information to answer - we'd need to know what user experience is desired here (perioddic refresh? Is this read-only data? Does any state or context need to be maintained across the refresh?) and we'd need to know why you chose a client-only DataSource for this scenario as well.

      However, do take a look at the docs for refreshData(), which provides a "transparent" refresh (end user doesn't see a "loading" message). Note this is a 10.0 and later API - you forgot to mention what version you're on.

      Comment


        #4
        Thank you for your fast reply.

        I am using your website feature explorer, so whatever version that is should be valid for this question I hope.

        For this support question, let's just say I have two buttons, A and B.

        By clicking either of them *in any order* any number of times, their data will load and be visible in the tree grid. Simple as that.

        It would help me greatly if you could show me the couple lines of code that are needed for the button click handlers.

        Thanks again,
        Mitch.

        Note: for this moment, let's ignore the auto-open and filtering parts of my initial support question.

        Here is the code:

        var myDataA = [
        { EmployeeId:"4", ReportsTo:"1", Name:"Charles Madigen 1" },
        { EmployeeId:"188", ReportsTo:"4", Name:"Rogine Leger 1" },
        { EmployeeId:"189", ReportsTo:"4", Name:"Gene Porter 1" },
        { EmployeeId:"265", ReportsTo:"189", Name:"Olivier Doucet 1" },
        { EmployeeId:"264", ReportsTo:"189", Name:"Cheryl Pearson 1" }
        ];

        var myDataB = [
        { EmployeeId:"4", ReportsTo:"1", Name:"Charles Madigen 2" },
        { EmployeeId:"188", ReportsTo:"4", Name:"Rogine Leger 2" },
        { EmployeeId:"189", ReportsTo:"4", Name:"Gene Porter 2" },
        { EmployeeId:"265", ReportsTo:"189", Name:"Olivier Doucet 2" },
        { EmployeeId:"264", ReportsTo:"189", Name:"Cheryl Pearson 2" }
        ];


        isc.setAutoDraw( false );

        var buttons = isc.HLayout.create({
        height:30,
        members:[

        isc.Button.create({
        width: "*",
        title: "Load data A",
        click: function() {
        MyTree.dataSource.setCacheData( myDataA );
        MyTree.fetchData();
        }
        }),

        isc.Button.create({
        width: "*",
        title: "Load data B",
        click: function() {
        MyTree.dataSource.setCacheData( myDataB );
        MyTree.fetchData();
        }
        })
        ]
        });

        var tree = isc.TreeGrid.create({

        ID: "MyTree",

        autoFetchData: false,
        dataFetchMode: "local",
        loadDataOnDemand: false,
        showFilterEditor: true,
        filterOnKeypress: true,
        filterLocalData: true,
        keepParentsOnFilter: true,

        initWidget: function(arguments) {
        this.Super( "initWidget", arguments);
        this.dataSource = isc.DataSource.create( {
        clientOnly: true,
        fields: [
        { name: "Name" },
        { name: "EmployeeId", primaryKey:true },
        { name: "ReportsTo", foreignKey:"EmployeeId" }
        ]
        });
        },

        dataProperties:{
        dataArrived:function (parentNode) {
        this.openAll();
        }
        },

        fields: [
        { name: "Name", canFilter:true }
        ]

        });


        isc.VLayout.create({
        autoDraw:true, width:"100%", height:"100%",
        members:[ buttons, tree ]
        });

        Comment


          #5
          The website Feature Explorer version changes at unpredictable times, and the environment is specialized. Please don't use it for testing, and please remember to report the version you test with.

          We don't really understand the gist of this new question; it seems to have already been answered. Did you read the docs for fetchData()? If so, then it should be clear that invalidateCache() is required if the data is loaded, otherwise fetchData() - but again this depends on the desired user experience. You should read the docs for refreshData() as well, which may actually be what you want.

          Comment


            #6
            Thanks again. My version is SmartClient_v100p_2015-06-15_Enterprise. That's as detailed a version as I can get at the moment.

            So, from your help, I conclude that I must do this:

            if ( MyTree.dataSource.getCacheData() == null )
            {
            MyTree.dataSource.setCacheData( myDataA );
            MyTree.fetchData();
            }
            else
            {
            MyTree.dataSource.setCacheData( myDataA );
            MyTree.invalidateCache();
            }

            So, I do this, and it does work, meaning I can click on either button and the data will load, repeatably.

            Question #1: Is this what you were advising?

            Either way, if I then enter any characters (for example, "leg") in the filter editor, it filters fine, however the buttons no longer work (they don't cause the data to load).

            Question #2: What else am I missing?

            Yes, I have read the documentation on fetchData, invalidateCache, and refreshData, however, I still need your support.

            Mitch.

            Comment


              #7
              You've got a call to fetchData() with no criteria - so this obviously will remove the current filter criteria - yet you also seem to expect filtering to work. You need to first explain whether and when criteria should be preserved.

              We also still don't understand why there is a clientOnly DataSource involved. This is creating confusion about what you mean by "load data", and about how you are deciding whether data has been loaded or not.

              Are you talking about loading new data into the DataSource from the server? If so why is there a clientOnly DataSource involved? This is important because the best way to manage data reloading is different depending on whether the new data is coming from the server, or from some client-side process, and whether the new data is needed because criteria of some kind changed, causing different records to be loaded, or whether the records are the same but have been modified in place.

              And critically, how are you currently determining whether data has been reloaded? Are you looking at the RPC tab in the Developer Console, or something else?

              Attempting to simplify down to "make this button do this" won't help. We'll just end up guiding you to create convoluted code that has no applicability to your actual use case.

              Comment


                #8
                Fair questions... let me try to provide more context...

                Initially, I am successfully fetching data from persistent server storage, and it is present in a different datasource from the one involved here.

                We don't touch the server again for this use case.

                We then use that data from time to time to construct a *new* data array (nothing like the original).

                We then take this "transformed" data, and want to load it into a re-usable TreeGrid with a clientOnly datasource using setCacheData(). We then call fetchData / invalidate.

                We do this and it is perfect. The user can use the filter editor, etc.

                Important note: we don't want or expect this TreeGrid and dataSource to ever go to the Server for data. We load it manually as described and it is perfect for rendering our client side tree structure only.

                Finally, the user tells us they want to see the data in a different transformation, and we create a new data array from the original datasource and feed (and show) that new data into the same TreeGrid.

                Short of a demo, I hope this makes more sense as written.

                Now let me try to respond to each of your paragraphs:

                >You've got a call to fetchData() with no criteria - so this obviously will remove the current filter criteria - yet you also seem to expect filtering to work. You need to first explain whether and when criteria should be preserved.

                Good point. Each time we reload the new data, we do actually think it is needed to clear the previous criteria so they start over with both new data and empty criteria. I tried this just now and it seems to fix the "hang" issue:

                click: function() {
                MyTree.clearCriteria(); // NEWLY ADDED
                if ( MyTree.dataSource.getCacheData() == null )
                {
                MyTree.dataSource.setCacheData( myDataA );
                MyTree.fetchData();

                }
                else
                {
                MyTree.dataSource.setCacheData( myDataA );
                MyTree.invalidateCache();
                }
                }

                Is this okay from your perspective?

                >We also still don't understand why there is a clientOnly DataSource involved. This is creating confusion about what you mean by "load data", and about how you are deciding whether data has been loaded or not.

                Hopefully, this is more clear now. Yes?

                >Are you talking about loading new data into the DataSource from the server? If so why is there a clientOnly DataSource involved? This is important because the best way to manage data reloading is different depending on whether the new data is coming from the server, or from some client-side process, and whether the new data is needed because criteria of some kind changed, causing different records to be loaded, or whether the records are the same but have been modified in place.

                This too is now more clear?

                >And critically, how are you currently determining whether data has been reloaded? Are you looking at the RPC tab in the Developer Console, or something else?

                "I see the correct data show up in the TreeGrid" is what I mean by "loading correctly".

                >Attempting to simplify down to "make this button do this" won't help. We'll just end up guiding you to create convoluted code that has no applicability to your actual use case.[/FONT]

                In a funny way, I worked hard to have the simple button example represent, in fact, my exact real use case :-)

                In the end, I think your guidance was thrown off because I did not provide enough context...

                Hopefully, we're on the same page now?

                If so, I still can't get the openAll to work on the first load.
                Last edited by mitch; 14 Dec 2015, 21:32.

                Comment


                  #9
                  It seems that all you want to do is replace the data in the DataSource and have the TreeGrid lose all state (current records and criteria) and re-fetch. The simplest thing to do here is to call setData() with an empty Tree, clearCriteria(), then fetchData(). This works whether or not the TreeGrid has previously loaded data from the DataSource. When you try this out, again, be sure you are working with the fully patched version.

                  invalidateCache() is obviously wrong as it does not clear criteria.

                  Checking for whether getCacheData() is null doesn't seem to make any sense either, since this is a process that is supposed to happen more than twice, right?

                  Yes, context is key. What happened here is that your intent was not clear enough, and trying to figure out your intent from your sample code produced more confusion, not less, because your API usage clashed with your stated intent.

                  We will often request sample code to make your use case more clear, but don't start there. We will obviously run in circles if we are looking at broken code without a clear explanation of what it should actually do.

                  Comment


                    #10
                    Hi Support,

                    Followup on this has taken a while, but here's an update for all who read this in the future:

                    I am using v10.1p_2015-12-18/Enterprise Deployment (built 2015-12-18), which was new at the time you advised above.

                    You last wrote:
                    > It seems that all you want to do is replace the data in the DataSource and have the TreeGrid lose all state (current records and criteria) and re-fetch. The simplest thing to do here is to call setData() with an empty Tree, clearCriteria(), then fetchData(). This works whether or not the TreeGrid has previously loaded data from the DataSource. When you try this out, again, be sure you are working with the fully patched version.

                    Perfect, and so we are doing this:
                    this.dataSource.setCacheData(data);

                    this.setData(isc.Tree.create({}));
                    this.clearCriteria();
                    this.fetchData();

                    And, as you said, it works, even when called repeatedly. Thank you :-)

                    I do have one follow-up question and that is:

                    I want to clear the old criteria from the previous load, as you advised, but sometimes, I'd like to pass a new criteria object to the fetchData() call so my new load is filtered. Can I do this?

                    When I try, the tree seems to hang with "loading..." and gives this javascript error:
                    Click image for larger version

Name:	image.png
Views:	124
Size:	157.4 KB
ID:	235182


                    Thank you again in advance.
                    Mitch.
                    Last edited by mitch; 22 Feb 2016, 15:03.

                    Comment


                      #11
                      We've made a fix to SC 10.0p and newer that should resolve the error you're hitting. It will be in the next nightly builds.

                      Comment


                        #12
                        Perfect. Thank you and I'll post a followup after testing...

                        Comment

                        Working...
                        X