Ag-grid viewport: невозможно прочитать свойство 'bind' из неопределенного

Я копирую пример ag-grid Viewport, помещая его в объект, представляющий таблицу вместо анонимной функции. Я не могу понять, почему он дает мне ошибку в заголовке при попытке подключиться к mockserver, учитывая, что это точно такой же код.

Вот код, который не работает:

   function ViewportDatasource(mockServer) {
        this.mockServer = mockServer;
        this.connectionId = this.mockServer.connect(this.eventListener.bind(this));
    }

    ViewportDatasource.prototype.eventListener = function (event) {
        switch (event.eventType) {
            case 'rowCountChanged':
                this.onRowCountChanged(event);
                break;
            case 'rowData':
                this.onRowData(event);
                break;
            case 'dataUpdated':
                this.onDataUpdated(event);
                break;
        }
    };

вот он весь объект:

function WatcherTable($rootScope,$scope, $http) {

    this.FIELD_KEY_ID = "id";
    this.FIELD_KEY_ISSUER_SHORT_DESC= "isd";
    this.FIELD_KEY_ISSUER = "iss";
    this.FIELD_KEY_CUSIP = "cus";
    this.FIELD_KEY_ISIN = "isin";
    this.FIELD_KEY_BOARD_LABEL = "lbl";
    this.FIELD_KEY_IND_SECT_LABEL = "isl";
    this.FIELD_KEY_CURR="cur"

    this.FIELD_KEY_BEST_BID_SIZE = "bBidSz";
    this.FIELD_KEY_BEST_BID_PRICE = "bBidPrc";
    this.FIELD_KEY_BEST_ASK_PRICE = "bAskPrc";
    this.FIELD_KEY_BEST_ASK_SIZE = "bAskSz";
    this.FIELD_KEY_BEST_BID_SPREAD = "bBidSpd";
    this.FIELD_KEY_BEST_ASK_SPREAD = "bAskSpd";

    this.FIELD_KEY_ORDER_BID_SIZE = "oBidSz";
    this.FIELD_KEY_ORDER_BID_PRICE = "oBidPrc";
    this.FIELD_KEY_ORDER_ASK_PRICE = "oAskPrc";
    this.FIELD_KEY_ORDER_ASK_SIZE = "oAskSz";
    this.FIELD_KEY_ORDER_BID_SPREAD = "oBidSpd";
    this.FIELD_KEY_ORDER_ASK_SPREAD = "oAskSpd";

    this.headerMyOrder="ORDER";
    this.headerMyBidOrder="BID ORDER";
    this.headerMyAskOrder="ASK ORDER";


    this.cols = [{
        headerName : "Security Info",
        marryChildren : true,
        children : [{
            headerName : "Issuer Short Desc.",
            field : this.FIELD_KEY_ISSUER_SHORT_DESC,

            //width : 0,
            //hide : true
        },
        {
            headerName : "Industry Sector Label",
            field : this.FIELD_KEY_IND_SECT_LABEL,
            //width : 80,
            filter: 'set',
            filterParams: { values: ['Advertising', 'Aerospace/Defense', 'Agriculture', 'Airlines', 'Apparel', 'Auto Manufacturers', 'Auto Parts&Equipment', 'Banks', 'Basic Materials', 'Beverages', 'Biotechnology', 'Building Materials', 'Chemicals', 'Coal', 'Commercial Services', 'Communications', 'Computers', 'Consumer, Cyclical', 'Consumer, Non-cyclical', 'Cosmetics/Personal Care', 'Distribution/Wholesale', 'Diversified', 'Electrical Compo&Equip', 'Electronics', 'Energy', 'Energy-Alternate Sources', 'Engineering&Construction', 'Entertainment', 'Machinery-Constr&Mining', 'Household Products/WaresIndustrial', 'Insurance', 'Internet', 'Investment Companies', 'Iron/Steel', 'Kangaroos', 'Leisure Time', 'Lodging', 'Machinery-Constr&Mining', 'Machinery-Diversified', 'Media', 'Metal Fabricate/Hardware', 'Mining', 'Miscellaneous Manufactur', 'Multi-National', 'Office Furnishings', 'Office/Business ', 'Oil&Gas', 'Oil&Gas Services', 'Packaging&Containers', 'Pharmaceuticals', 'Pipelines', 'Real Estate', 'Regional(state/provnc)', 'REITS', 'Retail', 'Savings&Loans'],
                            newRowsAction: 'keep'},
            },
        {
            headerName : "Board Label",
            field : this.FIELD_KEY_BOARD_LABEL,
            //width : 80,
            filter: 'text',
            filterParams: { apply: true }

        }, {
            headerName : "CUSIP",
            field : this.FIELD_KEY_CUSIP,
            //width : 150,
            //suppressFilter: true
        },
        {
            headerName : "ISIN",
            field : this.FIELD_KEY_ISIN,
            //width : 150,
            //suppressFilter: true
        },
        {
            headerName : "Currency",
            field : this.FIELD_KEY_CURR,
            //width : 150,
            //suppressFilter: true
        }
        ]},
        {
        headerName : "Best",
        marryChildren : true,

        children : [ {
            headerName : "Bid Size",
            pinned: 'right',
            field : this.FIELD_KEY_BEST_BID_SIZE,
            cellStyle: {'border-left': '1px solid #E82043', 'color':'#E82043', 'font-weight':'bold', 'text-align':'right'},
            cellRenderer: BidRenderer,
            filter:'number',


            //width : 125,
            //suppressFilter: true
        }, {
            headerName : "Bid Price",
            pinned: 'right',
            field : this.FIELD_KEY_BEST_BID_PRICE,
            cellStyle: {'border-right': '1px solid #E82043', 'color':'#E82043', 'font-weight':'bold', 'text-align':'right'},
            //cellRenderer: 'animateShowChange',
            cellFormatter: numberFormatter,
            cellRenderer: BidRenderer,
            width : 125,
            filter:'number',


            //suppressFilter: true
        },
        {
            headerName : "Ask Price",
            pinned: 'right',
            field : this.FIELD_KEY_BEST_ASK_PRICE,
            //cellRenderer: 'animateShowChange',
            cellFormatter: numberFormatter,
            cellRenderer: AskRenderer,
            cellStyle: {'color':'#19B092', 'font-weight':'bold', 'text-align':'right'},
            filter:'number',
            //width : 125,
            //suppressFilter: true
        },
        {
            headerName : "Ask Size",
            pinned: 'right',
            field : this.FIELD_KEY_BEST_ASK_SIZE,
            cellStyle: {'border-right': '1px solid #19B092', 'color':'#19B092', 'font-weight':'bold', 'text-align':'right'},
            cellRenderer: AskRenderer,
            filter:'number',
            //width : 125,
            //suppressFilter: true
        } ]
    } ];


    //definizione della tabella//// rowModelType: 'virtual', rowModelType: 'pagination',



    this.table = {
            showPrice: true,
            showSpread: true,
            orderSize: 0,
            orderFilter: '',
            enableServerSideFilter: true,
            enableServerSideSorting: true,
            sortingOrder: ['desc','asc',null],
            enableColResize : true,
            debug : true,
            rowSelection : 'multiple',
            rowDeselection : true,
            columnDefs : this.cols,
            rowModelType : 'viewport',
            headerHeight: 20,
            rowHeight: 20,
            viewportRowModelPageSize:30,
            viewportRowModelBufferSize: 15,         
            suppressColumnVirtualisation: true, 
            suppressMenuColumnPanel: true,
            onCellDoubleClicked: WatcherTable.prototype.onCellDoubleClicked.bind(this,$rootScope),          
            getContextMenuItems: WatcherTable.prototype.getContextMenuItems.bind(this,$rootScope),
            getMainMenuItems: WatcherTable.prototype.getMainMenuItems.bind(this, $rootScope), 
            getRowNodeId: function (data) {
                // the code is unique, so perfect for the id
                return data.isin;
            },
            onGridReady:setRowData($http)
    };

    function numberFormatter(params) {
        if (typeof params.value === 'number') {
            return params.value.toFixed(4);
        } else {
            return params.value;
        }
    }



    function BidRenderer () {}

    BidRenderer.prototype.init = function(params) {
        // create the cell
        this.eGui = document.createElement('div');
        this.eGui.innerHTML = '<div></div>';

        // set value into cell
        this.eValue = this.eGui.querySelectorAll('div')[0];
        this.eValue.innerHTML = params.valueFormatted ? params.valueFormatted : params.value;

    };

    // gets called once when grid ready to insert the element
    BidRenderer.prototype.getGui = function() {
        return this.eGui;
    };

    // gets called whenever the user gets the cell to refresh
    BidRenderer.prototype.refresh = function(params) {
        // set value into cell again
        this.eGui.innerHTML = '<div class="bid"></div>';
        this.eValue = this.eGui.querySelectorAll('div')[0];
        this.eValue.innerHTML = params.valueFormatted ? params.valueFormatted : params.value;

    };

    function AskRenderer () {}

    AskRenderer.prototype.init = function(params) {
        // create the cell
        this.eGui = document.createElement('div');
        this.eGui.innerHTML = '<div></div>';

        // set value into cell
        this.eValue = this.eGui.querySelectorAll('div')[0];
        this.eValue.innerHTML = params.valueFormatted ? params.valueFormatted : params.value;

    };

    // gets called once when grid ready to insert the element
    AskRenderer.prototype.getGui = function() {
        return this.eGui;
    };

    // gets called whenever the user gets the cell to refresh
    AskRenderer.prototype.refresh = function(params) {
        // set value into cell again
        this.eGui.innerHTML = '<div class="ask"></div>';
        this.eValue = this.eGui.querySelectorAll('div')[0];
        this.eValue.innerHTML = params.valueFormatted ? params.valueFormatted : params.value;
    };

    // client code (ie your code) will call this constructor, pass in whatever you need for the
    // viewport to do it's job
    function ViewportDatasource(mockServer) {
        this.mockServer = mockServer;
        this.connectionId = this.mockServer.connect(this.eventListener.bind(this));
    }

    // gets called by the grid, tells us what rows the grid is displaying, so time for
    // us to tell the server to give us the rows for that displayed range
    ViewportDatasource.prototype.setViewportRange = function (firstRow, lastRow) {
        this.mockServer.setViewportRange(this.connectionId, firstRow, lastRow);
    };

    // gets called by the grid, provides us with the callbacks we need
    ViewportDatasource.prototype.init = function (params) {
        this.params = params;
    };

    // gets called by grid, when grid is destroyed or this datasource is swapped out for another one
    ViewportDatasource.prototype.destroy = function () {
        this.mockServer.disconnect(this.connectionId);
    };

    // manages events back from the server
    ViewportDatasource.prototype.eventListener = function (event) {
        switch (event.eventType) {
            case 'rowCountChanged':
                this.onRowCountChanged(event);
                break;
            case 'rowData':
                this.onRowData(event);
                break;
            case 'dataUpdated':
                this.onDataUpdated(event);
                break;
        }
    };

    // process rowData event
    ViewportDatasource.prototype.onRowData = function (event) {
        var rowDataFromServer = event.rowDataMap;
        this.params.setRowData(rowDataFromServer);
    };

    // process dataUpdated event
    ViewportDatasource.prototype.onDataUpdated = function (event) {
        var that = this;
        event.changes.forEach(function (change) {
            var rowNode = that.params.getRow(change.rowIndex);
            // if the rowNode is missing, it means the grid is not displaying that row.
            // if the data is missing, it means the rowNode is there, but that data has not
            // loaded into it yet, so to early to set delta changes.
            if (!rowNode || !rowNode.data) {
                return;
            }
            // rowNode.data[change.columnId] = change.newValue;
            // this is a trick, it gets the row to refresh
            rowNode.setDataValue(change.columnId, change.newValue);
        });
    };

    // process rowCount event
    ViewportDatasource.prototype.onRowCountChanged = function (event) {
        var rowCountFromServer = event.rowCount;
        // this will get the grid to make set the height of the row container, so we can scroll vertically properly
        this.params.setRowCount(rowCountFromServer);
    };

    function setRowData($http) {
        // set up a mock server - real code will not do this, it will contact your
        // real server to get what it needs
        var mockServer = new MockServer();
        $http.get('data.json').then(function(response){
            mockServer.init(response.data);
        });

        var viewportDatasource = new ViewportDatasource(mockServer);
        table.api.setViewportDatasource(viewportDatasource);
        // put the 'size cols to fit' into a timeout, so that the scroll is taken into consideration
        setTimeout(function () {
            table.api.sizeColumnsToFit();
        }, 100);
    }

    // setup the grid after the page has finished loading
   /* document.addEventListener('DOMContentLoaded', function () {
        var gridDiv = document.querySelector('#liveStreamExample');
        new agGrid.Grid(gridDiv, table);

        // do http request to get our sample data - not using any framework to keep the example self contained.
        // you will probably use a framework like JQuery, Angular or something else to do your HTTP calls.
        var httpRequest = new XMLHttpRequest();
        httpRequest.open('GET', 'data.json');
        httpRequest.send();
        httpRequest.onreadystatechange = function () {
            if (httpRequest.readyState == 4 && httpRequest.status == 200) {
                var httpResponse = JSON.parse(httpRequest.responseText);
                setRowData(httpResponse);
            }
        };
    });*/

};

WatcherTable.prototype.getContextMenuItems = function ($rootScope,params){
    var result= [{name:"Show Book", action:WatcherTable.prototype.changeBookSubscription.bind(this,$rootScope,params.node.data)}];

    return result;
}

WatcherTable.prototype.onCellDoubleClicked = function ($rootScope,params){
    $rootScope.$broadcast("changeBookSubscription",{instrkey:params.data.cus+"."+params.data.isin,boardLabel:params.data.isd+" "+params.data.lbl});
    if(params.colDef.field.indexOf("bBid")>-1){
        $rootScope.$broadcast("showHitDialog",params);
        log("Show hit dialog");
        console.log(params);
    }
    else if(params.colDef.field.indexOf("bAsk")>-1){
        $rootScope.$broadcast("showLiftDialog",params);
        log("Show lift dialog");
        console.log(params);
    }
}

WatcherTable.prototype.changeBookSubscription = function ($rootScope,data) {
    $rootScope.$broadcast("changeBookSubscription",{instrkey:data.cus+"."+data.isin,boardLabel:data.isd+" "+data.lbl});
}

WatcherTable.prototype.getMainMenuItems = function($rootScope, params){
    var prcCols=["bBidPrc","bAskPrc","oBidPrc","oAskPrc"];
    var spdCols=["bBidSpd","bAskSpd","oBidSpd","oAskSpd"];
    var menuItems= [
        {name:'Show Price', 
            action:function(){
                        params.columnApi.setColumnsVisible(prcCols, !params.api.gridCore.gridOptions.showPrice);
                        params.api.gridCore.gridOptions.showPrice= !params.api.gridCore.gridOptions.showPrice;      
                    }, 
             checked: params.api.gridCore.gridOptions.showPrice
             },
        {name:'Show Spread', 
        action:function(){
                    params.columnApi.setColumnsVisible(spdCols, !params.api.gridCore.gridOptions.showSpread);
                    params.api.gridCore.gridOptions.showSpread= !params.api.gridCore.gridOptions.showSpread;        
                }, 
         checked: params.api.gridCore.gridOptions.showSpread
         },
         {
             name:'Orders',
                subMenu:[
                    {name:'Live Orders Only', 
                        action:function(){
                        if (params.api.gridCore.gridOptions.orderFilter==='live'){
                            params.api.gridCore.gridOptions.orderFilter='';
                            //TODO filter
                        }
                        else {params.api.gridCore.gridOptions.orderFilter='live';
                            //TODO filter
                        }

                    }, checked:(params.api.gridCore.gridOptions.orderFilter==='live')},
                    {name:'My Orders Only',     action:function(){
                        if (params.api.gridCore.gridOptions.orderFilter==='mine'){
                            params.api.gridCore.gridOptions.orderFilter='';
                            //TODO filter
                        }
                        else {params.api.gridCore.gridOptions.orderFilter='mine';
                            //TODO filter
                        }

                    }, checked:(params.api.gridCore.gridOptions.orderFilter==='mine')},
                    {name:'My Firm\'s Orders Only', action:function(){
                        if (params.api.gridCore.gridOptions.orderFilter==='firm'){
                            params.api.gridCore.gridOptions.orderFilter='';
                            //TODO filter
                        }
                        else {params.api.gridCore.gridOptions.orderFilter='firm';
                            //TODO filter
                        }

                    }, checked:(params.api.gridCore.gridOptions.orderFilter==='firm')},
                ]
         },
         {name: 'Size ...', action: function() {

             console.log(params.api.gridCore.gridOptions.orderSize);
             $rootScope.$broadcast("orderSizeDialog",{size:parseInt(params.api.gridCore.gridOptions.orderSize)});
            },
            }
    ];
    return menuItems;
}

Вы можете мне помочь?

РЕДАКТИРОВАТЬ

Я заметил, что функция eventListener не вызывается при создании объекта ViewportDatasource. Как я могу заставить это?

РЕДАКТИРОВАТЬ 2

Я вынул объявление ViewportDatasource из области действия WatcherTable. Теперь я преодолел начальную проблему, но не могу загрузить данные в сетку.

Я создал плункер: https://plnkr.co/edit/EEEJULRE72nbPF6G0PCK

1 ответ

Решение

(Взято из вашего вопроса сегодня, похожие проблемы здесь, я думаю)

У вас есть проблема с синхронизацией в вашем плункере - ваш MockServer пытается обработать данные до того, как они станут доступны.

Чтобы решить эту проблему, нужно сделать две вещи. Первая - попытаться установить источник данных только после того, как данные станут доступны в MockServer:

WatcherTable.prototype.setRowData = function ($http) {
    // set up a mock server - real code will not do this, it will contact your
    // real server to get what it needs
    var mockServer = new MockServer();
    var that = this;
    $http.get('data.json').then(function (response) {
        mockServer.init(response.data);
        var viewportDatasource = new ViewportDatasource(mockServer);
        that.table.api.setViewportDatasource(viewportDatasource);
        // put the 'size cols to fit' into a timeout, so that the scroll is taken into consideration
        setTimeout(function () {
            that.table.api.sizeColumnsToFit();
        }, 100);
    });
}

Во-вторых, в соответствии с той же темой, вы должны предотвратить попытки периодических обновлений обрабатывать данные до того, как они будут готовы. Здесь вы можете либо запускать периодические обновления ПОСЛЕ того, как данные станут доступны, или, проще говоря, добавить проверку, прежде чем пытаться использовать ее:

MockServer.prototype.periodicallyUpdateData = function() {
    if(!this.allData) return;

Я раздвоил ваш плункер (с указанными выше изменениями) здесь: https://plnkr.co/edit/cY30aHIPydVOjcihX8Zh?p=preview

Другие вопросы по тегам