initialize()

in js/vnext/grid-view.js [118:380]


  initialize({ scrolling, tableClasses, tableAttributes, dataSource, layout }) {
    this._tableView = new TableView({
      el: this.$el,
      layout,
      scrolling,
      classes: tableClasses,
      attributes: tableAttributes,
    });
    this.layout = layout;

    this.model = new Backbone.Model();

    this._dataSource = dataSource;

    const projections = this._projections = {};

    this._registerProjection = proj => {
      const name = proj.name;

      if (_.has(projections, ['name'])) {
        throw new Error('Duplication projectons');
      }

      /**
       * @callback ProjectionHandler
       * @param {DataChainState|StructureChainState|ContentChainState} state
       *    The input state.
       * @param {Object} config
       *    The projection configuration object.
       * @return {DataChainState|StructureChainState|ContentChainState|Promise}
       *    The output state or the `Promise` of the output state.
       */

      /**
       * @typedef ProjectionDefinition
       * @type {Object}
       * @property {string} name - name of the projection
       * @property {ProjectionHandler} handler
       *    The callback to transform the state
       * @property {Object} defaults
       *    The default configuration of the projection
       * @property {function} normalize
       *    The callback to normalize the projection configurations.
       */
      projections[name] = {
        name,
        handler: (_.isFunction(proj) ? proj : proj.handler).bind(this),
        defaults: proj.defaults,
        normalize: (proj.normalize || _.identity).bind(this),
      };

      return projections[name];
    };

    /**
     * @typedef DataChainState
     * @type {Object}
     * @property {string} uniqueId
     *    The unique id for the set of data items. It changes each time new
     *    query is made to the server.
     * @property {string} primaryKey
     *    The primary key of the data items.
     * @property {(Object[]|FakeArray)} items
     *    An array or a fake array of data items.
     * @property {number} totalCount
     *    The total item count on the server.
     * @property {Object.<string, Object>} itemIndex
     *    The items indexed by primary key.
     * @property {external:BackboneViewEventHash} events
     *    The event has in form of `Backbone.View#events`. Any projection can
     *    response to the DOM events by adding its own event handlers.
     */
    this._chainData = new ProjectionChain(this.model);

    /**
     * It extends the {@link DataChainState} with extra properties.
     * @typedef StructureChainState
     * @type {DataChainState}
     * @property {ColumnConfig[]} columns
     *    The array of column configurations.
     * @property {RowConfig[]} headRows
     *    The row configurations for `THEAD`.
     * @property {RowConfig[]} bodyRows
     *    The row configurations for `TBODY`.
     * @property {RowConfig[]} footRows
     *    The row configurations for `TFOOT`.
     */
    this._chainStructure = new ProjectionChain(this.model);

    /**
     * It extends the {@link StructureChainState} with extra properties, and
     * overrides some of the properties.
     * @typedef ContentChainState
     * @type {StructureChainState}
     * @property {ColumnGroup} columnGroup
     *    The column group object, represents the compiled column hierarchy.
     * @property {ColContent[]} cols
     *    The content for `COL` elements in `COLGROUP`.
     * @property {RowContent} headRows
     *    The content for rows in `THEAD`.
     * @property {RowContent} bodyRows
     *    The content for rows in `TBODY`.
     * @property {RowContent} footRows
     *    The content for rows in `TFOOT`.
     */
    this._chainContent = new ProjectionChain(this.model);

    this.pipeDataProjections(query, buffer, itemIndex);
    this.pipeStructureProjections([]
      .concat(this.layout === 'flex' ? flexColumns : columns)
      .concat([
        rows,
        selection,
        rangeSelection,
      ])
    );

    this.pipeContentProjections([
      columnGroup,
      cells,
      sortableHeader,
      events,
    ]);

    const patchEvents = state => _.extend(state, {
      events: _.mapObject(state.events, handler => handler.bind(this)),
    });
    const refreshState = {
      changes: null,
      force: false,
      promise: null,
    };

    this._isRendered = false;
    /**
     * Refresh the GridView
     * @function refresh
     * @memberof GridView
     * @instance
     * @param {boolean} [force=false]
     *    True for force refresh ignoring the cached states.
     */
    const refresh = () => {
      const changes = refreshState.changes;
      const force = refreshState.force;

      refreshState.changes = null;
      refreshState.force = false;

      /**
       * The `GridView` will update its configuration and redraw.
       * @event GridView#willUpdate
       */
      this.trigger('willUpdate', changes);

      // Don't refresh before the view is rendered
      if (!this._isRendered) {
        this.trigger('didUpdate', changes);
        return Promise.resolve();
      }

      return _.reduce([
        this._chainData,
        this._chainStructure,
        this._chainContent,
      ], (memo, chain) => chain.update(memo, changes, force), null)
        .then(patchEvents)
        .then(state => new Promise(resolve => {
          this._tableView.set(state, resolve);
        }))
        .then(nextTick)
        .catch(error => {
          if (error) {
            console.warn(error.stack || error);
          }
        })
        .finally(() => {
          /**
           * The `GridView` did update its configuration and redraw.
           * @event GridView#didUpdate
           */
          refreshState.promise = null;
          this.trigger('didUpdate', changes);
        });
    };

    const scheduleUpdate = this.refresh = force => {
      refreshState.force = force || refreshState.force;
      if (refreshState.changes) {
        _.extend(refreshState.changes, this.model.changedAttributes());
      } else {
        refreshState.changes = this.model.changedAttributes();

        if (refreshState.promise) {
          refreshState.promise = refreshState.promise.then(() => refresh()).finally();
        } else {
          refreshState.promise = nextTick().then(() => refresh());
        }
      }
    };

    this.model.on('change', () => scheduleUpdate(false));

    _.each([
      /**
       * The `GridView` will redraw the DOM. This event may trigger frequently
       * when virtualization is enabled.
       * @event GridView#willRedraw
       */
      'willRedraw',

      /**
       * The `GridView` did redraw the DOM. This event may trigger frequently
       * when virtualization is enabled.
       * @event GridView#didRedraw
       */
      'didRedraw',

      /**
       * @event GridView#willRedrawHeader
       */
      'willRedrawHeader',

      /**
       * @event GridView#didRedrawHeader
       */
      'didRedrawHeader',

      /**
       * @event GridView#willRedrawFooter
       */
      'willRedrawFooter',

      /**
       * @event GridView#didRedrawFooter
       */
      'didRedrawFooter',

      /**
       * @event GridView#wwillRedrawBody
       */
      'willRedrawBody',

      /**
       * @event GridView#didRedrawBody
       */
      'didRedrawBody',
    ], event => {
      this._tableView.on(event, (...args) => {
        this.trigger(event, ...args);
      });
    });

    this.on('all', (event, ...args) => {
      const events = _.chain(this._chainContent)
        .result('state').result('events').value();
      const handler = events && events[event];

      if (_.isFunction(handler)) {
        handler(...args);
      }
    });
  }