0
votes

I have a pretty basic custom element tri-playlist that I'm using to display a grid of another custom element tri-clip.

<dom-module id="tri-playlist">
    <template>
        <template is="dom-repeat" items={{clips}}>
            <tri-clip clip-num={{item.clipNum}}></tri-clip>
        </template>
    </template>
</dom-module>

clips is generated dynamically by fetching information from a server. I'm notified of any changes to this information (even my own), and consequently update clips, after which the template takes care of updating the view to reflect the new data (a clip was deleted, order was changed, etc). Using jQuery UI Sortable, I can re-arrange clips in a playlist or drag them to a new one, and on sortstop I inform the server of any changes so that it stays in sync. That all seems to work fine, separately.

However, after the DOM has been manipulated by my jQuery sort, the template doesn't update correctly. From what I can tell, this is because the template isn't aware of those changes to its (local?/light?) DOM.

So for example, let's say I have three clips [A][B][C] in a playlist, and I move the first one so that it's now last [B][C][A]. When the server informs me of the change (my own) and I update clips accordingly, the template notices the shift and performs it a second time. In this example, the final outcome is [C][A][B].

So far the only way I can get it to work is by setting clips = []; before setting it correctly. Which results in any tri-clips on the screen to momentarily disappear, and forces Polymer to create the elements all over again, which defeats one of the advantages of using a Polymer template in the first place (TLDR: polymer data binding makes the smallest amount of changes necessary).

I've come across this:

When one of the template helper elements updates the DOM tree, it fires a dom-change event.

In most cases, you should interact with the created DOM by changing the model data, not by interacting directly with the created nodes. For those cases where you need to access the nodes directly, you can use the dom-change event.

but, I'm not really sure how to apply it to my situation. From what I understand, I would use the dom-change event if I wanted to do something with the nodes after the template triggered the changes. Or am I wrong about that? Is there anyway to inform the template that changes were made and it needs to update its model accordingly? Am I going about this the wrong way?

2
Back in 0.5 you could use a two-way binding to the individual items, but this isn't possible anymore. I think you might want to take a gander at this answer, which shows you a way to edit complex structures/arrays and their members and have the bindings update properly. It provides an example using a basic table editor. I think it'd be fairly easy to adapt that example to edit item order. If I'm missing the point entirely, though, let me know! - Adaline Simonian
dom-repeat was not designed to deal with it's dom being modified by something other than itself. Suggest you make an issue ticket here github.com/Polymer/polymer/issues. - Scott Miles
@VartanSimonian From what I can tell, the problem isn't so much in editing the underlying array as much as the DOM itself. Until I mutate the DOM using jQuery Sort, any changes to the array update properly. Would that post you linked to still be a viable solution? I'm not quite sure I understand how to apply it to my situation. - zberry
For anyone interested, I've opened an issue on github. - zberry

2 Answers

1
votes

For whatever it's worth, the work-around I managed to come up with (which isn't ideal but fixes the issues I was running in to) is essentially to iterate over the dom-repeat and assess the changes that were made to its children (in my case by jQuery sortable). Then carry out operations that mirror those changes on dom-repeat's items array. I was using a jQuery multisortable plugin so I had to account for an undetermined amount of changes (where as normally jQuery sortable will only ever change one element at a time) but I figure the code below could easily be optimized for such situations. In the jQuery sortable stop event, I did the following:

1 ) Get the indices (index was a property of the items in my dom-repeat array) reflecting the positions of dom elements before sorting

// get indices of selected elements that presumably changed
var target = event.target,
    toRearr = [],
    firstSelected = null;

$( target ).children( '.selected' ).each( function() {
  toRearr.push(this.index);
  // keep reference of first selected for re-ordering start point
  if (firstSelected === null) firstSelected = this;
});

2) See where first selected element is after sorting (note that this is in relation to all of the children, whereas the previous loop was only selected elements)

// find the index of where we need to 'paste' the elements
var toPaste = -1;
$( target ).children( 'selector' ).each( function( index ) {
  if (this === firstSelected) {

    if (firstSelected.index > index) { // elements were moved to the left
      toPaste = index;
      return false; // breaks out of $.each loop
    }
    else if (firstSelected.index < index){ // elements were moved to the right
      toPaste = index + toRearr.length;
      return false; // breaks out of $.each loop
    }
    //else no elements were changed
 }
});

3) Make array mutations on dom-repeat's items array (or whatever you've named it) to reflect DOM changes

if (toPaste !== -1) { // meaning its children were indeed rearranged

  // inform Polymer of the change
  var tempPaste = toPaste;
  for (var i = toRearr.length - 1; i >= 0; i--) { // start from end to preserve order

    // we're moving an item to the right of our 'paste' index, so decrease
    // tempPaste by 1 so it stays consistent
    if (tempPaste > toRearr[i]) tempPaste--;

    // move element from old index to new index, using two array splices
    // that essentially remove and reinsert the element in the array
    target.splice('items', tempPaste, 0, target.splice('items', toRearr[i], 1)[0]);
  }
}

Hopefully that'll help anyone in a similar situation

0
votes

So basically you are manually changing the model so that Polymer can stay in sync with the changes which jQuery sortable made? Check this example if it might be relevant. Not sure if it works with jQuery sortable