CartoDB/cartodb20

View on GitHub
lib/assets/javascripts/builder/components/modals/add-widgets/add-widgets-view.js

Summary

Maintainability
A
40 mins
Test Coverage
var _ = require('underscore');
var $ = require('jquery');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var createTuplesItems = require('./create-tuples-items');
var widgetsTypes = require('./widgets-types');
var BodyView = require('./body-view');
var template = require('./add-widgets.tpl');
var renderLoading = require('builder/components/loading/render-loading');
var TableStats = require('./tablestats.js');

var ENTER_KEY_CODE = 13;

/**
 * View to add new widgets.
 * Expected to be rendered in a modal.
 */
module.exports = CoreView.extend({
  module: 'components/modals/add-widgets/add-widgets-view',

  className: 'Dialog-content Dialog-content--expanded',

  events: {
    'click .js-continue': '_onContinue'
  },

  initialize: function (opts) {
    if (!opts.userActions) throw new Error('userActions is required');
    if (!opts.modalModel) throw new Error('modalModel is required');
    if (!opts.layerDefinitionsCollection) throw new Error('layerDefinitionsCollection is required');
    if (!opts.widgetDefinitionsCollection) throw new Error('widgetDefinitionsCollection is required');
    if (!opts.analysisDefinitionNodesCollection) throw new Error('analysisDefinitionNodesCollection is required');

    this._userActions = opts.userActions;
    this._modalModel = opts.modalModel;
    this._layerDefinitionsCollection = opts.layerDefinitionsCollection;
    this._widgetDefinitionsCollection = opts.widgetDefinitionsCollection;
    this._analysisDefinitionNodesCollection = opts.analysisDefinitionNodesCollection;

    this._optionsCollection = new Backbone.Collection();
    this.tableStats = new TableStats({
      configModel: opts.configModel,
      userModel: opts.userModel
    });

    this._analysisDefinitionNodesCollection.each(function (analysisDefinitionNode) {
      analysisDefinitionNode = analysisDefinitionNode.querySchemaModel;
      analysisDefinitionNode.on('change', this._onQuerySchemaChange, this);
      this.add_related_model(analysisDefinitionNode);
      analysisDefinitionNode.fetch();
    }, this);

    this.listenTo(this._optionsCollection, 'change:selected', this._updateContinueButtonState);

    this.add_related_model(this._optionsCollection);

    this._onKeyDown = this._onKeyDown.bind(this);
    this._initBinds();
  },

  render: function () {
    this.clearSubViews();
    this.$el.html(template());

    if (this._hasFetchedAllQuerySchemas()) {
      this._renderBodyView();
    } else {
      this._renderLoadingView();
    }

    this._updateContinueButtonState();

    return this;
  },

  _initBinds: function () {
    $(document).bind('keydown', this._onKeyDown);
  },

  _disableBinds: function () {
    $(document).unbind('keydown', this._onKeyDown);
  },

  _onKeyDown: function (event) {
    event.preventDefault();
    if (event.which === ENTER_KEY_CODE) {
      this._onContinue();
    }
  },

  _onQuerySchemaChange: function () {
    if (this._hasFetchedAllQuerySchemas()) {
      this.render();
    }
  },

  _hasFetchedAllQuerySchemas: function () {
    return this._analysisDefinitionNodesCollection.all(function (analysisDefinitionNode) {
      return analysisDefinitionNode.querySchemaModel.get('status') !== 'fetching';
    });
  },

  _onContinue: function () {
    var self = this;
    var selectedOptionModels = this._optionsCollection.filter(this._isSelected);

    if (selectedOptionModels.length > 0) {
      this._saveSelectedWidgets(selectedOptionModels)
        .then(function (widgets) {
          self._userActions.goToEditWidget(widgets);
          self._userActions.updateWidgetsOrder(selectedOptionModels);
        })
        .catch(function () {
          self._userActions.updateWidgetsOrder(selectedOptionModels);
        });

      this._modalModel.destroy();
    }
  },

  _saveSelectedWidgets: function (selectedOptionModels) {
    var saveWidgetOptionsPromises = [];

    _.map(selectedOptionModels, function (selectedOptionModel) {
      saveWidgetOptionsPromises.push(this._userActions.saveWidgetOption(selectedOptionModel));
    }, this);

    return Promise.all(saveWidgetOptionsPromises);
  },

  _renderBodyView: function () {
    this._createOptionsModels();
    var view = new BodyView({
      el: this._$body(),
      optionsCollection: this._optionsCollection,
      widgetsTypes: widgetsTypes
    });
    this.addView(view.render());
  },

  _renderLoadingView: function () {
    this._$body().html(
      renderLoading({
        title: _t('components.modals.add-widgets.loading-title')
      })
    );
  },

  _$body: function () {
    return this.$('.js-body');
  },

  _updateContinueButtonState: function () {
    this.$('.js-continue').toggleClass('is-disabled', !this._optionsCollection.any(this._isSelected));
  },

  _isSelected: function (model) {
    return !!model.get('selected');
  },

  _createOptionsModels: function () {
    var self = this;
    this._optionsCollection.reset();
    var tuplesItems = createTuplesItems(this._analysisDefinitionNodesCollection, this._layerDefinitionsCollection);

    _.each(widgetsTypes, function (widgetType) {
      var models = widgetType.createOptionModels(tuplesItems, this._widgetDefinitionsCollection);
      models = models.map(function (model) { model.stats = self.tableStats; return model; });
      this._optionsCollection.add(models);
    }, this);
  },

  clean: function () {
    this._disableBinds();
    CoreView.prototype.clean.apply(this);
  }
});