Thank you for reporting this bug. We will make it our priority to review this report.
  1. #1
    Sencha Premium Member
    Join Date
    Dec 2011
    Posts
    34
    Vote Rating
    0
    MarcT is on a distinguished road

      0  

    Default Scrolling in LiveGrid

    Scrolling in LiveGrid


    I created a LiveGrid that auto-pages in content, following the LiveGrid example. The only real difference is that I'm using an HttpProxy with JSON data instead of RPC. The loading and paging all work great, except for one major problem: Every time the grid auto-loads new data, the view resets to the top (index 0), which makes it impossible to scroll down beyond the store's cache size.

    Specifically, in GridView.onDataChanged, you call refresh(false) without setting the "preventScrollToTopOnRefresh" bool. I believe you need to set this variable in the LiveGridView.refresh() function before calling super.refresh() (but only sometimes, not every time). I subclassed LiveGridView and did that myself, and the scrolling is working now (there are some issues with filtering, though, since now I don't reset to 0 when I should).

    I'm not sure if this is really a bug, because your LiveGrid example is working just fine, and I can't figure out how. Is there some special call I'm just not seeing, or some magic that the RPCProxy does that HttpProxy doesn't?

  2. #2
    Sencha Premium Member
    Join Date
    Dec 2011
    Posts
    34
    Vote Rating
    0
    MarcT is on a distinguished road

      0  

    Default


    Is there more information that I can provide that might help get this question answered? Has anyone else used the LiveGrid and had it work for them? I'm getting to the point of being blocked on this...

  3. #3
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,640
    Vote Rating
    80
    Colin Alworth is just really nice Colin Alworth is just really nice Colin Alworth is just really nice Colin Alworth is just really nice Colin Alworth is just really nice

      0  

    Default


    It doesn't seem as though a changed proxy could do this, as all a proxy does is deliver new data to the loader, who then updates anything that is listening in. A live JSON example should be pretty easy to make, provided you've got a working data source (and the explorer doesnt have a paging json source at the moment).

    Can you perhaps post your grid, loader setup that doesn't work? You could be missing something simple that configures these pieces to work together, or not quite seeing why we wire things a certain way.

    For example, the live grid's "scrollbar" isn't actually the scrollbar drawn for that element, but just something that looks and acts like a scrollbar, and is used to page in more data - playing with your browser's inspector tools should demonstrate this fact. We do some event wiring to get theses pieces to pretend that they are linked, but that is all. The container that is 'scrolled to top' doesn't really have room to scroll, only the apparent scroll bar is moved (see LiveGridView.positionLiveScroller for when that happens). The actual 'viewport' element only should contain the items that should be presently visible (though we might be hanging on to other old, empty rows when scrolling up, we may need to look at that).

  4. #4
    Sencha Premium Member
    Join Date
    Dec 2011
    Posts
    34
    Vote Rating
    0
    MarcT is on a distinguished road

      0  

    Default


    Thanks for the additional info, Colin. When I say the view "scrolls to the top", I mean that once the user scrolls down (I usually use the wheel but dragging the thumb does it too) and forces an automatic reload of additional items, as soon as those items come back from our REST service and get loaded into the store, the view gets a "refresh" call, and asks the proxy for items 1-40.

    If you can find something amiss in my wiring, that would be great. There's definitely room for my understanding of all of that to improve.

    Code:
    public class LogGrid implements IsWidget {
    
    
        private String uri = "/api/v1/log/collection";
        
        private ContentPanel container;
        private Grid<LogEntry> grid;
        private PagingLoader<LogLoadConfig, PagingLoadResult<LogEntry>> loader;
        private String levelFilter = "";
        private String searchStr = "";
        private int currentCount = -1;
        private boolean needFirstLoad = false;
        
        public interface LogData {
            String getNext_log_entries_uri();
            int getCount();
            int getTotal_filtered_count();
            int getTotal_unfiltered_count();
            List<LogEntry> getLog_entries();
        }
        
        public interface LogEntryABF extends AutoBeanFactory {
            static LogEntryABF instance = GWT.create(LogEntryABF.class);
            
            AutoBean<LogData> items();
            AutoBean<LogLoadConfig> loadConfig();
        }
        
        class LogReader extends JsonReader<PagingLoadResult<LogEntry>, LogData> {
            public LogReader(AutoBeanFactory factory, Class<LogData> rootBeanType) {
                super(factory, rootBeanType);
            }
            
            @Override
            protected PagingLoadResult<LogEntry> createReturnData(Object loadConfig, LogData incomingData) {
                PagingLoadConfig config = (PagingLoadConfig)loadConfig;
                return new PagingLoadResultBean<LogEntry>( incomingData.getLog_entries(),
                            incomingData.getTotal_filtered_count(), config.getOffset() );
            }
        }
        
        // Interface to expose filter params to the server
        interface LogLoadConfig extends PagingLoadConfig {        
            String getLevel();
            void setLevel(String level);
            
            String getCategory();
            void setCategory(String cat);
            
            String getContains_str();
            void setContains_str(String str);
        }
        
        @Override
        public Widget asWidget() {
            if(container != null) return container;
            
            GetFilteredCount();
            
            LogEntryABF factory = LogEntryABF.instance;
            
            Uri path = new Uri(uri, HostOverride.Always);
            RequestBuilder builder = new RequestBuilder( RequestBuilder.GET, path.buildUri() );
            builder.setHeader("Accept", "application/json");
            HttpProxy<LogLoadConfig> proxy = new HttpProxy<LogLoadConfig>(builder);
            proxy.setWriter(new UrlEncodingWriter<LogLoadConfig>(factory, LogLoadConfig.class));
    
    
            LogProperties props = GWT.create(LogProperties.class);
            ListStore<LogEntry> store = new ListStore<LogEntry>( props.key() );
            
            LogReader reader = new LogReader(factory, LogData.class);
            loader = new PagingLoader<LogLoadConfig, PagingLoadResult<LogEntry>>(proxy, reader);
            LogLoadConfig config = factory.create(LogLoadConfig.class).as();
            loader.useLoadConfig( config );
            
            loader.setReuseLoadConfig(true);
            loader.addLoadHandler(new LoadResultListStoreBinding<LogLoadConfig, LogEntry, PagingLoadResult<LogEntry>>(store));
            loader.addBeforeLoadHandler(new BeforeLoadHandler<LogGrid.LogLoadConfig>() {
    
    
                @Override
                public void onBeforeLoad(BeforeLoadEvent<LogLoadConfig> event) {
                    event.getLoadConfig().setLevel(levelFilter);
                    event.getLoadConfig().setContains_str(searchStr);
                }
            });
            
            List<ColumnConfig<LogEntry, ?>> l = new ArrayList<ColumnConfig<LogEntry, ?>>();
            ColumnConfig<LogEntry, Date> timeCol = new ColumnConfig<LogEntry, Date>(props.timestamp(), 40, "Time");
            timeCol.setCell( new DateCell( DateTimeFormat.getFormat("HH:mm:ss.SSS") ) );
            ColumnConfig<LogEntry, String> sevCol = new ColumnConfig<LogEntry, String>(props.level(), 40, "Severity");
            ColumnConfig<LogEntry, String> detailCol = new ColumnConfig<LogEntry, String>(props.msg(), 400, "Details");
            
            l.add(timeCol);
            l.add(sevCol);
            l.add(detailCol);
            ColumnModel<LogEntry> cm = new ColumnModel<LogEntry>(l);
            
            final LiveGridView<LogEntry> liveGridView = new LiveGridView<LogEntry>();
            liveGridView.setCacheSize(200);
            liveGridView.setSortingEnabled(false);
            liveGridView.setForceFit(true);
            liveGridView.setAutoExpandColumn(detailCol);
    
    
            grid = new Grid<LogEntry>(store, cm ) {
                @Override
                protected void onAfterFirstAttach() {
                    super.onAfterFirstAttach();
    
    
                    // CurrentCount is set when we get a response back from
                    // GetFilteredCount. If we don't have that yet, don't load here.
                    if(currentCount < 0) {
                        needFirstLoad = true;
                        return;
                    }
                    Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                        @Override public void execute() {
                            int batch = liveGridView.getCacheSize();
                            loader.load( Math.max(0,currentCount-batch), batch );
                        }
                    });
                }
            };
            grid.setLoadMask(true);
            grid.setLoader(loader);
            grid.setView( liveGridView );
            grid.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
            if(selChanged != null) {
                grid.getSelectionModel().addSelectionChangedHandler(selChanged);
                selChanged = null;
            }
            
            LogLoadConfig cfg = (LogLoadConfig)loader.getLastLoadConfig();
            cfg.setLevel(levelFilter);
            
            container = new ContentPanel();
            container.setWidget(grid);
            container.setBorders(true);
            container.setHeaderVisible(false);
            
            return container;
        }

  5. #5
    Sencha Premium Member
    Join Date
    May 2012
    Posts
    12
    Vote Rating
    0
    posthour is on a distinguished road

      0  

    Default Same problem

    Same problem


    I am having this exact same problem and I'm not sure why. Has this been resolved?

  6. #6
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,640
    Vote Rating
    80
    Colin Alworth is just really nice Colin Alworth is just really nice Colin Alworth is just really nice Colin Alworth is just really nice Colin Alworth is just really nice

      0  

    Default


    I've just been reading another, related post to this one, and it reminded me to take another look here - it should not be required to add a load handler to populate the store in this case.

    The LiveGridView reads in items from the remote loader, and keeps track of them in its own cache (specifically, in LiveGridView.cacheStore). The LiveGridView adds its own load handler, and populates the cache when data arrives, but does not update the main ListStore until required to do so.

    Take another look at the http://sencha.com/examples/#ExamplePlace:livegrid example on what moving parts need to be in place for the live grid to be working - while you may have your own loader and proxy, you shouldn't need to do any extra wiring from that loader/proxy to the grid/store/view than what is being done in that example.

  7. #7
    Sencha User
    Join Date
    Oct 2011
    Posts
    2
    Vote Rating
    0
    evdubs is on a distinguished road

      0  

    Default


    I have recently been integrating the LiveGrid with Atmosphere to do server-side push, and I have seen this same issue happen. I believe the issue is related to the preventScrollToTopOnRefresh member variable of GridView. Since the data that is being returned to the ListStore has changed, GridView is notified via the onDataChanged handler. onDataChanged calls refresh() and one of the first things refresh() does is check if preventScrollToTopOnRefresh is not enabled; if preventScrollToTopOnRefresh is not enabled, scrollToTop() is called, and the LiveGrid scrolls to the top. The protected variable preventScrollToTopOnRefresh is only used in refresh() and the onColumnMove() handler method. This seems inappropriate for the case when the Grid data is actually updated.

    To work around this issue, I simply extended LiveGridView<M> and added a setPreventScrollToTopOnRefresh() mutator method to be set in the parent widget's constructor. This has achieved the desired effect.
    Last edited by evdubs; 28 Jun 2012 at 9:17 PM. Reason: set atmosphere URL; correctly state preventScroll check; refer to preventScroll as protected

  8. #8
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,640
    Vote Rating
    80
    Colin Alworth is just really nice Colin Alworth is just really nice Colin Alworth is just really nice Colin Alworth is just really nice Colin Alworth is just really nice

      0  

    Default


    I've had the chance to go over this in depth with evdubs in IRC, and we came to the conclusion that something was being mis-wired - both in his case and in the code from MarcT, this line was present (or something like it):

    Code:
    loader.addLoadHandler(new LoadResultListStoreBinding<LogLoadConfig, LogEntry, PagingLoadResult<LogEntry>>(store));
    Here's why this is a problem:

    A Live grid, like a normal grid, needs a ListStore to work from when deciding what items to draw on the screen. Unlike a normal grid, a live grid has an extra store.

    When data is loaded from the server, it is stored in LiveGridView.cacheStore, and the necessary rows will be put in the normal store when needed. This allows the Live part of the view to manage loading new items and scrolling, but lets the Grid part continue to figure out how to draw the currently visible viewport.

    Here is the problem - the line listed above means that when new data comes back from the server, it is loaded into the cacheStore as normal, and then also into the visible, normal store. This makes the Grid part confused, since it is supposed to do certain things when the main grid data is refreshed like this. In turn, this confuses the LiveGridView, since it is supposed to be responsible for managing that store, so while it expects the store to be changed, it didn't expect it that early.

    Skip any wiring to the standard store and you should be fine. Let the LiveGridView manage loading items if you want it to work as normal. If you want custom behavior, it is up to you to modify the LiveGridView to deal with the new constraints you are adding.

  9. #9
    Sencha Premium Member
    Join Date
    Dec 2011
    Posts
    34
    Vote Rating
    0
    MarcT is on a distinguished road

      0  

    Default


    Thanks, Colin! I knew there had to be something wired wrong, but I could not for the life of me figure out what it was. I really appreciate you tracking that down for us!

    Please please please put that kind of information into the official docs! The API docs right now are almost useless since they just give the function names and parameter names, with no justification for _why_ the functions exist or how the params are expected to be used. The examples are great but they're so complex that it can take days to tease apart which calls I need and which are extraneous to my situation.

  10. #10
    Touch Premium Member
    Join Date
    Mar 2008
    Location
    Morgan Hill, CA
    Posts
    122
    Vote Rating
    3
    margozzi is on a distinguished road

      0  

    Default


    Thank God for this forum post. I don't think I would have ever figured this out.
    Here are the Javadoc comments for LiveGridView:

    LiveGridView for displaying large amount of data. Data is loaded on demand as the user scrolls the grid.

    That is it.

film izle

hd film izle

film sitesi

takipci kazanma sitesi

takipci kazanma sitesi

güzel olan herşey

takipci alma sitesi

komik eğlenceli videolar