pourover
Version:
A library for simple, fast filtering and sorting of large collections in the browser
483 lines (331 loc) • 22.2 kB
HTML
<html>
<head>
<title>Advanced Views</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
<link rel="stylesheet" media="all" href="docco.css" />
</head>
<body>
<div id="container">
<div id="background"></div>
<ul id="jump_to">
<li>
<a class="large" href="javascript:void(0);">Jump To …</a>
<a class="small" href="javascript:void(0);">+</a>
<div id="jump_wrapper">
<div id="jump_page">
<a class="source" href="advanced_filters.html">
advanced_filters.js
</a>
<a class="source" href="advanced_views.html">
advanced_views.js
</a>
<a class="source" href="basic_pourover_ing.html">
basic_pourover_ing.js
</a>
<a class="source" href="buffering.html">
buffering.js
</a>
</div>
</li>
</ul>
<ul class="sections">
<li id="section-1">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-1">¶</a>
</div>
<h1 id="advanced-views">Advanced Views</h1>
<p>This example will cover how to create and add sorts to collections, how to create custom view types with
custom selectionFns, and some common PourOver view tricks</p>
</div>
</li>
<li id="section-2">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-2">¶</a>
</div>
<p>First, let’s use our set up from the <a href="/pourover/examples/examples_build/basic_pourover_ing.html">basic example</a></p>
</div>
<div class="content"><div class='highlight'><pre>
<span class="hljs-keyword">var</span> monsters = [{name: <span class="hljs-string">"sphinx"</span>, mythology: <span class="hljs-string">"greek"</span>, eyes: <span class="hljs-number">2</span>, sex: <span class="hljs-string">"f"</span>, hobbies: [<span class="hljs-string">"riddles"</span>,<span class="hljs-string">"sitting"</span>,<span class="hljs-string">"being a wonder"</span>]},
{name: <span class="hljs-string">"hydra"</span>, mythology: <span class="hljs-string">"greek"</span>, eyes: <span class="hljs-number">18</span>, sex: <span class="hljs-string">"m"</span>, hobbies: [<span class="hljs-string">"coiling"</span>,<span class="hljs-string">"terrorizing"</span>,<span class="hljs-string">"growing"</span>]},
{name: <span class="hljs-string">"huldra"</span>, mythology: <span class="hljs-string">"norse"</span>, eyes: <span class="hljs-number">2</span>, sex: <span class="hljs-string">"f"</span>, hobbies: [<span class="hljs-string">"luring"</span>,<span class="hljs-string">"terrorizing"</span>]},
{name: <span class="hljs-string">"cyclops"</span>, mythology: <span class="hljs-string">"greek"</span>, eyes: <span class="hljs-number">1</span>, sex: <span class="hljs-string">"m"</span>, hobbies: [<span class="hljs-string">"staring"</span>,<span class="hljs-string">"terrorizing"</span>]},
{name: <span class="hljs-string">"fenrir"</span>, mythology: <span class="hljs-string">"norse"</span>, eyes: <span class="hljs-number">2</span>, sex: <span class="hljs-string">"m"</span>, hobbies: [<span class="hljs-string">"growing"</span>,<span class="hljs-string">"god-killing"</span>]},
{name: <span class="hljs-string">"medusa"</span>, mythology: <span class="hljs-string">"greek"</span>, eyes: <span class="hljs-number">2</span>, sex: <span class="hljs-string">"f"</span>, hobbies: [<span class="hljs-string">"coiling"</span>,<span class="hljs-string">"staring"</span>]}];
<span class="hljs-keyword">var</span> collection = <span class="hljs-keyword">new</span> PourOver.Collection(monsters);
<span class="hljs-keyword">var</span> mythology_filter = PourOver.makeExactFilter(<span class="hljs-string">"mythology"</span>, [<span class="hljs-string">"greek"</span>,<span class="hljs-string">"norse"</span>]);
<span class="hljs-keyword">var</span> gender_filter = PourOver.makeExactFilter(<span class="hljs-string">"sex"</span>, [<span class="hljs-string">"m"</span>,<span class="hljs-string">"f"</span>]);
<span class="hljs-keyword">var</span> hobbies_filter = PourOver.makeInclusionFilter(<span class="hljs-string">"hobbies"</span>,[<span class="hljs-string">"riddles"</span>,
<span class="hljs-string">"sitting"</span>,
<span class="hljs-string">"being a wonder"</span>,
<span class="hljs-string">"coiling"</span>,
<span class="hljs-string">"terrorizing"</span>,
<span class="hljs-string">"growing"</span>,
<span class="hljs-string">"luring"</span>,
<span class="hljs-string">"staring"</span>,
<span class="hljs-string">"god-killing"</span>]);
collection.addFilters([mythology_filter, gender_filter, hobbies_filter]);
<span class="hljs-keyword">var</span> view = <span class="hljs-keyword">new</span> PourOver.View(<span class="hljs-string">"default_view"</span>, collection);</pre></div></div>
</li>
<li id="section-3">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-3">¶</a>
</div>
<h3 id="sorting">Sorting</h3>
</div>
</li>
<li id="section-4">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-4">¶</a>
</div>
<p>To create a sort, we generally extend the Sort object with a <code>fn</code>, the comparator function used to order items.
We also specify which attribute the sort operates over — what it sorts with respect to — with the <code>attr</code> attribute.
Then, we instantiate our new sort.</p>
</div>
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> RevNameSort = PourOver.Sort.extend({
attr: <span class="hljs-string">"name"</span>,
fn: <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(a,b)</span>{</span>
<span class="hljs-keyword">if</span> (b < a){
<span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (b > a){
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
} <span class="hljs-keyword">else</span> {
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
}
});
<span class="hljs-keyword">var</span> rev_name_sort = <span class="hljs-keyword">new</span> RevNameSort(<span class="hljs-string">"rev_name"</span>);</pre></div></div>
</li>
<li id="section-5">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-5">¶</a>
</div>
<p>Like we do with filters, we add the sort to the collection. This indexes the sort of the current items. Additionally, <code>addSorts</code>
binds collection changes to a re-sorting operation.</p>
</div>
<div class="content"><div class='highlight'><pre>
collection.addSorts([rev_name_sort]);</pre></div></div>
</li>
<li id="section-6">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-6">¶</a>
</div>
<p>Once we have added the sort we can call the <code>setSort</code> method of a view with the name of a sort that has been added to its collection.
Even though sorts are added to collections, every view can set its sort independently.</p>
</div>
<div class="content"><div class='highlight'><pre>
view.setSort(<span class="hljs-string">"rev_name"</span>);</pre></div></div>
</li>
<li id="section-7">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-7">¶</a>
</div>
<p>Now, we when call <code>view.getCurrentItems</code>, the items will be sorted accordingly.</p>
</div>
<div class="content"><div class='highlight'><pre>view.getCurrentItems();</pre></div></div>
</li>
<li id="section-8">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-8">¶</a>
</div>
<h3 id="sort-types">Sort Types</h3>
</div>
</li>
<li id="section-9">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-9">¶</a>
</div>
<p>PourOver ships with two default sorts, <code>explicitSort</code> and <code>reverseCidSort</code></p>
</div>
</li>
<li id="section-10">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-10">¶</a>
</div>
<p><code>explicitSort</code> models a sort in which an explicit order of cids is passed. This is how we model
orders that have no functional reference to items’ attributes. <code>explicitSort</code>s are used for, among other things,
user-defined orders or arbitrary orders.
The <code>makeExplicitSort</code> constructor takes <code>(sort_name: String, collection: Collection, attribute_represented_in_order: String, order: [attribute])</code>
Here, we define an explicit sort by passing in the order of the items, identified by their name attribute.
Generally, the attribute that you store an <code>explicitSort</code> on will be a GUID.</p>
</div>
<div class="content"><div class='highlight'><pre>
<span class="hljs-keyword">var</span> my_slideshow_sort = PourOver.makeExplicitSort(<span class="hljs-string">"my_slideshow_order"</span>,collection,<span class="hljs-string">"name"</span>,[<span class="hljs-string">"hydra"</span>,<span class="hljs-string">"huldra"</span>,<span class="hljs-string">"medusa"</span>,<span class="hljs-string">"cyclops"</span>,<span class="hljs-string">"fenrir"</span>,<span class="hljs-string">"sphinx"</span>])</pre></div></div>
</li>
<li id="section-11">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-11">¶</a>
</div>
<p><code>reverseCidSort</code> is the reverse of the default sort. It sorts your items in the reverse of the order in which they were put into the collection</p>
</div>
<div class="content"><div class='highlight'><pre>
<span class="hljs-keyword">var</span> rev_cid_sort = PourOver.makeReverseCidSort(<span class="hljs-string">"rev_sort"</span>,collection)
collection.addSorts([my_slideshow_sort,rev_cid_sort])
view.setSort(<span class="hljs-string">"my_slideshow_order"</span>)</pre></div></div>
</li>
<li id="section-12">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-12">¶</a>
</div>
<h3 id="custom-view-types">Custom View types</h3>
</div>
</li>
<li id="section-13">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-13">¶</a>
</div>
<p>Changing our attention from sorts to the views they influence, how do we define custom views: views that combine their filters in a non-standard ways.
Keeping with the Backbone-ese pattern of extension, we simply extend the <code>PourOver.View</code> object with a new selectionFn.
<em>NOTE: The selectionFn must return a <code>MatchSet</code>, not a raw array of items. This <code>MatchSet</code> gets cached on the view for subsequent queries/renders.</em>
Here, we create a custom view that takes the current query on “mythology” subtracts the current query on sex and ignores hobbies entirely.
This could model a UI in which the user selects a mythology facet to view, a sex to hide and is unable to filter on hobby.</p>
</div>
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> CustomView = PourOver.View.extend({
selectionFn: <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span>{</span>
<span class="hljs-keyword">var</span> mythology_query = collection.filters.mythology.current_query,
sex_query = collection.filters.sex.current_query;
<span class="hljs-keyword">return</span> mythology_query.not(sex_query);
},
render: <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span>{</span>
current_items = <span class="hljs-keyword">this</span>.getCurrentItems();
console.log(current_items);
}
})
<span class="hljs-keyword">var</span> custom_view = <span class="hljs-keyword">new</span> CustomView(<span class="hljs-string">"custom_view"</span>,collection);
custom_view.on(<span class="hljs-string">"update"</span>,custom_view.render);</pre></div></div>
</li>
<li id="section-14">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-14">¶</a>
</div>
<p>Now, <code>custom_view.getCurrentItems()</code> will ignore the hobby query and return the result of the mythology query - that of sex.
Whenever any query changes or the collection changes, the custom view will “render” this result</p>
</div>
</li>
<li id="section-15">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-15">¶</a>
</div>
<h3 id="pattern-user-favorites">Pattern: User favorites</h3>
</div>
</li>
<li id="section-16">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-16">¶</a>
</div>
<p>Say we wanted to have an app that renders a grid of items that users can filter. However, we also want to add a pop-up that will show the
items that users have starred. Can we do this with a single collection and multiple views?</p>
</div>
</li>
<li id="section-17">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-17">¶</a>
</div>
<p>Absolutely.</p>
</div>
</li>
<li id="section-18">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-18">¶</a>
</div>
<p>First, lets make a new collection so as to not interfere with the previous part of the example</p>
</div>
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> collection_two = <span class="hljs-keyword">new</span> PourOver.Collection(monsters);</pre></div></div>
</li>
<li id="section-19">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-19">¶</a>
</div>
<p>Now, we will need three new objects: </p>
<ul>
<li>a filter that represents the objects a user has selected, a favorites filter</li>
<li>a grid view that intersects the hobby, mythology, and sex queries but ignores the favorites filter</li>
<li>a favorites view that ignores every filter but the favorites filter</li>
</ul>
</div>
</li>
<li id="section-20">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-20">¶</a>
</div>
<p>We use <code>manualFilter</code> to create the favorites filter. A <code>manualFilter</code> is exactly what it sounds like: a filter that you manually
control, adding and removing items to specify which items it selects.</p>
</div>
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> favorites_filter = PourOver.makeManualFilter(<span class="hljs-string">"favorites"</span>);
collection_two.addFilters([mythology_filter, gender_filter, hobbies_filter, favorites_filter]);</pre></div></div>
</li>
<li id="section-21">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-21">¶</a>
</div>
<p>When a user favorites an item, say the 2nd and 4th items in our collection, the hydra and the cyclops (this would happen in a callback)</p>
</div>
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> user_selection_cids = [<span class="hljs-number">1</span>,<span class="hljs-number">3</span>];
collection_two.filters.favorites.addItems(user_selection_cids);</pre></div></div>
</li>
<li id="section-22">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-22">¶</a>
</div>
<p>Now, we make a grid view that ignores the favorites filter</p>
</div>
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> GridView = PourOver.View.extend({
selectionFn: <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span>{</span>
<span class="hljs-keyword">var</span> mythology_query = collection_two.filters.mythology.current_query,
sex_query = collection_two.filters.sex.current_query,
hobbies_query = collection_two.filters.hobbies.current_query;
<span class="hljs-keyword">return</span> mythology_query.and(sex_query).and(hobbies_query);
}
});
<span class="hljs-keyword">var</span> grid_view = <span class="hljs-keyword">new</span> GridView(<span class="hljs-string">"grid_view"</span>,collection_two)</pre></div></div>
</li>
<li id="section-23">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-23">¶</a>
</div>
<p>Finally, we make a favorite view that only considers the favorites filter</p>
</div>
<div class="content"><div class='highlight'><pre><span class="hljs-keyword">var</span> FavView = PourOver.View.extend({
selectionFn: <span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">()</span>{</span>
<span class="hljs-keyword">return</span> collection_two.filters.favorites.current_query;
}
});
<span class="hljs-keyword">var</span> fav_view = <span class="hljs-keyword">new</span> FavView(<span class="hljs-string">"fav_view"</span>,collection_two)</pre></div></div>
</li>
<li id="section-24">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-24">¶</a>
</div>
<p>Now, <code>grid_view.getCurrentItems()</code> will only be affected by queries on mythology, sex, or hobbies.
<code>fav_view.getCurrentItems()</code> will only be affected by queries (<code>addItems</code> and <code>removeItems</code>) on the favorites filter</p>
</div>
</li>
</ul>
</div>
</body>
</html>