{"id":284,"date":"2015-03-02T01:00:00","date_gmt":"2015-03-02T01:00:00","guid":{"rendered":"http:\/\/localhost\/?p=284"},"modified":"2018-07-25T19:25:52","modified_gmt":"2018-07-25T19:25:52","slug":"collection-repeat-iteration-two","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two","title":{"rendered":"Collection Repeat: Estimate, Iterate, Improve"},"content":{"rendered":"<blockquote><p>Update: Collection Repeat has been replaced by <a href=\"https:\/\/ionicframework.com\/docs\/api\/components\/virtual-scroll\/VirtualScroll\/\">Virtual Scroll<\/a> in Ionic 3+.\n<\/p><\/blockquote>\n<p>Collection repeat is Ionic&#8217;s buttery-smooth solution for scrolling huge lists. Inspired by <a href=\"https:\/\/developer.apple.com\/library\/prerelease\/ios\/documentation\/UIKit\/Reference\/UITableViewDelegate_Protocol\/index.html\">iOS&#8217;s UITableView<\/a>, we switch out elements as the user scrolls, so that only the minimum necessary elements are rendered. We released our first version of collection repeat <a href=\"http:\/\/ionicframework.com\/blog\/collection-repeat\/\">last year<\/a> and have been improving it since then. Recently, we identified some huge potential performance increases and decided a complete refactor was necessary.<\/p>\n<p>Before we dive into the details, let\u2019s talk about how collection repeat works at the most basic level.<\/p>\n<p><!--more--><\/p>\n<h3 id=\"theessentials\">The Essentials<\/h3>\n<p>Say there are four items on the screen, matching items 1-4 in the user&#8217;s array of data: As the user scrolls down, item 1 will move up and out of view. Once it&#8217;s fully out of view, its element will move to the bottom of the screen to the space where item 5 should be, as item 5 moves up and into view.<\/p>\n<p>Additionally, we take the Angular scope that just represented item 1, assign item 5&#8217;s data to it, and trigger a digest on that scope to make the element represent item 5.<\/p>\n<p>To follow the above strategy and position every element properly, we need every item&#8217;s exact width and height.<\/p>\n<h3 id=\"theoldandthenew\">The Old and the New<\/h3>\n<p>In our first iteration of collection-repeat, we required developers to provide dimension stats for every item, because we assumed that every item might have a unique width and height:<\/p>\n<pre><code class=\"html\">&lt;ion-list&gt;\n  &lt;ion-item collection-repeat=&quot;item in items&quot;\n    collection-item-height=&quot;75&quot;\n    collection-item-width=&quot;&#039;100%&#039;&quot;&gt;\n    {{item}}\n  &lt;\/ion-item&gt;\n&lt;\/ion-list&gt;\n<\/code><\/pre>\n<p>With the new syntax, height and width are optional:<\/p>\n<pre><code class=\"html\">&lt;ion-list&gt;\n  &lt;ion-item collection-repeat=&quot;item in items&quot;&gt;\n    {{item}}\n  &lt;\/ion-item&gt;\n&lt;\/ion-list&gt;\n<\/code><\/pre>\n<p>As you can see, in the common case where every item is the same size, you don&#8217;t have to provide dimensions at all. Collection repeat now shines as a drop-in replacement for ngRepeat.<\/p>\n<p>See <a href=\"http:\/\/ionicframework.com\/docs\/nightly\/api\/directive\/collectionRepeat\">the documentation<\/a> for more information.<\/p>\n<h3 id=\"theproblemswiththefirstiteration\">The Problems with the First Iteration<\/h3>\n<p>The old collection repeat assumed that, in every case, any item in the list could be uniquely sized. This assumption required us to recalculate every single item&#8217;s width and height whenever the scroll view resized. This expensive operation caused unacceptable lag when loading or rotating the phone.<\/p>\n<p>When we took another look at UITableView, we hit upon a better solution. UITableView accepts an &#8216;estimatedHeight&#8217; for every element in the list and uses that to estimate the size of the scrollView. Then, while the user scrolls down, each item&#8217;s dimensions are calculated on demand, and the size of the scrollView adjusts to reflect the actual dimensions.<\/p>\n<p>We realized how much this could help performance and went into refactor mode.<\/p>\n<h3 id=\"improvementsinthenew\">Improvements in the New<\/h3>\n<p>Instead of requiring the user to input estimatedHeight, we compute the dimensions of the first element in the list with getComputedStyle() and use that for the estimatedHeight and estimatedWidth.<\/p>\n<p>This lets us calculate dimensions lazily. We estimate that <code>scrollView.height === estimatedHeight * items.length<\/code> at the start, and as the user scrolls, we calculate the actual height of every element.<\/p>\n<p>We also made some optimizations in the rendering of items. For example, we now batch DOM operations on items by setting cssText. We also now digest items entering items one frame after positioning them.<\/p>\n<p>But the biggest optimization is in the calculation of dimensions. The new collection repeat has four possible &#8216;modes&#8217; it enters, the first being the most performant, and the last being the least performant:<\/p>\n<ol>\n<li><strong><a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L731-L759\">Static<\/a> <a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L719-L729\">List<\/a> Mode<\/strong>: This mode is entered when the height is given as a constant or not given at all, and the width is 100%. Here, we assume the height of every element is equal to the estimatedHeight. The math for this mode is simple and easy because every item has the same dimensions.<\/p>\n<\/li>\n<li>\n<p><strong><a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L731-L759\">Static<\/a> <a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L706-L717\">Grid<\/a> Mode<\/strong>: Similar to static list mode, except there are multiple items per row. This is still simple because every item has the same dimensions.<\/p>\n<\/li>\n<li>\n<p><strong><a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L761-L917\">Dynamic<\/a> <a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L719-L729\">List<\/a> Mode<\/strong>: This mode is entered when height is given as a dynamic expression, but width is still 100%. In this mode, every item potentially has a unique height. We get the computed height of the first item and use that to calculate the estimated size of the scrollView. Then, as the user scrolls, we lazily calculate the dimensions of the next items that should be shown.<\/p>\n<\/li>\n<li>\n<p><strong><a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L761-L917\">Dynamic<\/a> <a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L706-L717\">Grid<\/a> Mode<\/strong>: This is the most complicated mode and is entered when both height and width are dynamic expressions. It&#8217;s the same as dynamic list mode, except we also have to account for a potentially unique number of items appearing in each row.<\/p>\n<\/li>\n<\/ol>\n<p>Finally, each of the four modes can be entered in either <a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L665-L684\">vertical<\/a> or <a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/864b46aa818c3a230e77225ab704c16acbc93ac5\/js\/angular\/directive\/collectionRepeat.js#L684-L704\">horizontal<\/a> scrolling.<\/p>\n<p>The problem with the old repeater was that it was <em>always<\/em> in Dynamic Grid Mode and calculated all dimensions up front. This led to worse performance while scrolling, loading, <strong>and<\/strong> resizing.<\/p>\n<p>Now, even in the worst case of dynamic grid mode, collection repeat is more performant than ever.<\/p>\n<h3 id=\"moreperformanceopportunities\">More Performance Opportunities<\/h3>\n<p>The biggest remaining opportunity for more performance gain is in the iOS browser\u2019s rendering of images.<\/p>\n<p>Whenever you set the <code>src<\/code> of an <code>img<\/code> on iOS to a non-cached value, there is a freeze of anywhere from 50-150ms&#8211;even on an iPhone 6. In our tests, an Android 4.1 device with images in collection repeat outperforms an iPhone 6.<\/p>\n<p>Images are very commonly used with collection repeat, and we change the <code>src<\/code> of those images often as the user scrolls.<\/p>\n<p>We tried <a href=\"https:\/\/github.com\/driftyco\/ionic\/blob\/e18e30fce379875c78e51fb6bf1445d9419153ce\/js\/workers\/binaryToBase64.js\">creating a web worker<\/a> that fetches the image, converts it, and sends its base64 representation back to the UI thread. The image is then set to this base64 representation as a <a href=\"https:\/\/css-tricks.com\/data-uris\/\">data-uri<\/a>. This fixes half of the problem. If you set an <code>img src<\/code> to a data-uri that has been set before, it instantly gets the rendered image from the cache and shows it without lag. However, the first time a unique data-uri is set, there is a similar delay to that of a a normal <code>src<\/code>.<\/p>\n<p>This is still an improvement from normal src, which just doesn&#8217;t cache well at all.<\/p>\n<p>We&#8217;re experimenting with <a href=\"https:\/\/github.com\/driftyco\/ionic\/issues\/3194\">a few more tricks<\/a> to improve iOS performance, and plan to release them as a simple-to-use solution soon. We welcome your ideas.<\/p>\n<h3 id=\"wherewearenow\">Where We Are Now<\/h3>\n<p>The new collection repeat is better than ever, and easier to use than ever. Give it a try!<\/p>\n<p>View the documentation at <a href=\"http:\/\/ionicframework.com\/docs\/nightly\/api\/directive\/collectionRepeat\">http:\/\/ionicframework.com\/docs\/nightly\/api\/directive\/collectionRepeat<\/a>.<\/p>\n<p>Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Update: Collection Repeat has been replaced by Virtual Scroll in Ionic 3+. Collection repeat is Ionic&#8217;s buttery-smooth solution for scrolling huge lists. Inspired by iOS&#8217;s UITableView, we switch out elements as the user scrolls, so that only the minimum necessary elements are rendered. We released our first version of collection repeat last year and have [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"","publish_post_category":"","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"","discourse_permalink":"","wpdc_publishing_response":"","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[3],"class_list":["post-284","post","type-post","status-publish","format-standard","hentry","category-all","tag-ionic"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Collection Repeat: Estimate, Iterate, Improve - Ionic Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Collection Repeat: Estimate, Iterate, Improve\" \/>\n<meta property=\"og:description\" content=\"Update: Collection Repeat has been replaced by Virtual Scroll in Ionic 3+. Collection repeat is Ionic&#8217;s buttery-smooth solution for scrolling huge lists. Inspired by iOS&#8217;s UITableView, we switch out elements as the user scrolls, so that only the minimum necessary elements are rendered. We released our first version of collection repeat last year and have [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2015-03-02T01:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-07-25T19:25:52+00:00\" \/>\n<meta name=\"author\" content=\"Adam Bradley\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@adamdbradley\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Adam Bradley\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two\"},\"author\":{\"name\":\"Adam Bradley\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/92f90fb9d52e33d4241c5ac46477cd21\"},\"headline\":\"Collection Repeat: Estimate, Iterate, Improve\",\"datePublished\":\"2015-03-02T01:00:00+00:00\",\"dateModified\":\"2018-07-25T19:25:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two\"},\"wordCount\":1041,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"keywords\":[\"Ionic\"],\"articleSection\":[\"All\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two\",\"url\":\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two\",\"name\":\"Collection Repeat: Estimate, Iterate, Improve - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"datePublished\":\"2015-03-02T01:00:00+00:00\",\"dateModified\":\"2018-07-25T19:25:52+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Collection Repeat: Estimate, Iterate, Improve\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/92f90fb9d52e33d4241c5ac46477cd21\",\"name\":\"Adam Bradley\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/04\/adambradley-150x150.jpg\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/04\/adambradley-150x150.jpg\",\"caption\":\"Adam Bradley\"},\"sameAs\":[\"http:\/\/twitter.com\/adamdbradley\",\"https:\/\/x.com\/adamdbradley\"],\"url\":\"https:\/\/ionic.io\/blog\/author\/adam\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Collection Repeat: Estimate, Iterate, Improve - Ionic Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two","og_locale":"en_US","og_type":"article","og_title":"Collection Repeat: Estimate, Iterate, Improve","og_description":"Update: Collection Repeat has been replaced by Virtual Scroll in Ionic 3+. Collection repeat is Ionic&#8217;s buttery-smooth solution for scrolling huge lists. Inspired by iOS&#8217;s UITableView, we switch out elements as the user scrolls, so that only the minimum necessary elements are rendered. We released our first version of collection repeat last year and have [&hellip;]","og_url":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two","og_site_name":"Ionic Blog","article_published_time":"2015-03-02T01:00:00+00:00","article_modified_time":"2018-07-25T19:25:52+00:00","author":"Adam Bradley","twitter_card":"summary_large_image","twitter_creator":"@adamdbradley","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Adam Bradley","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two"},"author":{"name":"Adam Bradley","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/92f90fb9d52e33d4241c5ac46477cd21"},"headline":"Collection Repeat: Estimate, Iterate, Improve","datePublished":"2015-03-02T01:00:00+00:00","dateModified":"2018-07-25T19:25:52+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two"},"wordCount":1041,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"keywords":["Ionic"],"articleSection":["All"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two","url":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two","name":"Collection Repeat: Estimate, Iterate, Improve - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"datePublished":"2015-03-02T01:00:00+00:00","dateModified":"2018-07-25T19:25:52+00:00","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/collection-repeat-iteration-two"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/collection-repeat-iteration-two#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Collection Repeat: Estimate, Iterate, Improve"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/92f90fb9d52e33d4241c5ac46477cd21","name":"Adam Bradley","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/04\/adambradley-150x150.jpg","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2018\/04\/adambradley-150x150.jpg","caption":"Adam Bradley"},"sameAs":["http:\/\/twitter.com\/adamdbradley","https:\/\/x.com\/adamdbradley"],"url":"https:\/\/ionic.io\/blog\/author\/adam"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/284","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=284"}],"version-history":[{"count":0,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/284\/revisions"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=284"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=284"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=284"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}