[CLOSED] deferInitialRefresh and infinite javascript loop

  1. #1

    [CLOSED] deferInitialRefresh and infinite javascript loop

    Hi:

    This is one bug from hell !

    I have been able to reproduce the bug outside my app, and it boils down to two things:
    - the deferInitialRefresh property of a gridpanel view being set to true (and it is set to true if the gridpanel is refreshed later if the store it is hooked up didn't get its data when the page is rendered).
    - the programmatic selection of a row gets into an infinite loop. This is an unfortunate combination of data and Ids and the fact that in my models I specify unique id fields. Please pay attention at how the detail data ids is set in the ExtnetController class. Basically each master row has two detail records. The even master rows have two detail records with the ids 1 and 2, but the odd master rows have two detail records with the ids 2 and 1. The ids are reversed on purpose to reflect the real app.

    The all the code of test app is below.
    To reproduce it:
    1.Run the page. Enble the debugging tools for whatever browser you use.
    2. select row 0
    2. go to the details tab
    3. go to the browse tab
    4. select row 1
    5. go to the details tab. You'll notice a lag, then the page is rendered and the data is modified. If you use IE 11 and enable the developer tools you'll see a stack overflow error in the console

    To fix this I used the reconfigure method which turns the gridpanel view deferInitialRefresh to false before it renders the grid, while bindStore doesn't do that.

    Code:

    ExtNetController.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using Ext.Net;
    using Ext.Net.MVC;
    using TestSelectionMemoryPlugin.Models;
    
    namespace TestSelectionMemoryPlugin.Controllers
    {
        public class ExtNetController : Controller
        {
          private static List<MasterDTO> _masterData = new List<MasterDTO>(); /*
                                            {
                                              new MasterDTO
                                              {
                                                Id = 1, Name = "Master 1", Details = new List<DetailDTO>
                                                                                     {
                                                                                       new DetailDTO {SurrogateId = 1, CultureCode = "en-CA", Description = "Master 1 English Description"},
                                                                                       new DetailDTO {SurrogateId = 2, CultureCode = "en-ES", Description = "Master 1 Spanish Description"},
                                                                                     }
                                              },
                                              new MasterDTO
                                              {
                                                Id = 2, Name = "Master 2", Details = new List<DetailDTO>
                                                                                     {
                                                                                       new DetailDTO {SurrogateId = 1, CultureCode = "en-CA", Description = "Master 2 English Description"},
                                                                                       new DetailDTO {SurrogateId = 2, CultureCode = "es-ES", Description = "Master 2 Spanish Description"},
                                                                                     }
                                              },
                                            }; */
    
          static ExtNetController()
          {
            for (int i = 0; i < 100; i++)
            {
              MasterDTO masterDto = new MasterDTO
                                    {
                                      Id = i, Name = String.Format("Master {0}", i) 
                                    };
    
              masterDto.Details = new List<DetailDTO>();
              for (int j = 1; j < 3; j++)
              {
                masterDto.Details.Add(new DetailDTO
                                      {
                                        CultureCode = j == 1 ? "en-CA": "es-ES",
                                        Description = String.Format("Master {0} {1} Description", i, j == 1? "English": "Spanish"),
                                        SurrogateId = i % 2 == 0 ? (j == 1 ? 2: 1) : j
                                      });
              }
              _masterData.Add(masterDto);
            }
          }
    
          public ActionResult Index()
            {
                ExtNetModel model = new ExtNetModel()
                {
                  MasterData = _masterData
                    
                };
    
                return this.View(model);
            }
    
            public ActionResult SampleAction(string message)
            {
                X.Msg.Notify(new NotificationConfig
                {
                    Icon = Icon.Accept,
                    Title = "Working",
                    Html = message
                }).Show();
    
                return this.Direct();
            }
    
          public StoreResult GetMasterData()
          {
            return this.Store(_masterData);
          }
    
          public StoreResult GetMasterRecord(int id)
          {
            return this.Store(_masterData.Where(m => m.Id == id)
              .ToList(),
              1);
          }
        }
    }
    DetailDTO.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace TestSelectionMemoryPlugin.Models
    {
      public class DetailDTO
      {
        /// <summary>
        /// Manufactured ID
        /// </summary>
        public int SurrogateId { get; set; }
        public String CultureCode { get; set; }
        public String Description { get; set; }
        public double change { get; set; }
      }
    }
    ExtNetModel.cs:

    using System.Collections.Generic;
    
    namespace TestSelectionMemoryPlugin.Models
    {
        public class ExtNetModel
        {
            public IList<MasterDTO> MasterData { get; set; }
           
        }
    }
    MasterDTO:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace TestSelectionMemoryPlugin.Models
    {
      public class MasterDTO
      {
        public int Id { get; set; }
        public String Name { get; set; }
        public IList<DetailDTO> Details { get; set; }
      }
    }
    Index.cshtml:

    @using Ext.Net;
    @using Ext.Net.MVC;
    @using ScriptMode = Ext.Net.ScriptMode
    
    @model TestSelectionMemoryPlugin.Models.ExtNetModel
    
    @{
        Layout = null;
      var X = Html.X();
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <title>Ext.NET MVC Sample</title>    
      <script>
        var getAdditionalData = function(data, idx, record, orig)
        {
          return {
            rowBodyColspan: record.fields.getCount(),
            rowBody: '<br>Here goes some data</br>'
          };
        }
    
        var change = function (value) {
          return Ext.String.format(template, (value > 0) ? "green" : "red", value);
        };
    
    
      </script>
      
    
    </head>
    <body>
      @Html.X().ResourceManager().ScriptMode(ScriptMode.Development).SourceFormatting(true)
      
      @(X.Model()
            .Name("Detail")
            .IDProperty("SurrogateId")
            .Fields(
                X.ModelField()
                    .Name("SurrogateId")
                    .Type(ModelFieldType.Int),
                X.ModelField()
                    .Name("CultureCode")
                    .Type(ModelFieldType.String),
                X.ModelField()
                    .Name("Description")
                    .Type(ModelFieldType.String)
          )
      )
    
    
    
      @(X.Model()
            .Name("Master")
            .IDProperty("Id")
            .Fields(
                X.ModelField()
                    .Name("Id")
                    .Type(ModelFieldType.Int),
                X.ModelField()
                    .Name("Name")
                    .Type(ModelFieldType.String)
            )
            .Associations(a => a.Add(X.HasManyAssociation()
                    .Model("Detail")
                    .Name("details")
                    .AssociationKey("Details"))
          )
      )
      
      @(X.Store()
          .ID("recordStore")
          .IDMode(IDMode.Static)
          .ModelName("Master")
          .AutoLoad(false)
          .Proxy(Html.X().AjaxProxy()
            .Url(Url.Content("~/ExtNet/GetMasterRecord"))
            .Reader(Html.X().JsonReader().TotalProperty("total").Root("data"))
          )
          .Parameters(ps =>
                      ps.Add(new StoreParameter("id", "#{masterGridPanel}.getSelectionModel().selected.getAt(0).get('Id')", ParameterMode.Raw))
              )
          .Listeners(l =>
            {
              l.Load.Handler = "var rec = store.getAt(0); /*debugger; */ #{recordFormPanel}.getForm().loadRecord(rec); #{detailsGridPanel}.bindStore(rec.details()); /*#{detailsGridPanel}.view.deferInitialRefresh = true; */ #{detailsGridPanel}.getSelectionModel().deselectAll();#{detailsGridPanel}.getSelectionModel().select(0);";
            
            }
          )
      )
      @(Html.X().Viewport().Layout("fit")
          .Items(
            Html.X().TabPanel()
              .ID("mainTabPanel")
                          .Listeners(a => a.TabChange.Handler = @" if (#{mainTabPanel}.getActiveTab() == #{detailsPanel}) #{recordStore}.load();  /* alert(App.detailsGridPanel.view.deferInitialRefresh + ' '  + App.detailsGridPanel.deferRowRender ); */")
              .Items(
                Html.X().GridPanel().Title("Master")
                  .ID("masterGridPanel")
                  .Region(Region.North)
                  .Split(true)
                  .Height(210)
                  .Store(Html.X().Store()
                    .ModelName("Master")
                    .AutoLoad(true)
                    .Proxy(Html.X().AjaxProxy()
                      .Url(Url.Content("~/ExtNet/GetMasterData"))
                        .Reader(Html.X().JsonReader().TotalProperty("total").Root("data").IDProperty("Id"))
                    )
                  
                  )             
                  .ColumnModel(
                    Html.X().Column().Text("Id").Width(120).DataIndex("Id"),
                    Html.X().Column().Text("Name").Flex(1).DataIndex("Name")
                  )
                  .Listeners(l =>
                  {
                    l.ViewReady.Handler = "#{masterGridPanel}.getSelectionModel().select(0);";
                    l.ViewReady.Delay = 1;
                    l.ItemDblClick.Handler = "#{mainTabPanel}.setActiveTab(#{detailsPanel});";
                  })
                ,
                X.Panel().Title("Details").Layout("border")
                  .ID("detailsPanel")
                  .Items(
                    X.FormPanel()
                      .ID("recordFormPanel")                 
                      .Region(Region.North)
                      .Height(100)
                      .Split(true)
                      .Title("Record Form")                
                      .Items(
                        X.NumberField()
                         .Name("Id")
                         .FieldLabel("ID"),
                        X.TextField()
                         .Name("Name")
                         .FieldLabel("Name")
                      ),
                    X.GridPanel().Title("Details")
                      .ID("detailsGridPanel")
                      .Region(Region.Center)
                      .Store(Html.X().Store()                    
                        .ModelName("Detail")
                        .AutoLoad(false)
                      )
                      .SelectionMemory(false)
                      .SelectionModel(
                          Html.X().RowSelectionModel()
                              .Mode(SelectionMode.Single)
                      )
                    //                  .SelectionMemoryEvents(false)
                      .ColumnModel(
                        Html.X().Column().Text("CultureCode").Flex(1).DataIndex("CultureCode"),
                        Html.X().Column().Text("Description").Flex(1).DataIndex("Description").Editor(Html.X().TextField()),
                        Html.X().Column()
                          .Text("Change")
                          .Align(Alignment.Right)
                          .DataIndex("change")
                          .RightCommandAlign(false)
                          .Renderer("change")
                          .Commands(
                              Html.X().ImageCommand()
                                  .CommandName("Dollar")
                                  .Icon(Icon.MoneyDollar)
                          )
                      )
                      .Plugins(Html.X().CellEditing())
                      .Features(
                          Html.X().RowBody().GetAdditionalData("getAdditionalData")
                      )
                      .View(X.GridView().StripeRows(true).LoadMask(true))
                      
                  )
              
              )
          ))
    
    </body>
    </html>
    Last edited by Daniil; Oct 10, 2014 at 3:36 PM. Reason: [CLOSED]
  2. #2
    Hi @bogc,

    Thank you for the report. I've reproduced. Investigating.
  3. #3
    I would try this Load listener for the recordStore.
    l.Load.Handler = @"if (records.length > 0) { // the Store might load no data
                           App.recordFormPanel.getForm().loadRecord(records[0]);
                           App.detailsGridPanel.getSelectionModel().deselectAll();
                           App.detailsGridPanel.bindStore(records[0].details());
                           App.detailsGridPanel.getView().on('refresh', function() {
                               this.panel.getSelectionModel().select(0);
                           });
                       }";

    Also this appears to be not working.
    l.ViewReady.Handler = "#{masterGridPanel}.getSelectionModel().select(0);";
    l.ViewReady.Delay = 1;
    I guess the ViewReady listener's code is executed before the Store loads the data.

    I would suggest to use this for the master GridPanel.
    .SelectionModel(X.RowSelectionModel().SelectedIndex(0))

Similar Threads

  1. loop through json object with javascript
    By Aod47 in forum 1.x Help
    Replies: 1
    Last Post: Sep 11, 2013, 4:19 PM
  2. Infinite looping with search
    By stonegate in forum 2.x Help
    Replies: 1
    Last Post: Oct 29, 2012, 12:28 PM
  3. [CLOSED] Infinite Scrolling Grid with GridFilter feature - JavaScript Error
    By MacGarnicle in forum 2.x Legacy Premium Help
    Replies: 8
    Last Post: Apr 04, 2012, 2:06 PM
  4. Replies: 23
    Last Post: Jul 15, 2011, 6:22 PM
  5. [CLOSED] javascript - Loop in Content
    By rnfigueira in forum 1.x Legacy Premium Help
    Replies: 1
    Last Post: May 23, 2011, 8:49 PM

Posting Permissions