Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I have a solution that might not work depending on the number of items in your list.</p> <p>My idea was to apply a filter to transform a simple list into a hierarchical one.</p> <p><strike>Here's a <a href="http://jsfiddle.net/xSptw/1/" rel="nofollow">working jsFiddle</a>.</strike> (See the new one at the bottom, really!)</p> <p>The idea is simple, take an existing list, and rearrange it on-the-fly:</p> <pre class="lang-html prettyprint-override"><code>&lt;div ng-repeat="g in items | groupBy:'group' | orderBy:'group'"&gt; &lt;h2&gt;{{g.group}}&lt;/h2&gt; &lt;ul&gt; &lt;li ng-repeat="item in g.items | orderBy:'title'"&gt;{{item.id}}. {{item.title}}&lt;/li&gt; &lt;/ul&gt; &lt;/div&gt; </code></pre> <p>In this case, we've take the original array, and applied a <code>groupBy</code> filter on it. This filter is actually sort of complex, because it needs to make sure it doesn't modify the array more than necessary, so it has to store a <strong>deep copy</strong> of the original array to compare against. If you remove that deep copy and inspection, you will end up with <code>$digest</code> iteration errors every time.</p> <blockquote> <p><strong>Edit:</strong> See bottom for a modified version that performs shallower copies of the array</p> </blockquote> <p>Here's the filter code:</p> <pre class="lang-js prettyprint-override"><code>app.filter("groupBy", function() { var mArr = null, mGroupBy = null, mRetArr = null; return function(arr, groupBy) { if(!angular.equals(mArr, arr) || mGroupBy !== groupBy) { mArr = angular.copy(arr); mGroupBy = groupBy; mRetArr = []; var groups = {}; angular.forEach(arr, function(item) { var groupValue = item[groupBy] if(groups[groupValue]) { groups[groupValue].items.push(item); } else { groups[groupValue] = { items: [item] }; groups[groupValue][groupBy] = groupValue; mRetArr.push(groups[groupValue]); } }); } return mRetArr; }; }); </code></pre> <p>(<code>mArr</code>, <code>mGroupBy</code>, and <code>mRetArr</code> are the "memoized" values, here.)</p> <p>What's nice about this filter is that it doesn't <em>require</em> that the data come in already grouped. The jsFiddle linked above include a button to dynamically add items to the array at the end, but they still end up sorted into groups.</p> <p>Again, the real caveat is that it could be slow and/or expensive based on the number of items in your list. Hundreds of moderately-complex items should be OK, but if you have thousands, you should pre-sort them somewhere else before they get to the scope.</p> <hr> <h1>Edit: Much better version</h1> <p>At the cost of more code complexity, I modified the filter to copy only the key information instead of deeply copying the whole array. This time, I make a hashmap of the groups containing the items. The items are still referenced, but this time it's an explicit reference, so modifying an item in the tree will not force a resort, but changing an item's group or adding or removing an item will.</p> <p><strong>New fiddle: <a href="http://jsfiddle.net/hhWaX/2/" rel="nofollow">http://jsfiddle.net/hhWaX/2/</a></strong></p> <p>And here's the new filter:</p> <pre class="lang-js prettyprint-override"><code>app.filter("groupBy", function() { var mArr = null, mGroupBy = null, mRetArr = null, getMemoArr = function(arr, groupBy) { var ret = {}; angular.forEach(arr, function(item){ var groupValue = item[groupBy]; if(ret[groupValue]) { ret[groupValue].push(item); } else { ret[groupValue] = [item]; } }); return ret; }; return function(arr, groupBy) { var newMemoArr = getMemoArr(arr, groupBy); if(mGroupBy !== groupBy || !angular.equals(mArr, newMemoArr)) { mArr = newMemoArr; mGroupBy = groupBy; mRetArr = []; var groups = {}; angular.forEach(arr, function(item) { var groupValue = item[groupBy] if(groups[groupValue]) { groups[groupValue].items.push(item); } else { groups[groupValue] = { items: [item] }; groups[groupValue][groupBy] = groupValue; mRetArr.push(groups[groupValue]); } }); } return mRetArr; }; }); </code></pre> <p>It's a lot more complicated, but this one should have significantly better performance over large sets, because there is the absolute minimum of duplication of the original array.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload