S
S
stefan_hansch2016-10-17 10:31:51
JavaScript
stefan_hansch, 2016-10-17 10:31:51

Backbone - transition through sub-tabs in turn, if the fields are all filled?

Hello. I'm more of a backend developer. Faced backbone + marionette.
The question is:
There are tabs, step 1 step 2, etc. While the user on each tab in turn fills out the form, it is impossible to move on to the next tab. It works. But starting from the second step, the tab has more sub-tabs (under steps), when filled in, validation does not work and you can walk through them back and forth, but you need to go to the next sub-tab only after filling in all the fields.
When I set a breakpoint to fire, the tab transition event fires only for tabs and not for tabs.
Here is the code itself:
views/inputs/steps.js

Lib.ns('Views.Inputs', function(Inputs) {
  'use strict';

  var Empty = Mn.ItemView.extend({
    tagName: 'li',
    template: function() { return ''; }
  });

  var Tab = Mn.ItemView.extend({
    template: '#Inputs-StepTabTemplate',
    tagName: 'li',
    triggers: {click: 'select'},
    ui: {index: '[data-ui="index"]'},
    bindings: {'[data-ui="title"]': 'title'},
    onRender: function() {
      this.stickit();
      this.ui.index.html(this.getOption('index') + 1);
    }
  });

  var Tabs = Mn.CollectionView.extend({
    tagName: 'ul',
    className: 'special',
    childView: Tab,
    childEvents: {select: 'selectHandler'},

    filter: function(child) {
      return child.get('name') !== 'active_step';
    },

    childViewOptions: function(item, index) {
      return {index: index};
    },

    selectHandler: function(view) {
      this.triggerMethod('select', this.collection.indexOf(view.model));
    },

    select: function(index) {
      this.$('li').removeClass('uk-active');
      this.children.findByModel(this.collection.at(index)).$el.addClass('uk-active');
    }
  });

  var Parent = Inputs.Container;
  var Steps = Parent.extend({
    tagName: 'ul',
    className: 'uk-switcher special',
    defaultChild: Empty,

    initialize: function() {
      Parent.prototype.initialize.apply(this, arguments);
      this.collection = this.model.get('fields');
    },

    childViewOptions: function(item) {
      var opts = Parent.prototype.childViewOptions.apply(this, arguments);
      opts.tagName = 'li';
      opts.showTitle = item.has('showTitle') ? item.get('showTitle') : false;
      opts.formSection = false;
      return opts;
    }
  });

  Inputs.Steps = Mn.LayoutView.extend({
    className: 'steps',
    template: '#Inputs-StepsTemplate',

    behaviors: {
      Messageable: {el: '[data-region="messages"]'},
    },

    regions: {
      tabs:  '[data-region="step-tabs"]',
      panes: '[data-region="step-panes"]'
    },

    ui: {
      prev: '[data-ui="prev"]',
      next: '[data-ui="next"]',
      save: '[data-ui="save"]'
    },

    events: {
      'click @ui.prev': 'prevClickHandler',
      'click @ui.next': 'nextClickHandler',
      'click @ui.save': 'saveClickHandler'
    },

    childEvents: {
      next: 'nextHandler'
    },

    initialize: function() {
      var fields = this.model.get('fields');
      this.activeStep = this.model.find('active_step');
      if (!this.activeStep) {
        fields.add({name: 'active_step', type: 'integer', visible: false});
        this.activeStep = this.model.find('active_step');
        this.activeStep.setValue(0);
      }
      var step = this.getOption('step');
      if (step == null) { step = this.activeStep.getValue(); }
      if (step == null) { step = 0; }
      this.activeStep.setValue(step);
    },

    select: function(index) {
      if (!this.switcher) { return; }
      this.scrollTop();
      this.tabs.select(index);
      this.switcher.show(index);
      this.activeStep.setValue(index);
    },

    onRender: function() {
      var opts = {
        rootView: this.getOption('rootView') || this,
        collection: this.model.get('fields')
      };
      this.tabs  = new Tabs(opts);
      this.listenTo(this.tabs, 'select', this.selectHandler);
      this.fakeTabs = new Tabs(opts);
      this.steps = new Steps(_.extend(opts, {model: this.model}));
      this.getRegion('tabs').show(this.tabs);
      this.getRegion('panes').show(this.steps);
      this.fakeTabs.render();
      var step = this.activeStep.getValue();
      this.index = step;
      this.updateNavigation();
      _.defer(function(view) {
        view.switcher = UIkit.switcher(view.fakeTabs.$el, {
          connect: view.steps.$el,
          animation: 'slide-horizontal'
        });
        view.switcher.on('show.uk.switcher', _.bind(view.switchHandler, view));
        view.select(step);
      }, this);
    },

    updateNavigation: function(index) {
      if (index == null) { index = this.index; }
      if (index === 0) {
        this.ui.prev.attr('disabled', 'disabled');
      } else {
        this.ui.prev.removeAttr('disabled');
      }
      // this.tabs.collection.length - active_step - 1
      if (index === this.tabs.children.length - 1) {
        this.ui.next.find('span').html('Подготовить');
      } else {
        this.ui.next.find('span').html('Следующий шаг');
      }
    },

    setRegistered: function(registered) {
      this.registered = registered;
      if (registered) {
        this.ui.save.show();
      } else {
        this.ui.save.hide();
      }
    },

    switchHandler: function(event, item) {
      this.index = $(item).index();
      this.updateNavigation();
    },

    prevClickHandler: function() {
      if (this.index === 0) { return; }
      this.select(this.index - 1);
    },

    nextClickHandler: function() {
      var view = this.getOption('rootView') || this;
      if (this.index === this.tabs.children.length - 1) {
        if (!this.validateCurrentStep()) {
          this.scrollTop();
          return;
        }
        view.triggerMethod('create');
      } else {
        if (this.index === 0 && !this.registered) {
          view.triggerMethod('register', 1);
          return;
        }
        if (!this.validateCurrentStep()) {
          this.scrollTop();
          return;
        }
        this.select(this.index + 1);
        view.triggerMethod('save');
      }
    },

    validateCurrentStep: function() {
      return this.model.get('fields').at(this.index).validate();
    },

    saveClickHandler: function() {
      var view = this.getOption('rootView') || this;
      view.triggerMethod('save');
    },

    selectHandler: function(index) {
      if (!this.registered) {
        var view = this.getOption('rootView') || this;
        view.triggerMethod('register', index);
        return;
      }
      if (this.index < index && !this.validateCurrentStep()) {
        this.scrollTop();
        return;
      }
      this.select(index);
    },

    nextHandler: function(view) {
      if (this.steps.children.last() === view) {
        this.triggerMethod('next');
        return;
      }
      var index = this.model.get('fields').indexOf(view.model);
      if (index >= 0) { this.select(index + 1); }
    },

    scrollTop: function() {
      // stop scroll animation on any user action
      var stopEvents = 'scroll mousedown wheel DOMMouseScroll mousewheel keyup touchmove';
      var stop = function() {
        UIkit.$('html,body').stop();
        $('html,body').off(stopEvents, stop);
      };
      $('html,body').on(stopEvents, stop);
      UIkit.Utils.scrollToElement(this.tabs.$el, {
        complete: function() { $('html,body').off(stopEvents, stop); }
      });
    }
  });
});

The handlers in the steps.js file are triggered when navigating through the tabs. There are no subtabs.
For example, when switching to another tab, the handler from the steps.js file worked
selectHandler: function(view) {
      this.triggerMethod('select', this.collection.indexOf(view.model));
    },

It does not work
on subtabs. For tabs and subtabs, one view is used:
var Steps = Parent.extend({
    tagName: 'ul',
    className: 'uk-switcher special',
    defaultChild: Empty

Thanks in advance for your reply.

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question