简体   繁体   中英

Ember component's template issue with jQuery sortable

I'm trying to create a simple Ember component that wraps jQuery UI Sortable plugin. Unfortunately I have an issue with component's template when sortable is cancelled and model is updated manually. It looks like the DOM does not reflect the state of the model. I'm not able to find why.

I've created JS Bin to present this issue. When you change item position, the first one in the group should be removed. Unfortunately it works randomly.

What's wrong with this code?

Here is your JS Bin sortable component:

App.MyListComponent = Ember.Component.extend({
  tagName: 'ul',

  didInsertElement() {
    let opts = {};
    opts.update = this.updateList.bind(this);
    this.$().sortable(opts);
  },

  updateList() {
    this.$().sortable('cancel');
    Ember.run.next(() => {
      this.get('content').removeAt(0);
    });
  }
});

And then this is your JS Bin updated with code from the ember-ui-sortable repo to the following:

App.MyListComponent = Ember.Component.extend({
  tagName: 'ul',

  uiOptions: [
    'axis',
    'containment',
    'cursor',
    'cursorAt',
    'delay',
    'disabled',
    'distance',
    'forceHelperSize',
    'forcePlaceholderSize',
    'grid',
    'handle',
    'helper',
    'opacity',
    'placeholder',
    'revert',
    'scroll',
    'scrollSensitivity',
    'scrollSpeed',
    'tolerance',
    'zIndex'
  ],

  destroySortable: Ember.on('willDestroyElement', function() {
    this.$().sortable('destroy');
  }),

  initSortable: Ember.on('didInsertElement', function () {
    let opts = {};

    ['start', 'stop'].forEach((callback) => {
      opts[callback] = Ember.run.bind(this, callback);
    });

    this.$().sortable(opts);

    this.get('uiOptions').forEach((option) => {
      this._bindSortableOption(option);
    });
  }),

  contentObserver: Ember.observer('content.[]', function () {
    Ember.run.scheduleOnce('afterRender', this, this._refreshSortable);
  }),

  move(oldIndex, newIndex) {
    let content = this.get('content');
    let mutate = this.getWithDefault('mutate', true);
    let item = content.objectAt(oldIndex);

    if (content && mutate) {
      content.removeAt(oldIndex);
      content.insertAt(newIndex, item);
    }

    if(!mutate){
      this.attrs.moved(item, oldIndex, newIndex);
    }

  },

  start(event, ui) {
    ui.item.data('oldIndex', ui.item.index());
  },

  stop(event, ui) {
    const oldIndex = ui.item.data('oldIndex');
    const newIndex = ui.item.index();

    this.move(oldIndex, newIndex);
  },

  _bindSortableOption: function(key) {
    this.addObserver(key, this, this._optionDidChange);

    if (key in this) {
      this._optionDidChange(this, key);
    }

    this.on('willDestroyElement', this, function() {
      this.removeObserver(key, this, this._optionDidChange);
    });
  },

  _optionDidChange(sender, key) {
    this.$().sortable('option', key, this.get(key));
  },

  _refreshSortable() {
    if (this.isDestroying) { return; }
    this.$().sortable('refresh');
  }
});

As you'll see, there is quite a bit extra going on versus your original, so you can have a look at what you missed and hopefully this helps you. It might be a good idea to install that component addon via ember-cli, but also have a look at competing solutions like ember-sortable and others first by using something like ember-observer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM