<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alberto Cantú Gómez</title>
    <description>The latest articles on DEV Community by Alberto Cantú Gómez (@betocantu93).</description>
    <link>https://dev.to/betocantu93</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F54979%2F6ded02b4-cc1d-44fe-b8cd-030d8865df9a.jpeg</url>
      <title>DEV Community: Alberto Cantú Gómez</title>
      <link>https://dev.to/betocantu93</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/betocantu93"/>
    <language>en</language>
    <item>
      <title>How to build a search experience</title>
      <dc:creator>Alberto Cantú Gómez</dc:creator>
      <pubDate>Sun, 24 Jan 2021 20:52:39 +0000</pubDate>
      <link>https://dev.to/bengala/how-to-build-a-search-experience-3c3p</link>
      <guid>https://dev.to/bengala/how-to-build-a-search-experience-3c3p</guid>
      <description>&lt;h4&gt;
  
  
  TL;DR
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/elastic/search-ui" rel="noopener noreferrer"&gt;@elastic/search-ui&lt;/a&gt; separates the UI from the core search mechanics and functionality and thus allows you create any search experience using any modern framework, even plain Javascript! While the Elastic's default UI implementation is written for React apps, in this blog post I will show the Ember.js implementation using the new addon &lt;a href="https://github.com/bengala-tech/ember-search-ui" rel="noopener noreferrer"&gt;ember-search-ui&lt;/a&gt;. Here's the Ember.js &lt;a href="http://ember-search-ui.netlify.app/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Search experiences
&lt;/h1&gt;

&lt;p&gt;Most software shares these basic requirements: some way to create, edit, or delete something (usually forms) and a way to search for them, the classic CRUD. There is an ongoing challenge to help our users / customers / clients with the best ways to find the information they need in our applications. There are many ways to create these search experiences, here are some examples:&lt;/p&gt;

&lt;h3&gt;
  
  
  We have the all time classic, table with filters per column.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7wfvhsbqejwumsliy16x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7wfvhsbqejwumsliy16x.png" alt="Some website showing a table with information"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frbf92k8588ghdbi0y73c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frbf92k8588ghdbi0y73c.png" alt="Some website showing a filtered table with information"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a map to find locations on Airbnb
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk9i6ddh936c7fy2ljbnz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk9i6ddh936c7fy2ljbnz.png" alt="Airbnb search results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A Kanban board to list issues on Jira
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnda8981bujcs2fa919tj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnda8981bujcs2fa919tj.png" alt="Jira's kanban board"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Job listing sites, such as LinkedIn
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhbg9i8lew55ghmek6lqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhbg9i8lew55ghmek6lqr.png" alt="Linkedin job listing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Product listing on Amazon
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqdxvr2t8ukyk2nr7n2dq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqdxvr2t8ukyk2nr7n2dq.png" alt="An Amazon.com product listing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking closely we can notice how all these search UIs use these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Some kind of inputs or controls to filter/drill down search results&lt;/li&gt;
&lt;li&gt;How the search query actually made&lt;/li&gt;
&lt;li&gt;How the latests results look like&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the ever-growing world of open source we can find all kinds of libraries that help us build the classic table with filtering, paging, sorting and many more common functionalities, and &lt;em&gt;hey&lt;/em&gt;, who wants to recreate them? They are amazing, battle tested, work as described and actually help us a lot in most scenarios; Still, I always found myself hitting a wall trying to improve the UX of &lt;em&gt;this&lt;/em&gt; specific table in &lt;em&gt;this&lt;/em&gt; particular project with some specific control to filter the results, or even reusing these controls on many different pages to maybe display the results of one differently, which often leads to rewrites or re-implementation of the internals of the library, increasing the surface area for bugs and if the application is large enough it often feels like you have to maintain two or more source codes.&lt;/p&gt;

&lt;p&gt;This issue is even more noticeable when you move from project to project, between ecommerce sites and data-driven dashboards and more, the problem is that these libraries link the user interface with the search mechanics, wouldn't it be amazing if they were separated? so that we can implement the user interface of our choice (hopefully the best one for your users), using the same basic concepts. Enters &lt;a href="https://github.com/bengala-tech/ember-search-ui" rel="noopener noreferrer"&gt;ember-search-ui&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ember Search UI
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ember-search-ui&lt;/code&gt; is an ember-cli addon that uses the core package &lt;a href="https://github.com/elastic/search-ui" rel="noopener noreferrer"&gt;@elastic/search-ui&lt;/a&gt; to give us ember developers a way to create any search experience and reuse any part of it between different pages, projects and the most complex UX requirements.&lt;/p&gt;

&lt;p&gt;It consists of 2 main concepts. Keep in mind this is not a tutorial, it's just a small MVP presentation, so please check out the &lt;a href="https://github.com/elastic/search-ui/blob/master/ADVANCED.md" rel="noopener noreferrer"&gt;complete reference&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;config&lt;/code&gt;: a simple javascript object that implements at &lt;code&gt;onSearch&lt;/code&gt; method, this is like ember data adapter, where you would want to configure your API specifications, it is simple; takes the current state of the driver and waits for a new state, you can apply here whatever data transformation your application needs, for example, transform current filters to &lt;code&gt;json-api&lt;/code&gt;, add auth headers and more.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RequestState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ResponseState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;driver&lt;/code&gt;: the driver is a framework agnostic piece of state and the glue of everything, it has 3 core purposes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It exposes some actions with clear API to create new searches, via &lt;code&gt;driver.getActions()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It emits events to subscribers when there's a new state change produced by any of these actions, you can subscribe using &lt;code&gt;driver.subscribeToStateChanges(this.yourCallBack)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It holds the current results, filters and pagination, you can access the current state using &lt;code&gt;driver.getState()&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;With these two you can basically create any UI, &lt;code&gt;ember-search-ui&lt;/code&gt; gives you two simple components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;SearchProvider /&amp;gt;&lt;/code&gt;: Basically an easy way to instantiate a driver in any template.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;
  &lt;span class="nt"&gt;&amp;lt;SearchProvider&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;config=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Table&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;driver=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;driver&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;columns=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;columns&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/SearchProvider&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;WithSearch /&amp;gt;&lt;/code&gt;: This component &lt;code&gt;subscribesToChanges&lt;/code&gt; to any driver passed it as argument, you'd also need to provide a function &lt;code&gt;@mapContextToProps&lt;/code&gt; that takes the current state and actions object and return the portion of the state and actions to set into the state of &lt;em&gt;this&lt;/em&gt; component, this way we avoid whole tree re-renders, &lt;code&gt;(stateAndActions) =&amp;gt; subsetOfStateAndActions&lt;/code&gt;, for easy usage this addon comes with a helper &lt;code&gt;map-context-to-props&lt;/code&gt; which receives the keys of the state or actions this component uses.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A couple of examples using the popular &lt;code&gt;ember-paper&lt;/code&gt; for material styles.&lt;/p&gt;

&lt;h4&gt;
  
  
  A search input component, lets call it &lt;code&gt;&amp;lt;SearchBox /&amp;gt;&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;  &lt;span class="c"&gt;{{!  search-box.hbs }}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;WithSearch&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;driver=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@driver&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;mapContextToProps=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;map-context-to-props&lt;/span&gt; 
      &lt;span class="s2"&gt;"searchTerm"&lt;/span&gt; 
      &lt;span class="s2"&gt;"setSearchTerm"&lt;/span&gt;
    &lt;span class="k"&gt;}}&lt;/span&gt; 
  &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PaperInput&lt;/span&gt; 
      &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt;
      &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Start searching..."&lt;/span&gt;
      &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;searchTerm&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
      &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;setSearchTerm&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/WithSearch&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Displaying results component, let's call it &lt;code&gt;&amp;lt;SearchResults /&amp;gt;&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;  &lt;span class="c"&gt;{{!  search-results.hbs }}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;WithSearch&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;driver=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@driver&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;mapContextToProps=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;map-context-to-props&lt;/span&gt; &lt;span class="s2"&gt;"results"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
  &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PaperList&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;each&lt;/span&gt; &lt;span class="nv"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;results&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;PaperItem&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"md-2-line"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;img&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"md-avatar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"md-list-item-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;resul&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;price&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/PaperItem&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;each&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/PaperList&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/WithSearch&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maybe we want to add an slider which filters down the products&lt;/p&gt;

&lt;h4&gt;
  
  
  Filter Slider component, lets call it &lt;code&gt;&amp;lt;Filters::Slider /&amp;gt;&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;  &lt;span class="c"&gt;{{!  filters/slider.hbs }}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;WithSearch&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;driver=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@driver&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;mapContextToProps=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;map-context-to-props&lt;/span&gt; 
      &lt;span class="s2"&gt;"filters"&lt;/span&gt; 
      &lt;span class="s2"&gt;"addFilter"&lt;/span&gt; 
      &lt;span class="s2"&gt;"removeFilter"&lt;/span&gt; 
      &lt;span class="s2"&gt;"setFilter"&lt;/span&gt;
    &lt;span class="k"&gt;}}&lt;/span&gt; 
    &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;let&lt;/span&gt; 
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;find-by&lt;/span&gt; &lt;span class="s2"&gt;"field"&lt;/span&gt; &lt;span class="na"&gt;@filterField&lt;/span&gt; &lt;span class="nv"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;PaperSlider&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;min=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;max=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;100&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;firstObject&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;fn&lt;/span&gt; &lt;span class="nv"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;setFilter&lt;/span&gt; &lt;span class="na"&gt;@filterField&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;{{/if}}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/WithSearch&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrapping it up...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;SearchProvider&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;config=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;SearchBox&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;driver=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;driver&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Filters::Slider&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;driver=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;driver&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;fielterField=&lt;/span&gt;&lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;SearchResults&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;driver=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;driver&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/SearchProvider&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see we defined a complete unique search experience that is decoupled of any particulars, you could add or create any new filters (even a Map like Airbnb), display the results completely in a different way, a table perhaps? the possibilities are endless.&lt;/p&gt;

&lt;p&gt;I hope you liked the intro and it helps you to build amazing UIs for your users and helps you and your team have sanity while building these!&lt;/p&gt;

&lt;p&gt;Please take a look at &lt;a href="https://github.com/bengala-tech/ember-search-ui" rel="noopener noreferrer"&gt;ember-search-ui&lt;/a&gt;. There's an example for a more deep dive on ember specifics, and ping me on twitter if I can help you with anything.&lt;/p&gt;

&lt;p&gt;This addon is merely a port of the amazing work of &lt;a href="https://github.com/elastic/search-ui" rel="noopener noreferrer"&gt;@elastic/search-ui&lt;/a&gt;, so thanks to everyone on Elastic team for continually making these incredible open source work. Please refer to that repo for a complete reference of the APIs.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>ember</category>
      <category>elastic</category>
      <category>ux</category>
    </item>
    <item>
      <title>Handy Emberjs Globals for Developing</title>
      <dc:creator>Alberto Cantú Gómez</dc:creator>
      <pubDate>Sat, 20 Jun 2020 18:42:41 +0000</pubDate>
      <link>https://dev.to/betocantu93/handy-emberjs-globals-for-developing-4cgl</link>
      <guid>https://dev.to/betocantu93/handy-emberjs-globals-for-developing-4cgl</guid>
      <description>&lt;h4&gt;
  
  
  TL;DR
&lt;/h4&gt;

&lt;p&gt;Here are some handy globals you can use in your Ember.js app to debug faster, even without ember inspector&lt;/p&gt;

&lt;p&gt;If you would like to use the addon version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/betocantu93/ember-handy-globals/blob/master/README.md"&gt;ember-handy-globals&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also find it as &lt;a href="https://gist.github.com/betocantu93/631456f4e867bd4ef73e296524485ba5"&gt;gist&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;In this blog post you will learn how to add some nice globals to help you while developing your Ember.js app.&lt;/p&gt;

&lt;p&gt;One of the things that makes Ember.js ecosystem a breeze is the &lt;a href="https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi"&gt;Ember Inspector&lt;/a&gt;. Quite often as Ember.js devs we use it to grab the current controller, a service, the route current model or a component to debug it, even while writing this post I learned a few new things about the inspector, I've never used the Promises tab before 🤒, I love it!&lt;/p&gt;

&lt;p&gt;But from time to time quick fixes and checks starts to feel a little bit annoying. Specially in long debugging sessions and of course if you like autosaving files in VSCode, like I do.&lt;/p&gt;

&lt;p&gt;Also from time to time you need a few ember objects in the console to debug correctly, or build up some complex object, so having the &lt;code&gt;$E&lt;/code&gt; reserved for a particular inspector object is not necessarily enough.&lt;/p&gt;

&lt;p&gt;Another good reason to do so, is that sometimes you are testing your app in a browser that doesn't have ember-inspector available or installed, so this gives you a set of tools to handle this!&lt;/p&gt;

&lt;p&gt;Here is an excellent &lt;a href="https://guides.emberjs.com/release/ember-inspector/"&gt;Intro To Ember Inspector&lt;/a&gt; if you are not familiar 😄&lt;/p&gt;

&lt;h2&gt;
  
  
  Off we go, Global instance-initializer
&lt;/h2&gt;

&lt;p&gt;One of the files I keep bringing from project to project, is the so called &lt;code&gt;instance-initializer/globals.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ember&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;initializer&lt;/span&gt; &lt;span class="nx"&gt;globals&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here I quote the Ember.js &lt;a href="https://guides.emberjs.com/release/applications/"&gt;Application and Instances&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every Ember application is represented by a class that extends Application. This class is used to declare and configure the many objects that make up your app.&lt;/p&gt;

&lt;p&gt;As your application boots, it creates an ApplicationInstance that is used to manage its stateful aspects. This instance acts as the "owner" of objects instantiated for your app.&lt;/p&gt;

&lt;p&gt;Essentially, the Application defines your application while the ApplicationInstance manages its state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So in this instance-initializer, we receive the application instance, which I use to create or derivate globals that keeps me productive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//instance-initializer/globals.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;globals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  First we might want to ensure we only add these while on development
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//instance-initializer/globals.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolveRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config:environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;globals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Basic useful globals
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//instance-initializer/globals.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolveRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config:environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/**
      This basically exposes the application, pretty useful because
      you can use stuff like this from the console.

      App.lookup('route:some-route').actions.doSomething();
    */&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/*
      This will gives us access to the store easily, to make fast queries or checks!

      Fast and easy:
      var s = App.store.peekRecord('some-model', 1);

      App.store.createRecord('some-model', {name: 'Alberto'})

    */&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__container__&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;service:store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;globals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After using App.lookup() directly for some time, I found too time consuming to time the type of object I was looking for&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;service:some-service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;route:some-route&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So I started to add a function shorcuts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//instance-initializer/globals.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolveRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config:environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="c1"&gt;//to avoid making this too long&lt;/span&gt;

    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`service:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// or a shortcut for every ember types.&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;objects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;globals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;initialize&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;While it's almost the same typing, first its truly easier to type, but also, the chrome console autocompletes, so a win.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auth.projects&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;some-model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auth.projects&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the most recent addition who actually made me write this blogpost, my ultimate gift 😅.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//instance-initializer/globals.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolveRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config:environment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CurrentContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`controller:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;service:router&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;currentRouteName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`controller:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;service:router&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;currentRouteName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`route:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;service:router&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;currentRouteName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CurrentContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;globals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We create this &lt;code&gt;CurrentContext&lt;/code&gt; class just to have native getters, to avoid having to call methods. It uses the &lt;code&gt;currentRouteName&lt;/code&gt; from &lt;code&gt;Service Router&lt;/code&gt; to get the current model, route or controller, so this is effectively a super shortcut for quick debugging stuff!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//current route model!&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//current route controller!&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//current route route!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What do you think about these shortcuts? Do you plan to use some of them? do you have any secret one you would like to share to the #emberjs community? Please do!&lt;/p&gt;

&lt;p&gt;If you would like to use the addon version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/betocantu93/ember-handy-globals/blob/master/README.md"&gt;ember-handy-globals&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also find it as &lt;a href="https://gist.github.com/betocantu93/631456f4e867bd4ef73e296524485ba5"&gt;gist&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ember</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Understanding Ember.js Public Assets Path for Browser and Cordova 🗄</title>
      <dc:creator>Alberto Cantú Gómez</dc:creator>
      <pubDate>Sun, 07 Jun 2020 17:38:17 +0000</pubDate>
      <link>https://dev.to/betocantu93/understanding-ember-js-public-assets-path-jj8</link>
      <guid>https://dev.to/betocantu93/understanding-ember-js-public-assets-path-jj8</guid>
      <description>&lt;h1&gt;
  
  
  Correction
&lt;/h1&gt;

&lt;p&gt;Thanks to &lt;a href="https://twitter.com/neojp"&gt;@neojp&lt;/a&gt; for his response &lt;a href="https://twitter.com/neojp/status/1269708074387939329"&gt;https://twitter.com/neojp/status/1269708074387939329&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I wrote this blog post without fully understanding the reasoning behind ember rewriting urls, fingerprinting and the TIL &lt;code&gt;&amp;lt;base /&amp;gt;&lt;/code&gt; tag, the simple and pretty straightforward solution to handle urls in both environments is to just add a &lt;code&gt;&amp;lt;base href={{rootURL}} /&amp;gt;&lt;/code&gt; in /app/index.html. I started using ember around 2.8, and I just found out what was the actual purpose of &lt;code&gt;baseURL&lt;/code&gt; in &lt;code&gt;config/environment.js&lt;/code&gt; and well, it got deprecated back in ember-cli 2.7, this practically changes how assets urls work in ember. Surprisingly &lt;a href="https://blog.emberjs.com/2016/04/28/baseurl"&gt;the recommended solution&lt;/a&gt; is to use&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;fully-qualified paths or a root-relative URL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! Old: (with baseURL and/or &amp;lt;base /&amp;gt; tag) }}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"assets/images/logo.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;{{! New: }}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/assets/images/logo.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you want to add the base tag, which I do recommend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- index.html --&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{rootURL}}"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This means that all non fully-qualified urls will get the rootURL of your config/environment.js prefixed by the browser.&lt;/p&gt;

&lt;p&gt;I think this is not really that well documented in Ember.js guides or Ember.js tutorial. New ember apps starts without the &lt;code&gt;&amp;lt;base /&amp;gt;&lt;/code&gt; tag, thus won't seamlessly work in the browser and in &lt;a href="http://corber.io"&gt;corber&lt;/a&gt;, because while running in cordova you can't simply point relative paths i.e &lt;code&gt;/asset/img/photo.jpg&lt;/code&gt;, all assets are placed elsewhere. The silver bullet is the &lt;code&gt;&amp;lt;base /&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;The helper proposed in this blog post is useful if you want another way around adding the &lt;code&gt;&amp;lt;base /&amp;gt;&lt;/code&gt; tag in your index.html because of some unexpected &lt;a href="https://stackoverflow.com/questions/1889076/what-are-the-recommendations-for-html-base-tag"&gt;behaviors&lt;/a&gt; around it or just to follow(?) the current ember-cli blueprint... the following helper gives you the correct relative path in templates and JS regardless if you are running in cordova or the browser, you just gotta make sure this helper runs after &lt;a href="https://cordova.apache.org/docs/es/latest/cordova/events/events.deviceready.html"&gt;cordova.deviceReady&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// helpers/public-url&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Helper&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ember/component/helper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-get-config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;IS_READY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;publicUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;FastBoot&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cordova&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;IS_READY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cordova&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;applicationDirectory&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;www/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rootURL&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rootURL&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Helper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;cordovaEvents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-cordova/events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

  &lt;span class="na"&gt;deviceReadyObserver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cordovaEvents.deviceready&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
      &lt;span class="nx"&gt;IS_READY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;recompute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;]){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;publicUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And use it in templates like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;public-url&lt;/span&gt; &lt;span class="s2"&gt;"assets/images/teams/{{team.short_name}}.png"&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;'s flag"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"width: 50px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And in js like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;publicUrl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../helpers/public-url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;publicUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`assets/images/teams/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;team&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;short_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.png`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>ember</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Recursion in Ember.js Templates, case factorial(n)</title>
      <dc:creator>Alberto Cantú Gómez</dc:creator>
      <pubDate>Fri, 01 May 2020 21:06:12 +0000</pubDate>
      <link>https://dev.to/betocantu93/recursion-in-ember-js-templates-case-factorial-n-2jjm</link>
      <guid>https://dev.to/betocantu93/recursion-in-ember-js-templates-case-factorial-n-2jjm</guid>
      <description>&lt;h1&gt;
  
  
  Factorial
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;The&lt;/strong&gt; &lt;strong&gt;product&lt;/strong&gt; &lt;strong&gt;of&lt;/strong&gt; &lt;strong&gt;(= the result)&lt;/strong&gt; &lt;strong&gt;a&lt;/strong&gt; whole number &lt;strong&gt;and all the&lt;/strong&gt; whole numbers &lt;strong&gt;below it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Four factorial (4 x 3 x 2 x 1) is 24&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Goal
&lt;/h2&gt;

&lt;p&gt;Create a Factorial component which will calculate the answer recursively using only &lt;strong&gt;templates&lt;/strong&gt; and &lt;strong&gt;helpers&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 - How to calculate factorial using recursion
&lt;/h3&gt;

&lt;p&gt;Let's first see how to solve the factorial using recursion in JS land&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//when we get to the base case it returns 1.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//Recursion, we are calling factorial again with n-1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2 - Factorial Component
&lt;/h3&gt;

&lt;p&gt;Let's write the same, but using ember templates (this is not the final version), it's purpose is to show similarity with the js equivalent&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;eq&lt;/strong&gt; is a helper to  test equality &lt;code&gt;{{eq "hola" "bye"}} = false&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;sub&lt;/strong&gt; is a helper to subsctract something &lt;code&gt;{{sub 5 1}} = 4&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mult&lt;/strong&gt; is a helper to multiply something &lt;code&gt;{{mult 5 2}} = 10&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can find these helpers and more at&lt;/p&gt;

&lt;p&gt;&lt;a href="https://shipshapecode.github.io/ember-math-helpers/docs/playground"&gt;ember-math-helpers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jmurphyau/ember-truth-helpers"&gt;ember-truth-helpers&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! factorial.hbs}}&lt;/span&gt;
&lt;span class="k"&gt;{{#if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;eq&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
    &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="c"&gt;{{! when we get the base case, it retuns 1.}}&lt;/span&gt;
&lt;span class="k"&gt;{{else}}&lt;/span&gt;
    &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;return&lt;/span&gt; 
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mult&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;Factorial&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;sub&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; /&amp;gt;)
  }} &lt;span class="c"&gt;{{! Recursion, we're calling factorial again with n-1}}&lt;/span&gt;
&lt;span class="k"&gt;{{/if}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This algorithm seems correct, and conceptually this is the same as the &lt;code&gt;JS&lt;/code&gt; equivalent, but it has some errors.&lt;/p&gt;

&lt;p&gt;First, when you want to &lt;code&gt;return&lt;/code&gt; something in ember templates, you use &lt;code&gt;yield&lt;/code&gt; keyword instead of &lt;code&gt;return&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! factorial.hbs}}&lt;/span&gt;
&lt;span class="k"&gt;{{#if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;eq&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
    &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="c"&gt;{{! when we get to the base case, return 1.}}&lt;/span&gt;
&lt;span class="k"&gt;{{else}}&lt;/span&gt;
    &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt; 
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mult&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;Factorial&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;sub&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; /&amp;gt;)
  }} &lt;span class="c"&gt;{{! Recursion, we're calling factorial again with n-1}}&lt;/span&gt;
&lt;span class="k"&gt;{{/if}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By last, this is the difficult part where we find ourselves a bit lost, whereas it can actually yield or "return" a component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;component&lt;/span&gt; &lt;span class="s1"&gt;'factorial'&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;sub&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This component would not actually run, so the client of this code would need to do something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;10&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Factorial&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;number=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;Factorial&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Factorial&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/Factorial&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;let&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Which actually does nothing because we are never getting the answer.&lt;/p&gt;

&lt;p&gt;Here's the solution&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! factorial.hbs}}&lt;/span&gt;
&lt;span class="k"&gt;{{#if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;eq&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
    &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="c"&gt;{{! when we get to the base case, return 1.}}&lt;/span&gt;
&lt;span class="k"&gt;{{else}}&lt;/span&gt;
    &lt;span class="c"&gt;{{! Recursion, we are calling factorial component again with n-1}}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Factorial&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;number=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;sub&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
        &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mult&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;/Factorial&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;{{/if}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By yielding the multiplication of the current number times the response of another factorial(n-1) &lt;strong&gt;(inside the  block)&lt;/strong&gt;, we just covered the recursion. &lt;/p&gt;

&lt;p&gt;Here's the final Component "API".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;10&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;Factorial&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;number=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;! is &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;answer&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/Factorial&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;let&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally if we wish to visually display or render the recursive tree nicely, we could use the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; tag&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! factorial.hbs}}&lt;/span&gt;
&lt;span class="k"&gt;{{#&lt;/span&gt;&lt;span class="nn"&gt;let&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;array&lt;/span&gt; &lt;span class="s2"&gt;"red"&lt;/span&gt; &lt;span class="s2"&gt;"blue"&lt;/span&gt; &lt;span class="s2"&gt;"yellow"&lt;/span&gt; &lt;span class="s2"&gt;"orange"&lt;/span&gt; &lt;span class="s2"&gt;"pink"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nv"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="k"&gt;}}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background-color: &lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;object-at&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mod&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;sub&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;colors&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;{{#if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;eq&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
      &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="c"&gt;{{! when we get to the base case, return 1.}}&lt;/span&gt;
    &lt;span class="k"&gt;{{else}}&lt;/span&gt;
      &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@number&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; * factorial(&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;sub&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;)
      &lt;span class="c"&gt;{{! Recursion, we are calling factorial component again with n-1}}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;Factorial&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;number=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;sub&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
        &lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mult&lt;/span&gt; &lt;span class="na"&gt;@number&lt;/span&gt; &lt;span class="nv"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
      &lt;span class="nt"&gt;&amp;lt;/Factorial&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;{{/if}}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;{{/&lt;/span&gt;&lt;span class="nn"&gt;let&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dJyzoY5z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/r1ud21vgr9c8gvez6fkc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dJyzoY5z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/r1ud21vgr9c8gvez6fkc.png" alt="HTML View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the Ember inspector would look like this!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YDypcuch--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ipyff2k1mgl9tzhpxjrs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YDypcuch--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ipyff2k1mgl9tzhpxjrs.png" alt="Ember Inspector"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Here's a &lt;a href="https://recursive-factorial.surge.sh/"&gt;demo&lt;/a&gt;
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Why using templates instead of pure JavaScript?
&lt;/h2&gt;

&lt;p&gt;Ember templates rerender automatically when some value &lt;em&gt;(must be a &lt;code&gt;@tracked&lt;/code&gt; decorated property or an Ember.Object property)&lt;/em&gt; referenced in them, changes, this means that we could have an observed recursion. Our components tree can actually make smart decisions, so we can have a recursive logical tree that recalculates on arbitrary events, like clicking a button that might increment a property referenced by the template thus, triggering a rerender, etc. In other words, we can take advantage that ember templates already know exactly when to "rerender" as effective observer(s) of our recursion.&lt;/p&gt;

&lt;p&gt;You can of course solve these kind of problems also by adding observers in your components js class, or some other technique, which is usually way more verbose and would need some kind of hand wiring observers via &lt;code&gt;addObserver&lt;/code&gt; and then destroy them if the component gets destroyed.&lt;/p&gt;

&lt;h6&gt;
  
  
  About our use case
&lt;/h6&gt;

&lt;p&gt;This short blog post is about something I learned with my colleagues today while trying to calculate a value using a recursive technique on ember &lt;em&gt;&lt;em&gt;templates&lt;/em&gt;&lt;/em&gt;, which we finally did not use.&lt;/p&gt;

&lt;p&gt;Our use case was very strange and specific, we wanted a way to reduce a complex nested object (flatten an object) to create a &lt;strong&gt;live&lt;/strong&gt; array, so a client could iterate on. This "live" stuff would use the &lt;strong&gt;templates&lt;/strong&gt; "observavility" and automatic "rerenders" to effectively observe an external dependency, i.e. &lt;code&gt;DS.Model&lt;/code&gt; instance, because the flattening process has logical branches based on the &lt;code&gt;DS.Model&lt;/code&gt; properties actual values (referenced in the templates). Since any change in &lt;code&gt;DS.Model&lt;/code&gt; would cause a complete rerender, and the performance implications were dire, we chose other path.&lt;/p&gt;

</description>
      <category>ember</category>
      <category>javascript</category>
      <category>recursion</category>
      <category>maths</category>
    </item>
    <item>
      <title>Auto Save en Ember Octane 💾</title>
      <dc:creator>Alberto Cantú Gómez</dc:creator>
      <pubDate>Sat, 25 Apr 2020 22:12:12 +0000</pubDate>
      <link>https://dev.to/betocantu93/auto-save-en-ember-octane-322f</link>
      <guid>https://dev.to/betocantu93/auto-save-en-ember-octane-322f</guid>
      <description>&lt;p&gt;Este es un blog-post para desarrolladores de JavaScript y sobre todo de Ember.js, es avanzado por lo cual supone que tienes conocimiento de JavaScript e idealmente de Ember.js.&lt;/p&gt;

&lt;h6&gt;
  
  
  Una nota antes de empezar
&lt;/h6&gt;

&lt;p&gt;Este será el primer blog que hago en español, considero que falta mucho contenido sobre tecnología en español y específicamente de &lt;a href="https://emberjs.com"&gt;&lt;em&gt;Ember.js moderno&lt;/em&gt;&lt;/a&gt;, el cual forma parte de mi día a día como desarrollador. Para no forzar traducciones cuando complican el texto, voy a dejar un glosario al final cuando considere necesario, sin embargo si tienes alguna duda siempre puedes localizarme en twitter &lt;a href="https://twitter.com/betocantu93"&gt;@betocantu93&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  ¿Qué es Ember.js?
&lt;/h5&gt;

&lt;p&gt;Bueno primero no me gustaría suponer que conoces Ember.js, &lt;a href="https://emberjs.com"&gt;&lt;em&gt;Ember.js&lt;/em&gt;&lt;/a&gt; es un framework de JavaScript para construir aplicaciones web robustas y escalables, sus principales atractivos son:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Baterías incluídas, un proyecto nuevo de Ember.js incluye entre muchas otras cosas:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Build pipeline (olvídate de estar configurando Rollup, Webpack o demás)&lt;/li&gt;
&lt;li&gt;Un router&lt;/li&gt;
&lt;li&gt;Data Layer (Ember Data, para manejar los datos en tu proyecto)&lt;/li&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Las convenciones, permiten desarrollar un proyecto web escalable de manera ordenada y predecible, un desarrollador de Ember.js puede brincar de proyecto en proyecto sin ningún problema ya que se sentirá en casa con código muy familiar.&lt;/li&gt;
&lt;li&gt;Addons, son como "plugins" que le puedes añadir a tu aplicación, paquetes de npm que gracias a las convenciones de Ember.js, pueden añadir código que aumente la funcionalidad de tu aplicación de manera asertiva, incremental y poderosa.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;¿Quieres utilizar graphql y apollo? &lt;a href="https://github.com/ember-graphql/ember-apollo-client"&gt;ember-apollo-client&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;¿Quieres animaciones? &lt;a href="https://ember-animation.github.io/ember-animated/"&gt;ember-animated&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;¿Quieres material design? &lt;a href="https://miguelcobain.github.io/ember-paper/"&gt;ember-paper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;¿Quieres soportar muchos lenguajes? &lt;a href="https://github.com/ember-intl/ember-intl"&gt;ember-intl&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;¿Quieres mapas? &lt;a href="https://kturney.github.io/ember-mapbox-gl/docs"&gt;ember-mapbox-gl&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt; Código Libre, Ember.js es desarrollado por la comunidad alrededor del 🌍, todos pueden contribuír y no hay ninguna empresa entorpeciendo con sus intereses.&lt;/li&gt;
&lt;li&gt; Fuerte versionamiento, Ember.js tiene un compromiso con no romper las aplicaciones de todos entre versiones ya que tiene un fuerte sistema de versionamiento y de ir removiendo código obsoleto, puedes estar seguro de que tu aplicación podrá migrar a nuevas versiones sin tener que reescribirla a através de los años.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Auto Save 💾
&lt;/h1&gt;

&lt;h6&gt;
  
  
  Pero, ¿qué es &lt;em&gt;Auto Save&lt;/em&gt;?
&lt;/h6&gt;

&lt;p&gt;Auto save es guardar un &lt;em&gt;modelo&lt;/em&gt; o &lt;em&gt;documento&lt;/em&gt; cada vez que ciertos eventos sucedan, esto tiene dos beneficios importantes&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mejora la experiencia de usuario al no tener que estar dando clic en 💾 a cada cambio en documentos o formularios grandes&lt;/li&gt;
&lt;li&gt;Puede evitar el miedo de perder la información&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Basta de intros, empecemos a ver código.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nuestro modelo &lt;code&gt;Person&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;attr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ember-data/model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PersonModel&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt; &lt;span class="nx"&gt;birthday&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Primero que nada necesitamos un componente para nuestro formulario básico tradicional y lo iremos poco a poco mejorando, en este caso estaré utilizando &lt;code&gt;ember-paper&lt;/code&gt; por facilidad&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! components/edit-person/index.hbs }}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PaperForm&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onSubmit=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@onSubmit&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;Form&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Form&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mut&lt;/span&gt; &lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;firstName&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Form&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mut&lt;/span&gt; &lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lastName&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Form&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mut&lt;/span&gt; &lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;birthday&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;birthday&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Form&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;on-submit&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Guardar
    &lt;span class="nt"&gt;&amp;lt;/Form&lt;/span&gt;&lt;span class="err"&gt;.on-submit&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PaperForm&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Bueno y ¿cómo lo uso? Muy fácil, hay que suponer que el this.save es una función en el controller que se encarga de hacer &lt;code&gt;this.model.save&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! templates/person.hbs}}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;EditPerson&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;person=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onSubmit=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;save&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ahora la primer cosa que puede mejorar un poco para hacer más fácil el trabajo con estas tribialidades como crear un controller para definir una acción &lt;code&gt;this.save&lt;/code&gt; con &lt;code&gt;this.model.save&lt;/code&gt;, utilizando &lt;code&gt;ember-composable-helpers&lt;/code&gt; puedes simplemente utilizar el helper de &lt;code&gt;invoke&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! templates/person.hbs}}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;EditPerson&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;person=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onSubmit=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;invoke&lt;/span&gt; &lt;span class="s2"&gt;"save"&lt;/span&gt; &lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ahora, ¿cómo se vería nuestro componente de EditPerson si reemplazamos el botón de guardar por una función de autosave?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! components/edit-person/index.hbs }}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PaperForm&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;Form&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Form&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mut&lt;/span&gt; &lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;firstName&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onBlur=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@autoSave&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Form&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mut&lt;/span&gt; &lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;lastName&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onBlur=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@autoSave&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Form&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;input&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mut&lt;/span&gt; &lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;birthday&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; 
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;birthday&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onBlur=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;@autoSave&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
   &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/PaperForm&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;De esta manera, cuando el usuario salga del input (onBlur) se estará ejecutando una función de autoSave, esto deja al que invoca el componente la decisión de cómo hacer el autosave.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! templates/person.hbs}}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;EditPerson&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;person=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;autoSave=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;invoke&lt;/span&gt; &lt;span class="s2"&gt;"save"&lt;/span&gt; &lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Esto funciona, pero qué tal si el usuario utilizara &lt;code&gt;[ Tab ]&lt;/code&gt; para moverse por tu forma, estarías haciendo muchos &lt;code&gt;{{invoke "save" this.model}}&lt;/code&gt; probablemente innecesarios, para eso vamos a introducir un nuevo concepto&lt;/p&gt;

&lt;h4&gt;
  
  
  El Componente AutoSave
&lt;/h4&gt;

&lt;p&gt;Este componente nos ayudará a encapsular lógica de auto save y que vamos a poder "inyectarla" en cualquier template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/auto-save/index.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GlimmerComponent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@glimmer/component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-concurrency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AutoSaveComponent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;GlimmerComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//Si hay un error, de permisos, por ejemplo.&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rollbackAttributes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="c1"&gt;//manera fácil de no tener que guardar track de el dirtinessde las relaciones&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;keepLatest&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="nx"&gt;autoSaveTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Para evitar que nuestra función se dispare de maneras no controladas, podemos utilizar el addon &lt;code&gt;ember-concurrency&lt;/code&gt; que de una manera muy declarativa nos deja implementar el patrón de &lt;code&gt;debouncing&lt;/code&gt;, que obliga a que una función no sea llamada otra vez hasta que cierto tiempo haya pasado sin ser llamada otra vez, algo así como "Ejecuta esta función, solo si 500 milisegundos han pasado sin que se haya vuelto a llamar". &lt;/p&gt;

&lt;p&gt;¿Pero cómo utilizar este componente? Necesitamos primero hacer &lt;code&gt;yield&lt;/code&gt; de nuestra &lt;code&gt;task&lt;/code&gt; en nuestro template&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! components/auto-save/index.hbs }}&lt;/span&gt;
&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;perform&lt;/span&gt; &lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;autoSaveTask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finalmente, podemos utilizarlo con facilidad.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;AutoSave&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;model=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="na"&gt;autoSave&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;EditPerson&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;person=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;model&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;autoSave=&lt;/span&gt;&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;autoSave&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/AutoSave&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Me gusta este patrón en el cual abstraemos una funcionalidad a un componente, de esa manera podemos reutilizar código sin tener que utilizar &lt;code&gt;Mixins&lt;/code&gt;, son como &lt;code&gt;Mixins&lt;/code&gt; pero en "Render", interesante.&lt;/p&gt;

&lt;p&gt;Todavía tenemos algunos casos extremos a resolver&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Con el diseño actual, podemos estar ejecutando guardar aún y cuando no es necesario, ya que puede que no haya cambiado el valor, por lo tanto innecesario.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ember-concurrency&lt;/code&gt; cancela todas las &lt;code&gt;task&lt;/code&gt; cuando el objeto en el que viven es destruído. Como estamos manejando la creación de la &lt;code&gt;task&lt;/code&gt; en un componente, hay un caso extremo en donde el usuario podría hacer cambios en el modelo y cambiarse de ruta por el algún botón de tu UI, mientras aún se está ejectuando la estrategia de &lt;code&gt;debounce&lt;/code&gt; (esperando los 500ms) en la task, por ende puede ser que &lt;em&gt;no se complete y guarde nuestro modelo&lt;/em&gt; lo que representa posible pérdida de información y es intolerable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para resolver el punto 1, podemos agregar un check antes del &lt;code&gt;debouncing&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/auto-save/index.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GlimmerComponent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@glimmer/component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-concurrency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AutoSaveComponent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;GlimmerComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="cm"&gt;/*
        @param {Boolean} checkIfDirtyAttributes Verificar si el modelo tiene cambios 
    */&lt;/span&gt;
  &lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkIfDirtyAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;checkIfDirtyAttributes&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hasDirtyAttributes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//Si hay un error, de permisos, por ejemplo.&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rollbackAttributes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;//manera fácil de no tener que guardar track de el dirtinessde las relaciones&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;keepLatest&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="nx"&gt;autoSaveTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ember Data lleva un registro en todo momento si el atributo de un modelo cambió, pero no de si sus &lt;em&gt;relaciones&lt;/em&gt; han cambiado, entonces si quieres guardar por que se cambió una relación, puedes ignorar los atributos, &lt;code&gt;@onChange={{fn @autoSave false}}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Para resolver el punto 2, podemos mover el task del componente al modelo en sí.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;attr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ember-data/model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-concurrency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PersonModel&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Model&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;attr&lt;/span&gt; &lt;span class="nx"&gt;birthday&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/*
        @param {Boolean} shouldRun puede o no ejecutarse?
    */&lt;/span&gt;
  &lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shouldRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;},&lt;/span&gt; &lt;span class="nx"&gt;checkIfDirtyAttributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nx"&gt;shouldRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;typeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shouldRun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;shouldRun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shouldRun&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;shouldRun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkIfDirtyAttributes&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hasDirtyAttributes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//Si hay un error, de permisos, por ejemplo.&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rollbackAttributes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="c1"&gt;//manera fácil de no tener que guardar track de el dirtinessde las relaciones&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;keepLatest&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="nx"&gt;autoSaveTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Como podemos ver, reemplazamos el &lt;code&gt;this.args.model&lt;/code&gt; por &lt;code&gt;this&lt;/code&gt;, porque ahora esta &lt;code&gt;task&lt;/code&gt; vive en un modelo en sí, además para hacerla un poco más flexible y reutilizable, permitimos que nos envién una función que retorne un boolean o un boolean en sí para saber si correr o no el &lt;code&gt;task&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tenemos que actualizar nuestro componente &lt;code&gt;AutoSave&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/auto-save/index.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GlimmerComponent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@glimmer/component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timeout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-concurrency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AutoSaveComponent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;GlimmerComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;shouldRun&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/*
        Podríamos tener aquí cualquier validación extra, 
        por ejemplo, permisos de ember-can
    */&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight handlebars"&gt;&lt;code&gt;&lt;span class="c"&gt;{{! components/auto-save/index.hbs }}&lt;/span&gt;
&lt;span class="k"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;perform&lt;/span&gt; &lt;span class="na"&gt;@model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;autoSaveTask&lt;/span&gt; &lt;span class="nv"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;shouldRun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Al final, nuestro componente &lt;code&gt;&amp;lt;AutoSave /&amp;gt;&lt;/code&gt; se puede decir que es un helper / mixin que funciona como middleware para ayudarnos a ejecutar la &lt;code&gt;task&lt;/code&gt; en nuestro modelo.&lt;/p&gt;

&lt;p&gt;Es un patrón algo complejo, pero permite agregar agregar fácil funcionalidad de autoSave a cualquier formulario de manera sencilla, ideal para aplicaciones modernas.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>ember</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
