<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Jen Weber]]></title><description><![CDATA[Jen Weber]]></description><link>https://jenweber.dev/rss.xml</link><generator>ember-casper-template</generator><lastBuildDate>Mon, 23 Aug 2021 15:38:45 GMT</lastBuildDate><atom:link href="https://jenweber.dev/rss.xml" rel="self" type="application/rss+xml"/><item><title><![CDATA[Building an Octane-style input in Ember]]></title><description><![CDATA[<p>Building a text input from scratch in&nbsp;Ember.js
Ember.js provides the <code>&lt;Input /&gt;</code> component out of the box, but sometimes it falls short of a developer's needs for functionality, data flow, and accessibility. In this tutorial, you'll learn how to use Ember's APIs and patterns to interact with native input elements. Even if you never need to make your own input, you'll leave with a better understanding of how to use the DOM within an Ember app.</p>
<h2 id="whymakeyourownnbspinput">Why make your own&nbsp;input?</h2>
<p>In Ember Octane, there's a heavy emphasis on data down, actions up for interacting with the DOM. However, the built-in <code>&lt;Input /&gt;</code> component follows the old two-way-bindings pattern. This is a fine and ok thing to do; I still use Input in all my apps! I really like two-way-binding for inputs! However, it can be a bit counterintuitive to do things like form validation in the Octane style, when you are still using the built-in Input.</p>
<p>Another reason to make your own input is for accessibility reasons. Unfortunately, every popular front-end framework has its share of accessibility problems. Although Ember is doing better than other frameworks in some regards, there are still some things to work through. If you make your own input component, you can programatically enforce that whenever a developer on your team uses it, they pass in some label text.</p>
<p>Lastly, Input has some confusing and inconsistent behavior, since it has to be backwards-compatible. It's in need of a rewrite, but that can't happen without introducing breaking changes into the framework. So, for now, we're stuck with it, at least until someone writes an RFC, gets it accepted, and does the work.</p>
<h2 id="nativeinputnbspbasics">Native input&nbsp;basics</h2>
<p>To understand Ember's <code>&lt;Input /&gt;</code>, it helps to understand what a normal, native input can do, and what it needs in order to work well for all users. This information will be important to know when we build our own input.</p>
<h3 id="1inputsandlabelsareinseparablepals">1. inputs and labels are inseparable pals.</h3>
<p>Whenever you create an input, it ought to have an associated label. There are two common styles for this:</p>
<pre><code class="html language-html">&lt;!-- put the input inside the label --&gt;
&lt;label&gt;
  First Name
  &lt;input type="text" /&gt;
&lt;/label&gt;

&lt;!-- reference the label by its id--&gt;
&lt;label for="input-first-name"&gt;
  First Name
&lt;/label&gt;
&lt;input id="input-first-name" type="text" /&gt;
</code></pre>
<p>In the second example, you have to be very careful to not re-use the ID assigned to the inputs and labels.</p>
<h3 id="2inputshavemanydifferentpossibleattributes">2. inputs have many different possible attributes</h3>
<p>A native input can have many different attributes, such as required,aria-labelled-by, id, class, type, disabled, and more. See the input docs on MDN for the full list.</p>
<h3 id="3inputshaveautomaticaccessibilitybehavior">3. inputs have automatic accessibility behavior</h3>
<p>Whenever you use the label and input elements, you get some excellent features automatically. They include keyboard navigation, screen reader support, and more. For this reason, you should always use the input element for inputs, not write a weird div that kinda acts like an input.</p>
<h3 id="4interactingwiththeinputfiresevents">4. Interacting with the input fires events</h3>
<p>Every input has events that fire whenever someone types in it. For example, there is an oninput event as well as onchange. Whenever you tie directly into events, it's important to check your browser compatibility needs. For example, oninput isn't supported in IE9.</p>
<h3 id="5followsemantichtmltoavoiduxtraps">5. Follow semantic HTML to avoid UX traps</h3>
<p>If you find it difficult to write valid markup and styling for an input, that's a red flag that your designs might have accessibility problems. This pain can save you from making big mistakes. For example, many developers work from designs where the label is hidden and placeholder text communicates the "label." This was popularized by Material Design, and it had some serious UX problems for all users. The MDN docs go as far as to say that you should not use placeholders at all, if you can avoid them.</p>
<h2 id="writingvanillajavascriptinputs">Writing vanilla JavaScript inputs</h2>
<p>In Ember, you can't use things like script tags in the templates, but it's still good to see how one would do this in a vanilla HTML and HTML + JavaScript settings. MDN has a good tutorial on this topic.
HTML only</p>
<p>A regular input can send data without any JavaScript at all if it is part of a form:</p>
<pre><code class="html language-html">&lt;form action="/api/names" method="post"&gt; 
  &lt;label for="name"&gt;Name:&lt;/label&gt;
  &lt;input type="text" id="name" name="user_name" /&gt;
  &lt;button type="submit"&gt;Send your message&lt;/button&gt;
&lt;/form&gt;
</code></pre>
<p>Clicking "submit" in the example above would send the name to the <code>/api/names</code> endpoint. So, if your app was hosted on <code>https://my-forms.example.com</code>, it would send a request to <code>https://my-forms.example.com/api/names</code>.</p>
<h2 id="htmljavascript">HTML + JavaScript</h2>
<p>You can write event listeners in JavaScript that respond to user interactions, and attach them to specific inputs.</p>
<p>For example, this HTML and JavaScript code will print the current value of the input to the console:</p>
<pre><code class="html language-html">&lt;label for="name"&gt;Name:&lt;/label&gt;
&lt;input type="text" id="name" name="user_name" /&gt;
&lt;script&gt;
  let myInput = document.querySelector('#name')
  myInput.addEventListener('input', function(e) {
    console.log(e.target.value)
  })
&lt;/script&gt;
</code></pre>
<p>You can even call an anonymous or specific function from the HTML, although this is considered bad practice for maintainability, testing, linting, etc in many codebases:</p>
<pre><code class="html language-html">&lt;input type="text" oninput="(function () {console.log('ew, gross') })()" /&gt;
&lt;input type="text" oninput="someOtherFunction()" /&gt;
</code></pre>
<h2 id="usingembersbuiltininputandtwowaynbspbinding">Using Ember's built-in Input and two-way&nbsp;binding</h2>
<p>Now let's take a look at what Ember's own built-in <code>&lt;Input /&gt;</code> does. The Input component, with a capital I, gives you some additional features and shortcuts compared to using a regular old input with a lowercase i. Most importantly, the input's value (what someone has typed into the box) can be "bound" to a property of the component. When the value changes as a user interacts, so does a property available to the JavaScript side. This is called "two way binding" and it's a very common pattern in pre-Octane Ember apps.</p>
<p>In our template, we can use the Input component and set its attributes. Don't forget to include a label! This example is for Ember 3.15+. Note that if you copy-paste this, you'll get errors until you also copy-paste the JavaScript.</p>
<pre><code class="handlebars language-handlebars">&lt;label for="name"&gt;
  Name
&lt;/label&gt;
&lt;Input type="text" id="name" value={{this.username}} /&gt;
&lt;button {{on "click" this.submitName}}&gt;
  Submit
&lt;/button&gt;
</code></pre>
<p>Now, in the JavaScript file for this component, we can indicate that username should be tracked for changes, and write an action for the Submit button. Just like in the vanilla HTML + JavaScript example, we'll print the input value to the console when someone clicks submit:</p>
<pre><code class="js language-js">import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class MyInputComponent extends Component {
  @tracked username = '';
  @action
  submitName() {
    console.log(this.username)
  }
}
</code></pre>
<p>Components are often meant to be reusable throughout an app, and it would be bad if we reused a component with hard-coded IDs. You might end up with multiple reused, non-unique ids on the page, which breaks your app. So, we can add in guidFor to generate a unique ID for our component to use. We'll save the results of guidFor to a new property called uniqueId, and use it in the markup:</p>
<pre><code class="handlebars language-handlebars">&lt;label for="name-{{this.uniqueId}}"&gt;
  Name
&lt;/label&gt;
&lt;Input type="text" id="name-{{this.uniqueId}}" value={{this.username}} /&gt;
&lt;button {{on "click" this.submitName}}&gt;
  Submit
&lt;/button&gt;
</code></pre>
<p>Component JavaScript that generates the unique ID:</p>
<pre><code class="js language-js">import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { guidFor } from '@ember/object/internals';
export default class MyInputComponent extends Component {
 uniqueId = guidFor(this);
 @tracked username = '';
 @action
  submitName() {
    console.log(this.username)
  }
}
</code></pre>
<h2 id="makingourowntextinputwithalowercaseilevelnbspone">Making our own text input with a lowercase i, level&nbsp;one</h2>
<p>Ok, so now you know the basics of how to interact with an input with a lowercase i using HTML and JavaScript, and also how to use Ember's Input with an uppercase I with two-way binding. Now it's time to make our own implementation that does not have two-way binding. This is a toy example, and in the following section, we'll add more features that help ensure that whenever it is used, it is accessible.
Let's start with the markup we had before, but make it a lowercase i and remove the value binding:</p>
<pre><code class="handlebars language-handlebars">&lt;label for="name-{{this.uniqueId}}"&gt;
  Name
&lt;/label&gt;
&lt;input type="text" id="name-{{this.uniqueId}}" /&gt;
&lt;button {{on "click" this.submitName}}&gt;
  Submit
&lt;/button&gt;
</code></pre>
<p>We have a few different paths that we could take, but let's start with the simplest. Ember provides the {{on}} helper for attaching JavaScript behavior to DOM events. Let's watch for the input event to fire, and write an action handleInput to call when it happens. Like before, you will need to copy-paste both the template and the JavaScript before it will work:</p>
<pre><code class="handlebars language-handlebars">&lt;label for="name-{{this.uniqueId}}"&gt;
  Name
&lt;/label&gt;
&lt;input type="text" id="name-{{this.uniqueId}}" {{on "input" this.handleInput}} /&gt;
&lt;button {{on "click" this.submitName}}&gt;
  Submit
&lt;/button&gt;
</code></pre>
<p>The component JavaScript, which will print the input's value to the console:</p>
<pre><code class="js language-js">import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { guidFor } from '@ember/object/internals';
export default class MyInputComponent extends Component {
 uniqueId = guidFor(this);
 @tracked username = '';

 @action
 handleInput(e) {
   console.log(e.data)
 }
 @action
  submitName() {
    console.log(this.username)
  }
}
</code></pre>
<p>Notice that our handleInput receives an argument, which I named e in the example above.  e is short for "event." I could have called it "bananas" if I wanted to instead. Anyway, whenever you use <code>{{on}}</code> to call a component's action, it always passes the event itself as the first argument. Our event is input. If we console.log e, we'd see a huge object for InputEvent. The InputEvent has a property called data that contains the value someone typed into the input!</p>
<p>Now that we have that input entry, we could do things like run validation and display helper text, set a property on this component, or call an action passed into the component from a parent. "Validation" in this context means something like checking to see if a user entry has the right format, and displaying a message to help them edit their information. For example, maybe the input has a character limit, and you want to let the user know that they are over it, and they need to do some editing before submitting.</p>
<h2 id="makingourowntextinputwithalowercaseilevelnbsptwo">Making our own text input with a lowercase i, level&nbsp;two</h2>
<p>In the previous section, we made a toy example. Now it's time to get to the real challenge - writing an input that requires you to have a label, and handles its ids gracefully and automatically.</p>
<p>I don't have a one-size-fits-all implementation for you (yet). However, my apps really only had three different flavors of inputs anyway - a basic text input, a text input with labels hidden because I had no choice in the design, and then non-text inputs whose attributes varied wildly. It's up to me to figure out which kind of input is used the most, and design an easily reusable component for that use case first. This would have the greatest impact in the shortest amount of time. Then, I should work through all the other cases throughout my app.</p>
<p>First, let's make our text input more generic, and have it rely on properties that are passed in. I'll explain the rationale for each one in a moment:</p>
<pre><code class="handlebars language-handlebars">&lt;label for="input-{{this.uniqueId}}" class={{@labelClass}}&gt;
  {{@labelText}}
&lt;/label&gt;
&lt;input type="text" id="input-{{this.uniqueId}}" class={{@inputClass}} {{on "input" @handleInput}} value={{@initialValue}} /&gt;
</code></pre>
<p>Next, let's define the JavaScript that provides the uniqueId and enforces that @labelText is passed in:</p>
<pre><code class="js language-js">import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { guidFor } from '@ember/object/internals';
export default class MyInputComponent extends Component {
 uniqueId = guidFor(this);

 constructor() {
   super(...arguments);
   if (!this.args.labelText) {
    assert('All inputs must have labels! MyInputComponent requires passing in labelText, i.e. &lt;MyInputComponent @labelText="First Name" /&gt;')
  }
 }
}
</code></pre>
<p>Here's my rationale for each attribute passed in, or not passed in:</p>
<p><code>labelText</code>: I will want to customize the label displayed every time I show this input
labelClass and inputClass: My input and its label need to look different depending on their context in my app.</p>
<p><code>initialValue</code>: I commonly need to pre-fill the input with the data that's available. For example, I might already know the username from when someone logged in. I call it initialValue to help prevent a developer from thinking that it is two-way-bound.</p>
<p><code>handleInput</code>: following data down, actions up, we should call a method passed in from the parent in order to modify data.</p>
<p><code>id</code>: I did not make id a property to pass in, because it's best practice to not use ids in CSS.
type: I also did not pass in type, since I prefer having components that are easy to understand and use, instead of a component that is complicated but ultra-reusable. I try to ask myself whether the component I'm making would need to be explained to a new hire, or if it would be self-explanatory.</p>
<p><em>Sidenote: I could have also used an Input with a capital I in my template example above, and written it to use two-way binding. I might go that route if I am refactoring an app that already makes use of two-way-bound inputs.</em></p>
<h2 id="makingthishappenfornbspeveryone">Making this happen for&nbsp;everyone</h2>
<p>Do you think that this sort of thing should be part of Ember's out-of-the-box toolset? The best solutions are those where you don't need to understand a lot in order to do the correct thing!
There is a group of developers working to improve Ember's accessibility right now, and they are working through this issue to problem solve and fix outstanding problems. If you would like to participate, speak up in the #st-a11y channel in the Ember Community Discord.</p>
<h2 id="anotherapproachtohandlingdomnbspevents">Another approach to handling DOM&nbsp;events</h2>
<p>Ember Octane popularized the use of modifiers, which provide a way to attach events or behavior as an element renders. There are some examples of modifiers in the ember-modifier library's README.
One could write a modifier that attaches and tears down event listeners for an input. This could be helpful if you need something that is both really fancy and reusable.</p>
<h2 id="yetanotherwaytohandlenbspevents">Yet another way to handle&nbsp;events</h2>
<p>Ember contributor and core team member Chris Garret has written an addon that provides a helper called box. This addon isn't part of Ember itself, and you would have to install it yourself in order to use it.</p>
<p>The <code>{{box}}</code> helper provides a way to add two-way-binding-like behavior to templates in a way that is concise and element-agnostic.Even if you don't plan to use box, it may be helpful to read it over as a way to boost your own understanding of the types of abstractions that people could create for handling DOM interactions.</p>
<h2 id="inconclusion">In conclusion…</h2>
<p>It's worth stating here that there's nothing wrong with using Input in your app, but there is something wrong with not using labels and aria attributes appropriately. It is up to you and your team whether you choose to enforce correct HTML through programmatic means, or just try to catch it in peer reviews.</p>
<p>I strongly recommend going the programmatic route whenever you can. This allows your fellow developers to learn on your own, and prevents the case where one developer is exhausted by enforcing standards while their colleagues grow annoyed. I have been on both sides of that table. I really prefer when the code helps me fix my own mistakes.</p>
<p>Good luck!</p>]]></description><link>https://jenweber.dev/building-an-octane-style-input</link><guid isPermaLink="true">https://jenweber.dev/building-an-octane-style-input</guid><pubDate>Thu, 09 Apr 2020 03:17:09 GMT</pubDate></item><item><title><![CDATA[Ember.js Octane in Five Minutes]]></title><description><![CDATA[<p>Octane is an upcoming Edition of Ember.js. Once it's out, <code>ember new</code> is going to show you an app like you haven't seen before. Here's a quick rundown of everything you need to know if you are an existing Ember dev.</p>
<p>It's a lot to take in, especially when you're used to Ember always looking the same year after year, but I think you'll enjoy the results.
We don't have enough time to get into why these things exist, but if you have questions, <a href="https://emberjs.com/community/">ask away</a>. </p>
<p>If you are new to Ember, check out <a href="https://medium.com/ember-ish/faqs-about-ember-js-in-2019-64efabbf84e6">this article instead</a>.</p>
<h2 id="impactonexistingapps">Impact on existing apps</h2>
<p>Nobody panic. Octane's features are done through minor releases.</p>
<p>Existing apps can keep doing their regular upgrade cycle, as they did before. Although Octane looks like lots of changes to Ember, they are provided as opt-in, non-breaking features. You would only see differences if you generated a new app, which has the Octane features all enabled.</p>
<h2 id="upgradingexistingapps">Upgrading existing apps</h2>
<p>If you want an existing app to be "Octanified," you will need to change some configuration settings in your app, to do things like disable JQuery and enable Native Class Components as the default. From there, you'll be able to gradually change your app's syntax, wherever you feel like doing so. For the most part, you can use a blend of features. Classic and Native Class Components can coexist.</p>
<h2 id="whatwillbedifferentinfreshlygeneratedapps">What will be different in freshly generated apps</h2>
<p>While playing around with the Octane preview/pre-release, here are the five areas where I needed to sit down and really learn them, not just copy/paste:</p>
<ol>
<li>Native Classes</li>
<li>Decorators</li>
<li>Using <code>@tracked</code> instead of Computed Properties</li>
<li>Element modifiers instead of component lifecycle hooks</li>
<li>Enforced data down, actions up</li>
</ol>
<p>An app created with <code>ember new</code> after Octane is released will be using all these features above, and more.</p>
<h3 id="nativeclasses">Native classes</h3>
<p>Whenever you import Component from <code>@glimmer/component</code> instead of <code>@ember/component</code>, you'll be in Native Class land. Classes are now a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class">feature of JavaScript</a>. If you find Class syntax confusing, it may be helpful to experiment with them some outside of Ember. This way, you'll have a clear picture of what is Ember and what is regular JavaScript.</p>
<p>Please note, you don't have to go learn Glimmer, even though the import comes from there.</p>
<p>Here's what a Component looks like using the new form:</p>
<pre><code class="js language-js">import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class HelloButton extends Component {
  text = 'Hello, world!';

  @action
  sayHello() {
    console.log('Hello, world!');
  }
}
</code></pre>
<p>Pretty weird, huh? No commas. No nesting inside an <code>action</code> object. Now actions are done using Decorators.</p>
<p>Components, Routes, Controllers, Services, Ember Data Models, etc. will all be available as Native Class imports.</p>
<h3 id="decorators">Decorators</h3>
<p>Decorators start with an <code>@</code>. They are already popular in tools like Angular and TypeScript, and are a Stage 2 JavaScript language feature. You can read about the general concept <a href="https://github.com/tc39/proposal-decorators">here</a>. If you create a Native Class version of a Component, Service, Controller, etc., then you can use decorators inside it.</p>
<p>The most important decorators to know about and use are <code>@action</code> and <code>@tracked</code>. Actions work more or less the same as they did before. <code>@tracked</code> is something new, and you'll use it instead of Computed Properties.
Keep reading for more information on that. You can also make your own Decorators if you're feeling fancy.</p>
<h3 id="usingtrackedinsteadofcomputedproperties">Using <code>@tracked</code> instead of Computed Properties</h3>
<p>Just to be clear, Computed Properties aren't <em>gone</em> from Ember. You can keep using them in "Classic" (aka non-Native Class) Components, Controllers, Routes etc. in your app. But if you are switching to Native Classes, you have two options for refactoring your computed properties.</p>
<p>As a first pass, you could use the <code>@computed</code> decorator inside your Native Class thing:</p>
<pre><code class="js language-js">import { computed } from '@ember/object';
// ...
  @computed('firstName', 'lastName')
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
// ...
</code></pre>
<p>But really what you want to use is <code>@tracked</code>. You decorate (or label) the properties that you want to be watched for changes:</p>
<pre><code class="js language-js">import { tracked } from '@glimmer/tracking';
//...
  @tracked firstName;
  @tracked lastName;

  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
// ...
</code></pre>
<p>I can use <code>{{this.fullName}}</code> in my template, just like before.</p>
<h3 id="elementmodifiersinsteadoflifecyclehooks">Element Modifiers instead of lifecycle hooks</h3>
<p>Ember Components have lifecycle hooks like <code>didInsertElement</code> that are popular places for shenanigans like inserting charts, registering features from non-Ember UI libraries, etc.</p>
<p>In Native Class Components, instead of declaring a <code>didInsertElement</code> method in your JavaScript file, you tell the template which action to run when the element renders:</p>
<pre><code>&lt;div {{did-insert this.someAction}}&gt;
  ...
&lt;/div&gt;
</code></pre>
<p>When that div renders, the action <code>someAction</code> will be called automatically. Pretty neat! As someone who used that hook a lot when using data vis tools like D3, I love this.</p>
<h3 id="enforceddatadownactionsup">Enforced Data Down, Actions Up</h3>
<p>In "classic" components, if you pass a property from a parent component to a child component, the child component could make changes to it, and those changes would affect every other place that same property was used. The change would show up in the parent, in other components, etc. This was referred to as "two way binding."</p>
<p>Now, for Native Class components, two way binding is no more.</p>
<p>If a child component tries to set a value of a property, it can only set the value on itself. The property from the parent is unchanged.</p>
<p>To give an example, let's say we have a property <code>penguins</code> passed from a parent component into a child component, and its value is zero.</p>
<pre><code class="hbs language-hbs">&lt;ChildComponent @penguins={{this.penguins}} /&gt;
</code></pre>
<p>In the child component, we try to set the value of <code>penguins</code> to 5, by calling this action:</p>
<pre><code>@action
morePenguins() {
  this.penguins = 5
}
</code></pre>
<p>In the child component, here's what we get displayed:</p>
<pre><code class="hbs language-hbs">{{@penguins}} &lt;!-- 0 - this is the property from the parent --&gt;
{{this.penguins}} &lt;!-- 5 - this is the component's own property --&gt;
</code></pre>
<p>In the parent component, the value of <code>penguins</code> is still zero too.</p>
<p>To change the value of <code>penguins</code> on the parent, we'd have to pass down an action that the child component can use:</p>
<pre><code class="hbs language-hbs">&lt;ChildComponent @penguins={{this.penguins}} @someAction ={{this.evenMorePenguins}} /&gt;
</code></pre>
<h2 id="learnmore">Learn more</h2>
<p>The goal of this article was to cover what to expect, in very brief terms. Ready for more?</p>
<ul>
<li>Learn what editions are <a href="https://emberjs.com/editions/">here</a></li>
<li>Octane-specific official resources are <a href="https://emberjs.com/editions/octane">here</a>, including how to test out the preview/pre-release</li>
<li>There are excellent, in-depth resources written mainly by Chris Garret, both for the Guides and a <a href="https://www.pzuraq.com/">personal blog</a>.</li>
<li>Ask questions in the <a href="https://emberjs.com/community/">Community Chat</a></li>
</ul>
<p>Good luck learning!</p>]]></description><link>https://jenweber.dev/ember.js-octane-in-five-minutes</link><guid isPermaLink="true">https://jenweber.dev/ember.js-octane-in-five-minutes</guid><pubDate>Sun, 02 Jun 2019 03:17:09 GMT</pubDate></item><item><title><![CDATA[Ember.js Stack Overflow Livestreams Return!]]></title><description><![CDATA[<p>In May of 2018, <a href="https://github.com/mansona">Chris Manson</a> and I set out to help improve Ember's presence on Stack Overflow, and I'm excited to share that we're doing it again! 
<strong>Our first livestream of 2019 is Wednesday, May 1st at noon UTC/8am EST.</strong></p>
<p>"<em>May</em> I ask a Question" is a month-long effort that includes livestreams and an invitation for community participation. Chris and I find an <a href="https://stackoverflow.com/questions/tagged/ember.js?sort=newest&pageSize=15">Ember-related Stack Overflow question</a> and share approaches to building and debugging, while making a minimal reproduction of the problem and delivering an answer. Our goal is to help make Ember more approachable for beginners and to demonstrate a Q&amp;A style that is centered in curiosity and exploration. We'd love you to help with these things too!</p>
<p>Although we are both members of Ember core teams, this is a side quest for us and is not affiliated with official projects or the organization.</p>
<h2 id="themanysidesofstacknbspoverflow">The many sides of Stack&nbsp;Overflow</h2>
<p>Last year, when we set out to encourage more participation, we got to see firsthand both the positive and negative sides of Stack Overflow. The Ember section was very chill and seemed great to us, but outside of that zone, there were problems. We got to see how the people behind the project <a href="https://stackoverflow.blog/2018/04/26/stack-overflow-isnt-very-welcoming-its-time-for-that-to-change/">think about the problems</a> that we and many other users encountered. We feel good about continuing this project since there seems to be thoughtfulness and progress.</p>
<p>On the sunny side, we are so inspired by the people who have been consistently doing Ember Q&amp;A over the past year!</p>
<h2 id="thelivestream">The livestream</h2>
<p>Our first livestream of 2019 is Wednesday, May 1st at noon UTC/8am EST!</p>
<p>In general, we're livestreaming on Thursdays or Fridays at noon UTC/8am EST. Recordings will be posted after each session. </p>
<p>For announcements of when we are streaming:&nbsp;</p>
<ul>
<li>follow us Twitter (<a href="https://twitter.com/jwwweber">jwwweber</a> and <a href="https://twitter.com/real_ate">real_ate</a>)</li>
<li>follow (Chris on Twitch)(https://www.twitch.tv/real_ate)</li>
<li>Watch our <a href="https://www.youtube.com/channel/UCyErLHzPqLAkL1F-SivFDcA">recordings on YouTube</a></li>
</ul>
<h2 id="howyoucanparticipate">How you can participate</h2>
<p>We would love it if more people want to jump in and help answer questions, or upvote the good work of others!
The most important thing I can stress is this - figure out what you enjoy, maybe bring along a friend who likes it too, and do that thing.
If Stack Overflow sounds overwhelming, maybe it's not your jam, and that's ok. There are so many different, critical ways to support an open source project. It's totally fine (and healthy!) to ignore certain aspects. It's really important to not burn yourself out on trying to pay attention all the time. If you want to give Stack Overflow a shot, here are some tips:</p>
<ul>
<li>Upvote good questions and answers. You're giving people who are already engaged a nice boost, and maybe some additional abilities</li>
<li>Include versioning in your answers. The opening sentence should say something like "This applies to Ember 3.x.x, and was written as of 3.1). Future devs will thank you.</li>
<li>If you search SO during your daily work and see answers from the past that are now outdated, comment with the modern answer.</li>
<li>Join the <a href="https://emberjs.com/community/">Ember Community Chat</a> if you want some company, spot an issue that should be modified, or can help with higher level operations like vote to close, editing, etc.</li>
<li>Use code snippets whenever possible</li>
<li>Help improve the quality of questions - if someone hasn't provided enough information, comment and ask for more. This will help you, them, and anyone else who lands on the question from Google!</li>
<li>Link back to the Docs or the Guides or both</li>
<li>Be excellent to each other. Every comment you make should be kind, helpful, and true.</li>
<li>If you want to receive email alerts when someone responds to your questions and answers, check out your Profile settings</li>
<li>Invite people to share their questions in the Discuss forum or on Stack Overflow in order to help make them searchable
Abandon ship if you don't like it. Your energy is needed elsewhere.</li>
</ul>
<h2 id="thanksfornbspreading">Thanks for&nbsp;reading!</h2>
<p>If you have any ideas for us, you can reach us both on the <a href="https://emberjs.com/community/">Ember Community Chat</a>!</p>
<h2 id="aboutus">About us</h2>
<p>Jen and Chris are both volunteer members of the Ember.js learning core team, where they build, write, and maintain public-facing resources for the framework.</p>
<p><a href="https://github.com/mansona">Chris Manson</a> is a developer at <a href="https://simplabs.com/">simplabs</a>. He's the creator of <a href="https://authmaker.com/">Authmaker</a> and <a href="https://github.com/empress/empress-blog">Empress</a>, and is an mentor and Ember.js advocate.</p>
<p><a href="https://github.com/jenweber">Jen Weber</a> is an engineer at <a href="https://cardstack.com">Cardstack</a>. She is an active open source developer, tech writer, and public speaker. She works to make tech a more welcoming industry.</p>]]></description><link>https://jenweber.dev/ember.js-stack-overflow-livestreams-return</link><guid isPermaLink="true">https://jenweber.dev/ember.js-stack-overflow-livestreams-return</guid><pubDate>Mon, 29 Apr 2019 23:26:30 GMT</pubDate></item><item><title><![CDATA[Help, my Ember acceptance tests are leaking!]]></title><description><![CDATA[<p>Are you stuck debugging async in your tests?
I'll cover one use case, including what the symptoms looked like, the root problem, and the solutions.
The code samples are all Ember 3.x.</p>
<h2 id="thesymptoms">The symptoms</h2>
<p>Today, I was working on fixing the following Ember Data deprecation:</p>
<blockquote>
  <p>DEPRECATION: Attempted to call store.serializerFor(), but the store instance has already been destroyed. [deprecation id: ember-data:method-calls-on-destroyed-store]</p>
</blockquote>
<p>Everywhere I could, I put checks like <code>this.store.isDestroyed</code> so that I could find and skip the offending code, but that didn't help. It always turned out to be <code>false</code>, and therefore useless to my debugging.</p>
<p>I thought, maybe this is some weird Ember Data thing, and I should look into that more.</p>
<p><em>Spoilers - it wasn't a weird Ember Data thing.</em></p>
<p>I asked for help on the <a href="https://emberjs.com/community/">Ember Community chat</a>, and Chris Thoburn aka <a href="https://twitter.com/Runspired">Runspired</a> helped me out.
I almost always leave conversations with him by thinking, I need to write a blog post about that.</p>
<h2 id="theproblem">The problem</h2>
<p>Here's what was going on. </p>
<p>My acceptance test was leaking state, meaning that some asychronous code was resolving after the test had already moved on and reset some resources for the next test.
This process of bringing the test back to a "clean slate" is referred to as "test teardown" in Ember and other JavaScript environments.</p>
<p>In my case, an <code>afterEach</code> hook that destroyed the store was happening before the code was actually done.
However, you might see the same sort of thing if you use <code>this.anything</code> after the testing teardown steps.</p>
<h2 id="findingthecause">Finding the cause</h2>
<p>The test itself was fairly normal, and used Ember's async test helpers just like it was supposed to:</p>
<pre><code class="js language-js">test('track field dirtiness in owned, related records', async function(assert) {
  await visit('/hub/posts/1');

  let reviewStatusActionTrigger = findTriggerElementWithLabel.call(this, /Comment #1: Karma/);
  await click(reviewStatusActionTrigger);

  let karmaInput = findInputWithValue.call(this, '10');
  await fillIn(karmaInput, '9');
  assert.dom('[data-test-cs-version-control-button-save="false"]').exists('Save button is enabled');
  assert.dom('[data-test-cs-version-control-button-cancel="false"]').exists('Cancel button is enabled');
});
</code></pre>
<p>The test code was doing what it should. Chris confirmed that it was my app code causing the problem, not an Ember Data issue, nor this test. But where could it be hiding?</p>
<p>My first strategy was that I commented out parts of the test until I figured out exactly which interaction caused the problem. Something happened after doing the <code>fillIn</code>.</p>
<p>I followed the code to see what actions were active.
It turns out that the component was using a Service to do <code>fetch</code> data, and then using the Ember Data serializer to parse the response:</p>
<pre><code class="js language-js">async validate(model) {
  let responses = this.toValidate.map(async (record) =&gt; {
    let { url, verb } = this._validationRequestParams(record);
    let response = await fetch(url, {
      method: verb,
      headers: this._headers(),
      body: JSON.stringify(record.serialize())
    });
  });

  let json = await Promise.all(responses);
  // do more work
}
</code></pre>
<p>The offending line was <code>record.serialize()</code>, which needs Ember Data, and my tests weren't waiting for the result.</p>
<h2 id="fixingit">Fixing it</h2>
<p>I could think of three possible paths forward:</p>
<ul>
<li>Stub the service</li>
<li>Make the tests wait (how???)</li>
<li>Check the Ember environment in my app code (ew)</li>
</ul>
<p>Stubbing the service was easiest in this case.
When you "stub" a service, you make a fake service that the test uses.</p>
<p>The actual code for the service used by the component was over 200 lines, but the component was only using three functions from it!</p>
<p>There are just a few steps to stubbing a service:</p>
<h3 id="1movethistroublesometestintoitsownfile">1. Move this troublesome test into its own file.</h3>
<p>My acceptance test had many smaller tests within it, but only one had async problems. I didn't want to have to stub every service function used by every one of those tests, so I moved the deprecation-triggering test into its own file.</p>
<p>I can run this test alone with <code>ember test --server --module "Acceptance | your test name"</code>. The module is whatever is specified in the test: <code>module('Acceptance | your test name', function(hooks) {...})</code>. </p>
<p>Alternately, to run my test alone, I can use <code>ember test --server --filter "some term"</code> where some term is found in the line <code>test('here is some term', async function(assert) {...}</code></p>
<p>The <code>--server</code> means that my tests will automatically re-run when I make changes, and it runs much much faster than doing <code>ember test</code> repeatedly by hand.</p>
<h3 id="2createthestubintheacceptancetest">2. Create the stub in the acceptance test.</h3>
<p>Next I made my fake service.
I looked at the component to see which functions it used.
I studied those functions to see what kind of data they should return, and console logged the things they returned when the test was runnning.
The asynchronous methods return a promise, and the other methods return data that the test needs to render.</p>
<p><em>The example below is Ember 3, but if you're using an older version of Ember, <a href="https://medium.com/ember-ish/how-to-use-a-service-from-an-acceptance-test-in-ember-js-6781fee2411b">this other article</a> might help you out with service stubbing. Ignore the pop-ups. There's no paywall.</em></p>
<pre><code class="js language-js">// my stubbed service

let StubCardstackDataService = Service.extend({
  validate() {
    return Promise.resolve({})
  },
  getCardMeta() {
    return 'Comment #1'
  },
  branches() {
    return []
  },
  fetchPermissionsFor() {
    return Promise.resolve({mayUpdateResource: false, writableFields: ['karmaValue', 'karmaType']})
  }
})
</code></pre>
<h3 id="4injectthetestinthebeforeeachhook">4. Inject the test in the <code>beforeEach</code> hook.</h3>
<p>This special function runs before every test. Instead of using my real <code>cardstack-data</code> service, the component will use my fake service when it runs the test. In order to make that happen, I have to register my service.</p>
<pre><code class="js language-js">hooks.beforeEach(function() {
  this.owner.register('service:cardstack-data', StubCardstackDataService);
});
</code></pre>
<p>Make sure the information that you give to <code>register()</code> matches the name of the service used in the Component.</p>
<h3 id="thefinalresult">The final result</h3>
<p>If you want to see how the stub is used in the context of the whole test, check out resulting PR <a href="https://github.com/cardstack/cardstack/pull/749/files">here!</a></p>
<h2 id="takeaways">Takeaways</h2>
<p>One thing I learned from this experience was that if I am writing components that have async data fetching behavior, there's a huge benefit to putting that code into a Service. That way, I can stub it easily in my tests!</p>
<p>This isn't the only way to handle this problem - remember, I outlined 3 options earlier, and there are probably more.</p>
<p>My other takeaways are that whenever I can, I should lean on Ember Data and the model hook to do data requests. Sticking closely to the "happy path" in Ember means that you get some async data handling for free. I was working on a project that does not have model hooks because it has no route files (weird, right?), but a normal Ember app has more async-handling features available!</p>
<h2 id="formoreinformation">For more information</h2>
<p>Here are some resources that I found helpful while learning and debugging. Thanks to these authors for their great work!</p>
<ul>
<li>The offical Ember tutorial's example of <a href="https://guides.emberjs.com/release/tutorial/service/#toc_integration-testing-the-map-component">stubbing a service</a></li>
<li><a href="https://engineering.linkedin.com/blog/2018/01/ember-timer-leaks">Ember Timer Leaks</a> by Evan Farina</li>
<li><a href="https://dockyard.com/blog/2018/03/29/testing-your-ember-application-in-2018">Testing your Ember application in 2018</a> by Scott Newcomer. Ok so it's not 2018 but if the async/await in this article is news to you, this is a really great primer</li>
<li>Similar to the item above, <a href="https://www.youtube.com/watch?v=8D-O4cSteRk">The future of Ember Testing</a> presented by Tobias Bieniek </li>
</ul>
<h2 id="thanks">Thanks!</h2>
<p>Thanks for reading and especially thanks to Chris Thoburn for the help!</p>
<h2 id="abouttheauthor">About the author</h2>
<p><em><a href="https://twitter.com/jenweber">Jen Weber</a> is web developer and writer based in Boston, USA (she/her or they/them). As a member of the <a href="https://emberjs.com/team">Ember Core Team</a>, she works on code, docs, and contributor engagement for the <a href="https://emberjs.com">Ember.js</a> front end framework. Jen works at <a href="https://cardstack.com/">Cardstack</a>, where she helps build tools to power Web 3.0. She is a fan of open source and making tech a more welcoming, inclusive industry.</em></p>]]></description><link>https://jenweber.dev/help-my-ember-tests-are-leaking</link><guid isPermaLink="true">https://jenweber.dev/help-my-ember-tests-are-leaking</guid><pubDate>Fri, 19 Apr 2019 00:08:12 GMT</pubDate></item><item><title><![CDATA[How to squash and rebase in git]]></title><description><![CDATA[<p>Did you ever need to do <code>git rebase --continue</code> over and over again just to get your own branch up to date? Chances are, you are on the sad path, and 99% of the time, there's a better strategy!</p>
<p>Rebasing is commonly a nightmare for newer devs or even experienced devs who don't use this workflow often, but it doesn't have to be! Here's how I walk people through their first squash and rebase. There are a ton of excellent articles and videos on this, but everyone does it a little differently. I like <a href="https://youtu.be/V5KrD7CmO4o">this video by The Modern Coder</a> that shows part of the process in action. After a few times doing this process, it will feel tedious but straightforward. No nightmares here.</p>
<h2 id="setyourdefaulttexteditoringit">Set your default text editor in git</h2>
<p>You only need to do this once. Skip this if you already configured this on your computer. If you have not set your default editor for git, then some of the steps below would open in vim, which is difficult to use if you have zero practice with it.</p>
<p>I use VSCode, and so I used the one-line command in <a href="https://dev.to/deadlybyte/make-vs-code-your-default-git-editor-j6d">this article by Carl Saunders</a>.</p>
<h2 id="stepstosquashandrebase">Steps to squash and rebase</h2>
<p>Create a backup branch and push it up to GitHub/Gitlab/etc. This backup branch will have all your work nice and safe in case you need to start over.</p>
<pre><code>git checkout my-branch-name
git branch backup-my-branch-name
git push origin backup-my-branch-name
</code></pre>
<p>Look up the commit id of the most recent commit that isn't yours, and copy it:</p>
<pre><code>git log
</code></pre>
<p>Start the rebase, replacing the placeholder text below with the commit id you copied:</p>
<pre><code>git rebase -i the-commit-id-you-looked-up-goes-here
</code></pre>
<p>Mark the commits you want to squash by putting "s" instead of "pick" in front of all the commits except the first one. The one at the top should still say "pick."
If there are a ton of conflicts, sometimes it's best to do some pair programming with the other dev(s) who made changes to these files. They can help you decide what to do.</p>
<p>Save and close, and wait for a new text editor prompt to pop up.</p>
<p>Enter your commit message.</p>
<p>Save and close.</p>
<p>Squashing should be complete. Check for any problems:</p>
<pre><code>git status
</code></pre>
<p>Next, I'm getting ready to rebase. All my collaborators merge their work into a branch called <code>main</code>, so that's what I'm using in this example. I pull that branch first, just to make sure my local copy is up to date:</p>
<pre><code>git checkout main
git pull origin main
</code></pre>
<p>Time to actually rebase. Check out your branch, and rebase!</p>
<pre><code>git checkout my-branch-name
git rebase main
</code></pre>
<p>Fix any git conflicts just like you would if you were merging something.</p>
<p>Stage the files once you have resolved conflicts:</p>
<pre><code>git add some-file.js another-file.html example.txt
</code></pre>
<p>Then, finish the rebase:</p>
<pre><code>git rebase --contine
</code></pre>
<p>You overwrote git history, so you will need to force push your work up. Make sure you are on your own branch before running this! It will overwrite other people's work if you are on the wrong branch.</p>
<pre><code>git push --force-with-lease origin my-branch-name
</code></pre>
<h2 id="ifsomethinggoeswrong">If something goes wrong</h2>
<p>No worries, you have a backup branch! Ask a colleague for help. This is a learning opportuinity. If you are all alone and you don't have anything else at all to work on, you could try the following to put things back to how they were. Be careful, don't lose your work!</p>
<p>Abort the rebase in progress:</p>
<pre><code>git rebase --abort
</code></pre>
<p>Rename the messed up branch:</p>
<pre><code>git branch -m messed-up-my-branch-name
</code></pre>
<p>Check out your backup branch. You may need to discard changes/new files with <code>git restore</code> before this command can be run.</p>
<pre><code>git checkout backup-my-branch-name
</code></pre>
<p>Copy your backup, and give the copy the name you were using before:</p>
<pre><code>git checkout -b my-branch-name
</code></pre>
<p>Now you are back to where you started, and can try again.</p>
<h2 id="whysquashbeforerebase">Why squash before rebase?</h2>
<p>Whenever you rebase, git compares every commit to the rebase target to see if there are conflicts. It makes you resolve them before you move on. Sometimes, this is useful - maybe you want to step through. But most of the time, fixing the conflict causes even more conflicts.</p>
<p>Instead, we want to squash all our work into just one commit. Then we resolve any conflicts <em>just once</em>, and then we are off to the races.</p>
<h2 id="whenshouldi_not_squashbeforerebase">When should I <em>not</em> squash before rebase?</h2>
<p>Every company/project has different norms about how they like to manage their project git history. If they tell you not to squash or rebase, then don't. Ask to pair program with someone a few times to get a sense of a nice workflow for their process.</p>]]></description><link>https://jenweber.dev/how-to-squash-and-rebase</link><guid isPermaLink="true">https://jenweber.dev/how-to-squash-and-rebase</guid><pubDate>Mon, 23 Aug 2021 14:45:45 GMT</pubDate></item><item><title><![CDATA[Remodeling an Ember App - Codemods and jQuery]]></title><description><![CDATA[<p>When you need to upgrade an Ember app, codemods can help you update your app's syntax faster than you could make changes by hand. Today, we'll talk about codemods and cover what to do about jQuery usage in your apps.</p>
<p>This is Part 4 of a series of blog posts. We're on a journey together to remodel an older Ember app, <a href="https://github.com/ember-learn/ember-api-docs">ember-api-docs</a>, incrementally bringing it up to date with the latest and best Ember and Ember Data patterns.</p>
<p>For this series, I'm pair programming with Chris Thoburn, aka <a href="https://github.com/runspired">@runspired</a>, who is known for
his work on Ember Data. He has over 500 commits and some great
debugging skills that you and I can learn from.</p>
<h2 id="whatyouwilllearninthissegment">What you will learn in this segment</h2>
<ul>
<li>How to find codemods</li>
<li>How to run the codemods</li>
<li>How to decide what to change, and what to leave alone</li>
<li>Where jQuery fits into the story</li>
</ul>
<h2 id="theroadsofar">The road so far</h2>
<p>So far, we have upgraded our dependencies and tests are all passing. This sets a strong foundation for running codemods and then making sure that tests keep passing.</p>
<h2 id="octanify">Octanify</h2>
<p>Ember Octane is Ember's first "edition." You can think of an edition as a collection of features and syntax that together form a cohesive mental model. There are multiple ways to accomplish a feature in Ember, and Octaneified apps use the latest styles.</p>
<p>There is a CLI command that configures an app to use Octane:</p>
<pre><code>npx @ember/octanify from   which set some dependencies and flipped flags in optional-features.json
</code></pre>
<p>You can find more instructions about this command in the <a href="https://guides.emberjs.com/release/upgrading/current-edition/">Ember upgrade guide</a>. In short, it sets some dependencies in <code>package.json</code> and feature flags in <code>optional-features.json</code>. Optional features are one way that new features are rolled out in Ember apps without making breaking changes. When you are ready, you can opt into them, but if you are not ready, you aren't blocked from upgrading. Regular upgrading is critical for companies and teams that value getting security, bugfixes, and feature updates over long periods of time.</p>
<p>Octanify sets the following feature flags in <code>optional-features.json</code>:</p>
<pre><code>{
  "default-async-observers": true,
  "jquery-integration": false,
  "template-only-glimmer-components": true,
  "application-template-wrapper": false,
}
</code></pre>
<p>You can learn all about these individual features in the <a href="https://guides.emberjs.com/release/configuring-ember/optional-features/">Optional Features Guide</a>.</p>
<h2 id="handlingjquery">Handling jQuery</h2>
<p>For our app, all the optional features set by Octanify were fine. Setting <code>jquery-integration: false</code> meant that instead of using <code>this.$()</code> in our apps, we had to install <code>@ember/jquery</code> and import jQuery individually - no problem. However, the <code>jquery-integration</code> flag made us wonder, could we remove <code>jQuery</code> from our app altogether? We were only using it in one place in our app, and doing so would cut some kb from our app.</p>
<p>However, we didn't just need to check our app. We also had to check if any addons used jQuery. I used to search my <code>node_modules</code> for usages, but that would return a lot of false positives compared to a strategy that Chris Thoburn showed me.</p>
<p>Chris first ran the build for the app:</p>
<pre><code class="sh language-sh">npx ember build
</code></pre>
<p>By default, the command puts built files in the <code>dist/</code> directory. Then, Chris was able to search for <code>this.$</code> and <code>Ember.$</code> and confirm that our app's addons did not need jQuery. This is better than searching <code>node_modules</code>, since we are only checking for the use of jQuery in addon features we are <em>actually using</em>.</p>
<p>Another common use of jQuery in apps is via Ember Data. By default, Ember Data uses jQuery for its HTTP requests. However, if an app has <code>ember-fetch</code> installed, it will use <code>fetch</code> instead. It's important to install <code>ember-fetch</code> in order to provide broad browser support for your fetch requests.</p>
<p>Finally, we took another look at our direct use of jQuery in this app, in our Table of Contents component:</p>
<pre><code class="js language-js">import { action } from '@ember/object';
import Component from '@ember/component';
import jQuery from 'jquery';

export default class TableOfContents extends Component {
  @action
  toggle(type) {
    jQuery(this.element)
      .find('ol.toc-level-1.' + type)
      .slideToggle(200);
  }
}
</code></pre>
<p>jQuery is providing a nice open/close animation for one of our menus. Whenever I see jQuery in use, I always check <a href="http://youmightnotneedjquery.com/">youmightnotneedjquery</a> to see if there's an easy alternative. In this case, there was not, so in the interest of moving forward, we leave jQuery in our app for now, and open an issue asking for help making a new CSS/plain JavaScript animation.</p>
<p>It's not a huge deal to leave jQuery in your app unless you have a strategic, benchmarked focus on app performance. jQuery is an incredibly successful project - so successful that its best features are now provided by native browser JavaScript. Ember's API documentation app is used by developers around the world with varying internet quality levels, and so we do need to save some kb where we can, however this one task should not block our progress towards improving other areas of the app.</p>
<h2 id="embercliupdatecodemods">Ember CLI Update Codemods</h2>
<p>Now on to some more codemods. <a href="https://github.com/ember-cli/ember-cli-update">Ember CLI Update</a> can update your dependencies, and also provides a subset of the most common codemods that make deeper changes.</p>
<p>After running dependency updates, you can run the codemods like this:</p>
<pre><code class="sh language-sh"># Start your app
npx ember serve
# Run the codemods
npx ember-cli-update --run-codemods
</code></pre>
<p>Some codemods start with the letters <code>fpe</code>. This stands for "function prototype extension."</p>
<h2 id="additionalcodemods">Additional codemods</h2>
<p>There are many more codemods than those provided by Ember CLI update. One good place to look for them is <a href="https://github.com/ember-codemods">ember-codemods</a> on GitHub.</p>
<p>Some codemods require that you have your app running. That's why we start the app with <code>ember serve</code>. Such codemods look at the built files in order to infer the correct changes to make. This is possible through a strategy called telemetry, via <a href="https://github.com/ember-codemods/ember-codemods-telemetry-helpers"><code>ember-codemods-telemetry-helpers</code></a>.</p>
<h3 id="aproblemwithonecodemod">A problem with one codemod</h3>
<p>When we ran <code>ember-modules-codemod</code>, we encountered an error. The codemod helpfully told us which line it failed on:</p>
<pre><code class="js language-js">titleToken(model) {
  return model?.fn?.name;
}
</code></pre>
<p>This line of code uses optional chaining, with <code>?.</code> syntax. Optional chaining is a feature of JavaScript that was added <em>after</em> these codemods were initially written.</p>
<p>We can see for ourselves one of the challenges of codemods - when they are written, they are a snapshot in time. It takes work to keep them functioning as JavaScript and Ember apps change. If you are working on a large app, it may be less work to fix a codemod bug than to make all the changes by hand. Additionally, codemods are written to work for individuals' apps, and so it takes community effort for codemods to work across many edge cases that authors could not forsee.</p>
<p>Another example of maintainability issues with codemods is the ES5 getter codemod - you have to run it before you run native classes codemods, because it doesn't know how to parse native classes. They didn't exist when the getter codemod was written. These aren't unsolvable problems, but they mean that developers whose apps are super out of date may have a harder time upgrading. There are many benefits to having a regular ugrade and maintenance schedule for your work, and this is one example.</p>
<h2 id="configuringvscodetoplaynicewithdecorators">Configuring VSCode to play nice with decorators</h2>
<p>Some of the codemods we ran introduced decorators. VSCode was our code editor of choice, and its default linters didn't like the use of decorators.</p>
<p>We removed <code>jsconfig.json</code> from our app's <code>.gitignore</code>, and then configured VSCode to stop yelling about decorators in <code>jsconfig.json</code>:</p>
<pre><code>{
    "compilerOptions": {
      "experimentalDecorators": true
    },
}
</code></pre>
<h2 id="dealingwithcodemodmistakes">Dealing with codemod mistakes</h2>
<p>Not all codemods are flawless. There's a lot of variation in an app! You should think of them has helpful suggestions, rather than a complete solution to your upgrade process. The best way to catch codemod mistakes is to carefully review the diff, run your tests, and make a commit after each codemod. Here's an example issue we had with a codemod that rewrote <code>link-to</code>:</p>
<pre><code class="handlebars language-handlebars">// before codemod

{{#link-to
  data-test-uses-link
  (concat parentName section.routeSuffix)
    model.project.id
    model.projectVersion.compactVersion
    model.name
    item.name
    (query-params anchor=item.name)}}
  {{item.name}}
{{/link-to}}

// after codemod
&lt;LinkTo @route={{concat this.parentName section.routeSuffix }} @models={{array this.model.project.id this.model.projectVersion.compactVersion}} @query={{hash anchor=item.name}}&gt;
</code></pre>
<p>The codemod cut out some of our route segments, leading to incorrect links in one part of the app:</p>
<pre><code class="text language-text">// correct
/ember-data/3.26/classes/Ember.Inflector/methods/singular?anchor=singular

// incorrect
/ember-data/3.26/classes/ember-data/methods/3.26?anchor=singular
</code></pre>
<p>How did this happen? We took a look at the source code for the codemod, and saw that it has special handling for dynamic routes, query params, and data-test. When we used all three of these things together in one link-to, it was the perfect storm.</p>
<h2 id="codemodstrategiesandtakeaways">Codemod strategies and takeaways</h2>
<p>Our upgrade would have been easier if we ran a single codemod at a time, and make it its own commit, and ran it in CI, and shipped it. If you have the ability to roll your app back quickly in production, you don't need to work as carefully.</p>
<p>That said, you really need to go through the diff if you try to do a big bang upgrade.</p>
<p>If your test suite coverage is bad, and you can't improve it, you can run codemods on specific chunks of code and QA them - do one set of related components at a time, then look at the diff and click test. Many (or maybe all) codemods accept file paths in the CLI commands.</p>
<h2 id="conclusion">Conclusion</h2>
<p>That's it for codemods! Next up, we will take a look at the code that couldn't be modded, and do some refactors to simplify the app now that we have Octane's awesome features available, such as <code>tracked</code>. Thanks for reading!</p>]]></description><link>https://jenweber.dev/remodeling-an-ember-app---codemods</link><guid isPermaLink="true">https://jenweber.dev/remodeling-an-ember-app---codemods</guid><pubDate>Tue, 13 Jul 2021 20:23:53 GMT</pubDate></item><item><title><![CDATA[Remodeling an Ember App - Introduction]]></title><description><![CDATA[<p>In a series of blog posts, we'll go on a journey together to remodel an older Ember app, incrementally bringing it up to date with the
latest and best Ember and Ember Data patterns.</p>
<p>We will walk through updating a complex, real-world app,
<a href="https://github.com/ember-learn/ember-api-docs">ember-api-docs</a>,
which is the front end of <a href="https://api.emberjs.com">api.emberjs.com</a>. Along the way, you will learn how to approach this process
for your own apps - updating dependencies, debugging build errors, migrating to Octane syntax, writing better Ember Data serializers &amp; adapters… there's a lot to do!</p>
<p>For this series, I'm pair programming with Chris Thoburn, aka <a href="https://github.com/runspired">@runspired</a>, who is known for
his work on Ember Data. He has over 500 commits and some great
debugging skills that you and I can learn from.</p>
<h2 id="preparation">Preparation</h2>
<p>The focus of this first article will be about deciding where to begin,
what kind of mindset to have, and an overview of the debugging tools in our toolbox.
There are a lot of things we <em>could</em> work on improving in this app, and there
are multiple paths we could take that lead to success.</p>
<h3 id="areasoffocus">Areas of focus</h3>
<p>Making a list of them and prioritizing is very important step for any app refactor or upgrade.
Some broad tasks to consider include:</p>
<ul>
<li>Updating to the latest package versions</li>
<li>Resolving deprecations</li>
<li>Running codemods</li>
<li>Refactoring confusing stuff</li>
<li>Documentation</li>
<li>Testing and QA</li>
</ul>
<h3 id="makingadetailedlist">Making a detailed list</h3>
<p>Thinking about these areas of focus helps me get started with making a detailed list.</p>
<p>Chris Thoburn and I did a brainstorm about <code>ember-api-docs</code> and came up with the following list:</p>
<ul>
<li>Upgrade Ember and Ember data versions</li>
<li>Run some codemods for Octane</li>
<li>rewrite computed properties to cached getters, simplifying where possible (reduce intermediary computed properties)</li>
<li>Figure out how to use less of Ember Data</li>
<li>Figure out what needs to be in API response meta</li>
<li>audit models and remove async relationships that didn't need to be async and ensure that inverses were properly wired</li>
<li>make sure the API returned the format the store expects, and remove the serializer entirely</li>
<li>Remove the adapter and replace it with one that just does a simple fetch request</li>
<li>drop both the adapter and the serializer package and the ember-data package, instead installing store and model and record-data directly</li>
</ul>
<h3 id="prioritizing">Prioritizing</h3>
<p>Next up is prioritizing. This can be hard to do when you don't know an app well, and that's ok. This list's ordering should shift over time as you learn new things! But you do need to decide what to try first.</p>
<p>For this app, the clear first step is to upgrade the Ember and Ember Data versions.</p>
<p>We start here so that we have all the latest and greatest features. This can also be a challenging step because shifting dependency versions sometimes reveal bugs, but finding them later in the middle of a refactor is no fun.</p>
<h3 id="gettingintherightmindset">Getting in the right mindset</h3>
<p>Overhauling an app (or even a complex component) takes a different mindset than I use in my usual daily coding.
Most days, I have expectations about how the code works, because I wrote it or studied a section of it closely. But in an overhaul, big upgrade, or refactor, there are a lot of unknowns! If I intentionally adopt a different mindset at the beginning of a big uprade, I'm a happier developer. What's that mindset look like?</p>
<p>When I make a change that does not fix a problem, I try to be curious about why.</p>
<p>When I see a different error, I treat it as a step forward.</p>
<p>I keep a list of my successes as I go, including learning new things or getting one step deeper into the process.</p>
<p>When I get stuck, I conduct small experiments, and record the results.</p>
<p>I accept and expect that some of my experiments will be dead ends.</p>
<p>When I don't know where an error is coming from, I focus on trying to find the source before trying to fix the error.</p>
<p>I am skeptical of the things I changed, by default. This especially includes what's in the node_modules folder and shifting dependency versions.</p>
<h3 id="generaldebuggingstrategies">General debugging strategies</h3>
<p>Over the course of this series, we will use lots of different debugging techniques.
You will see examples of all of these! But it's useful to look at them as a group, and think of this as tools in your toolbox.</p>
<ul>
<li>Selectively commenting things out from your app to hone in on where a problem is coming from. Chris calls this "bisecting," and I call it "Cat in the Hat" debugging. Identifying the areas that are <em>not</em> a problem is progress!</li>
<li>Cleaning out <code>node_modules</code> - deleting the app's <code>node_modules</code> folder and the lock file, either <code>package-lock.json</code> or <code>yarn.lock</code>, and reistalling dependencies</li>
<li>Restarting the local server - important to do if you change dependency versions or build configuration</li>
<li>Logging template values to the console. In your <code>.hbs</code> files, you can add <code>{{log this.myVariable}}</code> and see the output in the console</li>
<li>If your app is using it, turning <code>fastboot</code> off while you debug. Add <code>?fastboot=false</code> to the end of your locally served URL, i.e. <code>http://localhost:4200/?fastboot=false</code>. </li>
<li>Using "Break on exceptions" or "Break on uncaught exceptions" in your browser's debugging tools, to help with finding out where errors are coming from, with some more informative context</li>
<li>Using <code>debugger</code> in your <code>js</code> files to set breakpoints that you can see in your browser's debugging tools</li>
<li>Using <code>yarn why package-name</code> or <code>npm ls package-name</code> to confirm which versions of dependencies
are actually in use, and which other dependencies use them.</li>
</ul>
<h2 id="upnext">Up next</h2>
<p>Next, in Part 2, we will start doing the app version upgrade!</p>]]></description><link>https://jenweber.dev/remodeling-an-ember-app---introduction</link><guid isPermaLink="true">https://jenweber.dev/remodeling-an-ember-app---introduction</guid><pubDate>Wed, 02 Jun 2021 00:31:47 GMT</pubDate></item><item><title><![CDATA[Remodeling an Ember App - Package Updates]]></title><description><![CDATA[<p>Today's topic is upgrading dependencies on an older Ember app.
This is a real-world app and the issues we face will be different from your apps, but you can learn the overall strategy and debugging approaches,</p>
<p>This is Part 2 of a series of blog posts. We're on a journey together to remodel an older Ember app, incrementally bringing it up to date with the
latest and best Ember and Ember Data patterns.</p>
<h2 id="abouttheseries">About the series</h2>
<p>We will walk through updating a complex, real-world app,
<a href="https://github.com/ember-learn/ember-api-docs">ember-api-docs</a>,
which is the front end of <a href="https://api.emberjs.com">api.emberjs.com</a>. Along the way, you will learn how to approach this process
for your own apps - updating dependencies, debugging build errors, migrating to Octane syntax, writing better Ember Data serializers &amp; adapters… there's a lot to do!</p>
<p>For this series, I'm pair programming with Chris Thoburn, aka <a href="https://github.com/runspired">@runspired</a>, who is known for
his work on Ember Data. He has over 500 commits and some great
debugging skills that you and I can learn from.</p>
<h2 id="decidinghowtoapproachupgradingdependencies">Deciding how to approach upgrading dependencies</h2>
<p>There are two main approaches Ember developers take, either:</p>
<ol>
<li>Upgrade addons</li>
<li>Upgrade core Ember and Ember Data dependencies</li>
</ol>
<p>Or, they approach this in the reverse order:</p>
<ol>
<li>Upgrade core Ember and Ember Data dependencies</li>
<li>Upgrade addons</li>
</ol>
<p>The path you choose depends on your app - how many addons does it have? How outdated are they?</p>
<p>In our case, we decided on the latter. We didn't want to introduce too many changes at once if we didn't have to, and we were pretty confident in our ability to sort through addon-specific errors.</p>
<h2 id="runningtheemberandemberdataupgrade">Running the Ember and Ember Data upgrade</h2>
<p>This app was on 3.16. The latest Ember and Ember Data version is 3.26.</p>
<p>We can do the upgrade with the help of <code>ember-cli-update</code>, which will apply the blueprint used for fresh apps.</p>
<pre><code>npx ember-cli-update
</code></pre>
<p>You can read a whole bunch about upgrading in the <a href="https://guides.emberjs.com/release/upgrading/">Ember Guides</a> and <a href="https://cli.emberjs.com/release/basic-use/upgrading/">Ember CLI Guides</a>,
so we won't go into too much detail here.</p>
<p>We reviewed each file that was modified first, to confirm that we wanted those changes.</p>
<p>Then, we walked through all merge conflicts and resolved them.</p>
<p>Next, we installed our new dependencies with <code>yarn install</code>.</p>
<p>Finally, we started the Ember server with <code>npx ember serve</code> and hit our first bug.</p>
<p><em>(Why <code>npx ember serve</code>? <code>npx</code> ensures that the Ember CLI version used by 
your app is the one specified in package.json, and not your globally
installed version. It usually doesn't matter, but when it does, it's often
when working on upgrade tasks like this!)</em></p>
<h2 id="debuggingabuildfailure">Debugging a build failure</h2>
<p>Here's the build failure we saw in the console after we started up our server:</p>
<pre><code>Build Error (broccoli-persistent-filter:Babel &gt; [Babel: @ember/ordered-set]) in @ember/ordered-set/index.js

Duplicate plugin/preset detected.
If you'd like to use two separate instances of a plugin,
they need separate names, e.g.

  plugins: [
    ['some-plugin', {}],
    ['some-plugin', {}, 'some unique name'],
  ]

Duplicates detected are:
[
  {
    "alias": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js",
    "options": {
      "emberVersion": "3.26.1",
      "root": "/Users/jweber/projects/ember-api-docs",
      "name": "@ember/ordered-set"
    },
    "dirname": "/Users/jweber/projects/ember-api-docs",
    "ownPass": false,
    "file": {
      "request": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js",
      "resolved": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js"
    }
  },
  {
    "alias": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js",
    "options": {
      "emberVersion": "3.26.1",
      "root": "/Users/jweber/projects/ember-api-docs",
      "name": "@ember/ordered-set"
    },
    "dirname": "/Users/jweber/projects/ember-api-docs",
    "ownPass": false,
    "file": {
      "request": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js",
      "resolved": "/Users/jweber/projects/ember-api-docs/node_modules/ember-compatibility-helpers/comparision-plugin.js"
    }
  }
]
</code></pre>
<p>Not very nice.</p>
<p>We did <code>yarn why @ember/ordered-set</code> to confirm that only one version of
that dependency was in use:</p>
<pre><code>yarn why @ember/ordered-set

yarn why v1.22.10
[1/4] 🤔  Why do we have the module "@ember/ordered-set"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
[4/4] 🚡  Calculating file sizes...
=&gt; Found "@ember/ordered-set@4.0.0"
info Reasons this module exists
   - "ember-data" depends on it
   - Hoisted from "ember-data#@ember#ordered-set"
   - Hoisted from "ember-data#@ember-data#record-data#@ember#ordered-set"
info Disk size without dependencies: "44KB"
info Disk size with unique dependencies: "1.98MB"
info Disk size with transitive dependencies: "47.57MB"
info Number of shared dependencies: 151
✨  Done in 1.83s.
</code></pre>
<p>Chris hypothesized that this was due to floating dependencies.
Yarn and NPM let developers specify a range of acceptable dependency versions.
Sometimes there are mismatches. It's often a good initial step to try
deleting node modules and the lock file, as a small experiment, and then try again:</p>
<pre><code class="sh language-sh">rm -rf node_modules
rm yarn.lock
yarn install
npx ember s
</code></pre>
<p>That resolved the error about duplicate plugins!</p>
<h2 id="ontothenexterrordependenciesofdependencies">On to the next error - dependencies of dependencies</h2>
<p>Next, we saw some errors about <code>ember-popper</code>.</p>
<p>We looked to see what was using <code>ember-popper</code> using <code>yarn why</code>.
One of our dependencies, <code>ember-styleguide</code> used it. We looked at
a more recent version of <code>ember-styleguide</code>, in the <code>package.json</code>.
The latest ember-styleguide didn't even use popper,
so upgrading ember-styleguide got us over this hurdle.</p>
<p>We also saw an error about <code>ember-basic-dropdown</code>.
<code>yarn why</code> told us it was used by <code>ember-power-select</code>.
Upgrading <code>ember-power-select</code> and following instructions in the
CHANGELOG fixed that issue.</p>
<p>Now, when we did <code>yarn install</code> and <code>npx ember s</code>, the app built successfully.</p>
<h2 id="debuggingruntimeerrors">Debugging runtime errors</h2>
<p>Now when we visted <code>localhost:4200</code>, we saw something strange.
The app would render for a moment (thanks to Fastboot),
but then when the app's JavaScript loaded in fully, it crashed.</p>
<p>Apps that use Fastboot show their errors in the terminal running the server,
<em>not</em> the browser console. So, to get a closer look at what was going on,
we temporarily turned off fastboot by visiting <code>http://localhost:4200?fastboot=false</code>.</p>
<p>There were two errors in the browser console:</p>
<p>One:</p>
<pre><code>Error occurred:

- While rendering:
  -top-level
    application
      es-header
        es-navbar
          bs-navbar
            bs-navbar/content
              bs-collapse
                bs-navbar/nav
                  search-input
                    ember-tether
                      search-input/dropdown
</code></pre>
<p>Two:</p>
<pre><code>Uncaught (in promise) ReferenceError: process is not defined
    &lt;anonymous&gt; Ember
    js vendor.js:173080
    __webpack_require__ vendor.js:172838
    &lt;anonymous&gt; Ember
    js vendor.js:173069
    __webpack_require__ vendor.js:172838
    &lt;anonymous&gt; Ember
    js vendor.js:173126
    __webpack_require__ vendor.js:172838

...and a whole lot more
</code></pre>
<p>We wanted to see where the problem was coming from, as step one.
We commented out the <code>es-header</code> component and saw the app render.
Then we commented out pieces of the app until we got to
identifying <code>search-input</code> as the culprit.</p>
<p>The search feature of this app comes from a third-party library.
Is something in there using <code>process</code>?</p>
<p>I enabled "Break on exceptions" in my browser's debugging tools.
This showed us that indeed, <code>process.env</code> was being used in a third party script.
But why did this work before? What else had changed?</p>
<h2 id="debuggingwebpack">Debugging webpack</h2>
<p>The webpack errors are a clue about what changed.
When we updated a bunch of things in <code>package.json</code> using <code>ember-cli-update</code>,
one of them was <code>ember-auto-import</code>. This package uses webpack to
bring npm dependencies into an Ember app with zero config,
and we changed its versions.</p>
<p>One thing that changed was that certain polyfills were no longer included.
This was outlined <a href="https://github.com/ef4/ember-auto-import#i-upgraded-my-ember-auto-import-version-and-now-things-dont-import-what-changed">in the project README</a>
along with steps to fix it.</p>
<p>We added the following to our <code>ember-cli-build.js</code>:</p>
<pre><code>autoImport: {
  webpack: { 
    node: { 
      process: 'mock'
    }
  }
},
</code></pre>
<p>Now, the app rendered successfully! We removed <code>?fastboot=false</code> from our
localhost URL, and saw that it was working fine in that mode too.</p>
<h2 id="upnext">Up next</h2>
<p>So, the app rendered, but the tests are failing.
Coming soon, we'll tackle those test failures.</p>]]></description><link>https://jenweber.dev/remodeling-an-ember-app---package-updates</link><guid isPermaLink="true">https://jenweber.dev/remodeling-an-ember-app---package-updates</guid><pubDate>Wed, 09 Jun 2021 13:10:08 GMT</pubDate></item><item><title><![CDATA[Remodeling an Ember App - Testing]]></title><description><![CDATA[<p>Today's topic is debugging an Ember app's test suite after upgrading some dependencies. This is a real-world app and the issues we face will be different from your apps, but you can learn the overall strategy and debugging approaches,</p>
<p>This is Part 3 of a series of blog posts. We're on a journey together to remodel an older Ember app, <a href="https://github.com/ember-learn/ember-api-docs">ember-api-docs</a>, incrementally bringing it up to date with the latest and best Ember and Ember Data patterns.</p>
<p>For this series, I'm pair programming with Chris Thoburn, aka <a href="https://github.com/runspired">@runspired</a>, who is known for
his work on Ember Data. He has over 500 commits and some great
debugging skills that you and I can learn from.</p>
<h2 id="whatyouwilllearninthissegment">What you will learn in this segment</h2>
<ul>
<li>How to run tests on-demand instead of on every change</li>
<li>How to run the tests for different git branches, side by side</li>
<li>What to expect in your test suite after upgrading the linters</li>
<li>How to deal strategically and efficiently with hundreds of linting errors</li>
</ul>
<h2 id="ourprogresssofar">Our progress so far</h2>
<p>In the previous article, we had updated a bunch of dependencies and solved issues that prevented the app from building (build time errors) or prevented it from working correctly in the browser (runtime errors). We got to the end successfully, but there were some test failures.</p>
<p>Why were there failures? Well, we made a <em>lot</em> of changes! No matter what framework I'm using, any time I make a bunch of changes before running my test suite, I expect some tests to fail. Another path we could have chosen was to make changes one at a time, while running the test suite at every step. However, sometimes, seasoned Ember.js developers can take a big leap forward and then reconcile the errors. Sometimes, this latter approach saves you time, but if you get stuck, it's a good idea to take a step back and do one change at a time.</p>
<h2 id="newtoolsforyourtoolkittestrunningmodes">New tools for your toolkit - test running modes</h2>
<p>Sometimes, the default behavior of <code>ember test --server</code> gets in the way. By default, the app rebuilds and test run every time you make a change. That wipes out the test failure information that you may need to reference while you make changes in multiple files.</p>
<p>Chris Thoburn showed me one way to improve on this experience and fix my test failures more quickly! He often runs a build continuously, and then in a separate process, run the tests. Although the app rebuilds every time you make a change, the test only re-run when you tell them to. So, the test failure output is always there when we need it.</p>
<pre><code class="sh language-sh"># In one tab of your terminal, start the build and watch for changes
ember build --watch --output-path="./dist"

# In a new tab or window of your terminal, run the tests
ember test --serve --path="./dist" --no-launch
</code></pre>
<p>In these examples, we pointed the tests to the <code>dist</code> folder for the built app. We can apply this same strategy in order to run tests from different git branches side by side. For example, you could check out one branch of your app, run a single build, check out another branch, run a build, and then run the tests for both. Then you can inspect the results and compare them! You can even add debuggers to both branches and stop the test suites in the same place to inspect the state. Here's how!</p>
<pre><code class="sh language-sh">git checkout main
yarn install
ember build --output-path="./dist-main"
git checkout upgrade-branch
ember build --output-path="./dist-my-upgrade-branch"
# Run the tests for main
ember test --serve --path="./dist-main" --no-launch
# Run the tests for the upgrade branch
ember test --serve --path="./dist-my-upgrade-branch" --no-launch
</code></pre>
<h2 id="resolvinglintingerrors">Resolving linting errors</h2>
<p>One change we made was that we increased the version number of <code>eslint-plugin-ember</code> and <code>ember-template-lint</code>. When we did that, we brought in new linting rules for our <code>.hbs</code> templates and JavaScript files. We got automatic guidance on how we could improve our app's syntax, coding style, and most importantly, its accessibility. However, it means our linting tests were all failing with over 100 errors! How do we deal with that?</p>
<p>For every linting error or warning, we must choose between the following options:</p>
<ul>
<li>Update the codebase itself to resolve the warning</li>
<li>Turn the rule off in <code>.eslintric</code> or <code>.template-lintrc.js</code></li>
<li>Add a code comment that tells the linter to ignore that file or line of code</li>
</ul>
<p>Most of the time, when you are doing a dependencies upgrade, you should only fix very very tiny things and for everything else, ignore the rule and do the fixes themselves in later PRs. Why? We don't want to inadverdantly change the app's behavior! When upgrading dependencies, ideally the app looks the same when we're done.</p>
<h3 id="toolstohelpyoudealwithlintingerrors">Tools to help you deal with linting errors</h3>
<p>There are some helpful tools in the Ember ecosystem for tracking and resolving linting issues over time, and they are especially important for large apps or teams.</p>
<p>First, <code>ember-template-lint</code> helps you automatically convert template linting errors into future to-do tasks. You can learn more about this feature in the <a href="https://blog.emberjs.com/how-to-todo-in-ember-template-lint/">Official Ember Blog</a>.</p>
<p>Second, Chris Manson created <a href="https://github.com/mansona/lint-to-the-future"><code>lint-to-the-future</code></a>, which turns all your errors into code comments that ignore the rule. When you fix a warning, remove the code comment. You can also visualize and measure your progress with some graphs!</p>
<h3 id="ourapproach">Our approach</h3>
<p>Although 100+ linting errors sounds like a lot, most of them were the same error over and over again. We could see them by running <code>yarn lint</code>.</p>
<p>We wanted to avoid making too many unnecessary changes in this app until after our upgrade step was over, so we turned a whole bunch of rules off in our <code>.eslintrc</code>. It makes sense that we had to turn off these rules, since many of them were telling us about how to turn our app into an Octane-style app. They will be useful after we finish the dependencies upgrade! We can learn more about each rule by searching for the rule name in the <a href="https://github.com/ember-cli/ember-cli-eslint"><code>ember-cli-eslint</code></a> source code.</p>
<pre><code class="js language-js">// excerpt from .eslintrc
  rules: {
    'ember/no-jquery': 'off',
    'ember/no-jquery': 'off',
    'no-console': 'off',
    'ember/no-new-mixins': 'off',
    'ember/no-mixins': 'off',
    'ember/native-classes': 'off',
    'ember/require-tagless-components': 'off',
    'ember/no-test-this-render': 'off',
    'ember/no-classic-classes': 'off',
    'ember/no-get': 'off',
    'ember/no-actions-hash': 'off',
    'ember/no-classic-components': 'off',
    'ember/no-private-routing-service': 'off',
  },
</code></pre>
<p>We only needed to ignore two rules in our <code>.template-lintrc.js</code>. We can learn all about why these rules exist by searching for the rule name in <a href="https://github.com/ember-cli/eslint-plugin-ember"><code>eslint-plugin-ember</code> source code</a>. The file name matches the rule that shows up in the warnings. You can find it quickly on GitHub by pressing the letter <code>T</code> from the repository's main page and typing in the name of the rule.</p>
<pre><code class="js language-js">// excerpt from .template-lintrc.js
rules: {
  'no-link-to-positional-params': false,
  'require-input-label': false,
  // and some more rules that were already there
}
</code></pre>
<p><a href="https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/require-input-label.md"><code>require-input-label</code></a> is an example of a linting rule that helps you discover accessibility issues in your app! It warns you if you have an input element that lacks an associated label element. In our case, this warning was a false hit - we found a bug! We <a href="https://github.com/ember-template-lint/ember-template-lint/issues/1835#issuecomment-857223622">reported the bug</a> and linked to the public example of it.</p>
<p><a href="https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-link-to-positional-params.md"><code>no-link-to-positional-params</code></a> was telling us not to do links in this style: <code>{{link-to "About Us" "about"}}</code>, Instead, we should do <code>&lt;LinkTo @route="about"&gt;About Us&lt;/LinkTo&gt;</code>. We can definitely handle this later.</p>
<p>Once our linting tests were passing, we moved on to the next step!</p>
<h2 id="understandingapplicationtestfailures">Understanding application test failures</h2>
<p>When you do an upgrade like this, there are some common sources of failures. It's helpful to ponder this line of questioning below for a little when you run into a test that is tricky to fix:</p>
<ul>
<li>Did I make a mistake during the upgrade?</li>
<li>Did the upgrade uncover a bug that was hidden previously?</li>
<li>Were there any breaking changes in my dependencies?</li>
<li>Was my app relying on a bug in Ember that was fixed?</li>
<li>Did my app rely on private API methods?</li>
</ul>
<p>After pondering this list, now I can move to the next level:</p>
<ul>
<li>What evidence do I have that my hypothesis is correct?</li>
<li>Do I need to update a test, update something in my app, or both?</li>
<li>If I reread the test failure again, do I get any new insights?</li>
</ul>
<h3 id="ourtestfailures">Our test failures</h3>
<p>Here's an example test failure we worked through:</p>
<pre><code class="text language-text">Error: Element not found when calling `fillIn('#ember-basic-dropdown-content-ember
1246 .ember-power-select-search-input[type=search]')`.
  at http://localhost:7357/assets/test-support.js:35450:15
  at async selectSearch (http://localhost:7357/assets/test-support.js:39374:9)
  at async Object.&lt;anonymous&gt; (http://localhost:7357/assets/tests.js:66:7)
</code></pre>
<p>In this case, the question that jumps out at us is "Were there any breaking changes in my dependencies?" We upgraded from <code>ember-power-select</code> version <code>2.3.5</code> to <code>4.1.6</code>. Following the rules of semantic versioning, we can see that there have been two releases with breaking changes. Time to check the release notes of the library, in <a href="https://github.com/cibernox/ember-power-select/blob/master/CHANGELOG.md">CHANGELOG.md</a>. If you don't see a changelog for the library you are using, look at the Releases section on GitHub instead.</p>
<p>By reading the changelog, we could see that there was a new required attribute to add to the component. Adding <code>@searchEnabled={{true}}</code> to our power select dropdowns was all that was needed.</p>
<p>Another test failure we saw was this:</p>
<pre><code class="text language-text">not ok 66 Chrome 91.0 - [196 ms] - Acceptance | document title: is of format className - version - Ember API Docs
  ---
    actual: &gt;
        Ember Api Docs
    expected: &gt;
        Container - 1.0 - Ember API Documentation
</code></pre>
<p>This was an example of a mistake we made while upgrading. The test that was failing was checking the page title of a certain URL in the app. A page's title is important for SEO, accessibility, and overall user experience. It's part of the text that shows up in a search result. It is the text that shows up at the top of your browser tab. And for people who use assistive tech, it helps them know what page they are on when they flip through tabs. You can learn more about page titles in <a href="https://guides.emberjs.com/release/accessibility/page-template-considerations/#toc_page-title">the Ember Guides</a>.</p>
<p>What happened was that when the upgrade diff was applied, it added something like <code>{{page-title "EmberApiDocs"}}</code> to the <code>application.hbs</code> template. Community member <a href="https://github.com/prakashchoudhary07">prakashchoudhary07</a> discovered that the page title was being set in another, more sophisticated way in this app, and they opened a PR to help us out. We could delete the page title helper, and the test passed. Thanks prakashchoudhary07!</p>
<p>Lastly, there was an error due to reliance on private API. One test was using private APIs on the router inside the test only. When I had tried to upgrade the test to use the router service instead, the method was no longer available. So, I undid the change I made during linting fixes and instead added an ignore to the <code>eslint.rc</code>. We can deal with that issue in another PR!</p>
<h2 id="upnext">Up next</h2>
<p>Now, all our tests are all passing! Next in this series, we will run some Octane codemods and refactor some components that are tough to work with. Thanks for reading!</p>]]></description><link>https://jenweber.dev/remodeling-an-ember-app---testing</link><guid isPermaLink="true">https://jenweber.dev/remodeling-an-ember-app---testing</guid><pubDate>Wed, 16 Jun 2021 21:45:45 GMT</pubDate></item><item><title><![CDATA[Revisiting my 2018 Ember.js Roadmap blog post]]></title><description><![CDATA[<p>What has changed since last year? In May of 2018, I wrote a blog post for the <a href="https://blog.emberjs.com/2018/05/02/ember-2018-roadmap-call-for-posts.html">Ember.js Roadmap process</a>.
It was called <a href="https://gist.github.com/jenweber/a9fbea98478fc3841fb8b24f7dc961c8">Be loud and be ready - my hopes for Ember.js in 2018</a>.</p>
<p>I tried to imagine a future where Ember was wildly successful, not just surviving but <em>growing</em>, then work backwards to think through what those imaginary, wildly successful people must have done.</p>
<p>So, how did we do? Are we on-track to being the wildly successful people?</p>
<h2 id="beingloud">Being loud</h2>
<p>Here's what I said a year ago about "Being Loud":</p>
<blockquote>
  <p>The people of Future-Ember worked to increase public awareness so that more developers knew about it and considered it for their projects. The Core Team led by example, writing and speaking, and the rest of the community was empowered to do the same.</p>
</blockquote>
<p>I would wager that it's been a record year for this. </p>
<p>I know that a lot of core team members and cornerstone community members have given talks at non-Ember events and have done a <em>ton</em> of writing. 7/10 of my talks were at general venues like BostonJS and Boston Code Camp. 
Community members have also revived the <a href="https://www.reddit.com/r/emberjs/">emberjs subreddit</a>. The <a href="https://blog.emberjs.com/">official blog</a>, once kind of a dead zone except for version releases, is now packed with evidence that Ember is on the move.
The <a href="https://the-emberjs-times.ongoodbits.com/">Ember Times</a> team has their writing down to a science.
The newest Ember.js Core Team member, Chris Garrett aka <a href="https://github.com/pzuraq">pzuraq</a> is both an accomplished writer <em>and</em> someone who has helped ship a whole bunch of Ember's latest &amp; greatest features. See posts <a href="https://medium.com/@pzuraq">here</a> and <a href="https://www.pzuraq.com/">here</a>.
In another example, core team member <a href="https://github.com/MelSumner">Melanie Sumner</a> wrote an <a href="https://guides.emberjs.com/release/reference/accessibility-guide/">Accessibility Guide</a> for the official Ember Guides, gave talks at accessibility-focused events throughout the year, and is working on framework code to make Ember better out-of-the-box for people who use assistive technology like screen readers.
<a href="https://github.com/jessica-jordan">Jessica Jordan</a> is speaking at this year's <a href="https://2019.jsconf.eu/">JSConf EU</a> - a huge achievement - on web comics and animation.
The list goes on and on.</p>
<blockquote>
  <p>Future-Ember provided approachable, current, convincing materials for new visitors.</p>
</blockquote>
<p>A lot of this work has been completed, and the next 3 months will be the true testing grounds for its effectiveness.</p>
<p>Most notably, the outcome of the 2018 Roadmap process was <a href="https://github.com/emberjs/rfcs/pull/364">this RFC</a>, which introduced the first-ever <a href="https://emberjs.com/editions/">"edition"</a> of Ember, called Octane. Octane is currently in a <a href="https://emberjs.com/editions/octane">preview period</a> before its official release.
The purpose of Octane is to pull together all the latest features of Ember into one cohesive (non-breaking) experience.
It looks a whole lot more like regular JavaScript, with more explicit signals about which parts of an app are "special Ember things."
Hopefully, as a result, it will be easier for new devs to pick up.</p>
<h2 id="beingready">Being ready</h2>
<p>Here's what I said was needed last year. Team… we did all of these. If you helped out with them, thank you so much.</p>
<blockquote>
  <p>A refactor of (at least) the first page of every topic within the Guides, to make them friendlier and more readable… Ember has a large group of community members who I believe are ready and willing to help out.</p>
  <p>"Breaking" some of the Guides urls that have been held as canon.</p>
</blockquote>
<p>While some things still need work, there were close about 400 Guides PRs over the past year. Four. Hundred.
Along the way, I ended up <a href="https://github.com/emberjs/rfcs/pull/431">writing an RFC</a> to help jumpstart an even deeper set of revisions, and it was ultimately accomplished by over 40 contributors as part of work to prepare for the Octane preview.</p>
<blockquote>
  <p>Current addon authors could help lead a refactor of the CLI docs. Newer people can help with writing and making sure that the results are as helpful as possible.</p>
</blockquote>
<p>This also had an <a href="https://github.com/ember-cli/rfcs/pull/120">RFC</a> that described the plan. Again, over 40 people helped to migrate content from the old site, remove outdated/incorrect info, and write some new things.
For example, we now have an official tutorial for anyone who wants to write an addon.
It's all live at <a href="https://cli.emberjs.com">cli.emberjs.com</a>.
There's still work to do to clean up, like adding redirects from the old site, but we can do it!</p>
<blockquote>
  <p>Freshen up the look and feel of our main "marketing" resources like the home page.</p>
</blockquote>
<p>The <a href="https://github.com/emberjs/rfcs/pull/425">proposed website redesign</a> looks phenomenal.
It's inviting, modern, and really signals that Ember is something worth paying attention to.
<a href="https://github.com/samselikoff">Sam Selikoff</a>, <a href="https://github.com/wifelette">Leah Silber</a>, and others were instrumental to bringing this design to the forefront. People like <a href="https://github.com/MelSumner">Melanie Sumner</a>, <a href="https://github.com/mansona">Chris Manson</a>, and the Ember Learning Team laid the infra groundwork that makes it possible to do a full rebranding, thanks to some public website overhauls and the <a href="https://github.com/ember-learn/ember-styleguide">ember-styleguide</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I wouldn't have said this in previous years, but based on everything that's happened latey, I think we are actually on track to grow Ember's popularity and use.
I'm constantly in awe of the trailblazing going on in this community, in terms of diversity, inclusion, accessibility, removing barriers to entry, maintainability, cooperation across national borders, and more.
If people take notice of our tech, maybe they will take notice of these other things too, and spread them throughout the industry.
Even if people don't use our code, maybe our work will still make things just a little bit better for everytone.</p>
<p>It's easier than ever to be optimistic.</p>]]></description><link>https://jenweber.dev/revisiting-my-2018-ember.js-roadmap-blog-post</link><guid isPermaLink="true">https://jenweber.dev/revisiting-my-2018-ember.js-roadmap-blog-post</guid><pubDate>Tue, 26 Mar 2019 21:07:29 GMT</pubDate></item><item><title><![CDATA[The Ember Experience - 2019 Roadmap post]]></title><description><![CDATA[<p>If I imagine life after <a href="https://emberjs.com/editions/octane">Octane</a> lands, I can see a few areas that need work: the learning story for new Ember devs and adopting accessibility as a framework feature.</p>
<p>This post is in response to Ember's <a href="https://blog.emberjs.com/2019/05/20/ember-2019-roadmap-call-for-posts.html">Call for Roadmap  Blog posts</a>, and I invite you to write one of your own! What do you think Ember should focus on over the next year? I wrote <a href="https://medium.com/r/?url=https%3A%2F%2Fgist.github.com%2Fjenweber%2Fa9fbea98478fc3841fb8b24f7dc961c8">one of these last year too</a>, and you can read <a href="../revisiting-my-2018-ember.js-roadmap-blog-post">here</a> about how Ember hit nearly all the goals I was hoping for!</p>
<h2 id="whatistheemberexperience">What is the Ember Experience?</h2>
<p>The ideal Ember Experience is that things work as they should out-of-the-box, following modern web development patterns. It's easy to get ramped up using real-world code examples that teach the recommended practices used by experienced Ember developers, from day one.</p>
<p>What do we need to fix or build so we can get there?</p>
<h2 id="letsteachpeopletousethethingswereproudnbspof">Let's teach people to use the things we're proud&nbsp;of</h2>
<p>Take a look at <a href="https://vuevixens.github.io/docs/workshop/">Vue Vixen's library</a> of open-source workshops. It's inspiring to see what it looks like when framework learning and teaching is done exceptionally well! While we may not have the same resources as other frameworks, whatever we have set as priorities, we have consistently achieved. I've heard other people say that the <a href="https://guides.emberjs.com">Ember Guides</a> are great and some of the best docs out there, but we need to reach higher.</p>
<p>Why should teaching and documentation be a priority of the entire Ember organization? As many as 50% of visitors to the Ember Guides and Tutorials are visiting for the first time. (Either that, or a ton of you are very good about using Incognito mode on your browsers, or other tools that block Google Analytics.)</p>
<h3 id="aproblemtofix">A problem to fix</h3>
<p>There's a problem though. The majority of Ember's Core Team members have never done any writing for the Guides.</p>
<p>Thankfully, there are a zillion community members who help keep the guides up to date and improve them over time, at a pace of <a href="https://github.com/ember-learn/guides-source/pulse">30–40 PRs per month</a>. Although it takes up most of my open source time, whatever time I spend on reviewing PRs is always time well spent, and I am so grateful for the participation.</p>
<p><strong>However, the voice of Ember's leadership is largely missing from the materials that are most often used.</strong>
&nbsp;
Everyone has their area of specialization, and many core team members do their own kind of docs writing for the APIs, deprecations, etc, but I think we need to do better. Even inside the learning core team, we're not focused on long-lived, most-used content. The group focus is on website infra and the <a href="https://the-emberjs-times.ongoodbits.com/">Times newsletter</a>. Only a couple people do most of the Guides work. We're off-balance, and we need more core team members to be engaged.</p>
<h3 id="howtomovethenbspneedle">How to move the&nbsp;needle</h3>
<p>These are the main areas where we could make some changes:</p>
<ol>
<li>We could make documentation and learning be a focus for 2019–2020, meaning that the core teams would commit to doing more work to help teach the tools that we are all guiding and building.&nbsp;</li>
<li>We add more writing-focused people to the core teams (all four teams)</li>
<li>We support the creation of "less official" Ember resources, which could serve as a training ground for new writers. It's hard writing within the constraints of the Guides, so cultivating other resources is good practice and enriches the knowledge base. The Guides are the "happy path" but we need other places that document the weird stuff.</li>
<li>We do a better job of collaborating with community members who could help with big changes. There's too much risk currently for most non-core people to do something like devote a full week to helping revise docs- would their work ever get merged? Or will it get lost in back &amp; forth about the right way to teach Ember? I'm willing to be a bit reckless with the Guides merge button, but the rest of the teams need to be on board.</li>
<li>We set a specific, time-based goal of writing another Ember Tutorial to go with Super Rentals and the Quickstart. If there are no community volunteers, we do it ourselves.</li>
<li>We consider granting PR review &amp; merge abilities to non-core team members, for the Guides only. This distributes review responsibility so that seasoned writers/reviewers can spend more time planning the next big steps, getting new writers involved, and writing itself.</li>
<li>We get some kind of code block tests in place. We burn a lot of time fixing formatting and mistakes that could be caught by a JavaScript parser. It's more "infra" work, but in direct service of learning quality, and recaptures some time burned by the current writers &amp; maintainers.</li>
</ol>
<p>There are some downsides to these points. If core team members were to help more with the guides, it would mean they were doing less of something else. What can we cut? What can we do more efficiently? Which things can become the responsibility of someone else, through mentorship and leadership as opposed to individual contributions? Who is bored of what they are doing, or has been uninvolved lately, and wants to try something new? This will take discussion.</p>
<h2 id="embracethelandofcopypaste">Embrace the land of copy-paste</h2>
<p>Another achievable, realistic way to level up the guides is to give better code examples. Copying and pasting code is an important part of how people learn and work, so our resources should make that easy and safe to do.</p>
<p>Today, if you copy and paste from Ember's official Guides, there are problems. For example, the Guides show input text boxes over and over, but only a small percentage of those samples have a <code>&lt;label&gt;</code>. Sure, the article focus is on teaching how to use the component, not how to write HTML. However, if we assume that most people will copy and paste, and we want Ember users to follow best practices for the web, we should step up our game. There are a ton of little mistakes like that which impact accessibility, for example, plus browser compatibility.</p>
<h3 id="morerealworldexamples">More real-world examples</h3>
<p>To that end, we should create more real-world examples for Ember and make them available to our users. For example, we teach how to use form inputs, but not how to make a fully-functional, accessible form. A long time ago, there was a section of the Guides called the Cookbook which tried to do this, but it was unmaintained and eventually removed.
The only way we can deliver is if we have more people engaged in writing and maintaining Ember's documentation and tutorials, at a deep level.</p>
<h2 id="accessibilityasaframeworkfeature">Accessibility as a framework feature</h2>
<p>Accessibility is a big theme in Ember lately, and I'm a huge fan of any progress in that area. Since Ember covers so much of the concerns of front end development, it's not a giant leap to roll accessibility into Ember's out-of-the-box experience.</p>
<p>A focus on accessibility gives Ember a competitive edge that can attract companies who must follow accessibility standards, and developers who care about making their work usable by everyone. Our outside image is important, and how people feel while using Ember apps is important.</p>
<p>As a long-lived library and community, we get the chance to solve the really hard problems, the kinds of things that can change the web. We should make that happen.</p>
<p>To give some concrete examples, I think the <a href="https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-invalid-interactive.md">accessibility linting rules</a> were a huge success, and we should make the next moves to incorporate ember-a11y tools as the standard. We could become the leader among SPA frameworks for solving things like changing routes in a way that screen readers understand.&nbsp;</p>
<p>For more on this, read Y<a href="https://yehudakatz.com/2019/05/20/ember-2019/">ehuda Katz' 2019 Roadmap Blog Post</a> as well as <a href="http://www.melsumner.com/blog/ember/wishing-on-a-star/">Melanie Sumner's post</a> from last year.</p>
<h2 id="businessusecase">Business use case</h2>
<p>These aspects of Ember have a real impact on my work at <a href="https://cardstack.com">Cardstack</a>. Ember.js is a key dependency of the Card SDK. Any improvements we make to the Ember Guides will help me teach Cardstack to new devs. Any improvements we make to accessibility help our products gain traction with enterprise customers.</p>
<p>Other companies benefit in the same way on the accessibility front. When it comes to learning materials, there's a real cost to <em>not</em> investing in this area, since most companies using Ember train at least some of their developers in-house.
It's totally reasonable for a dev with experience in React, Vue, or Angular to learn Ember quickly.
We have an opportunity to make onboarding easier for these companies, and if we do it well, new teams who want to adopt Ember can confidently do so.</p>
<h2 id="howtoparticipateinthenbsproadmap">How to participate in the&nbsp;Roadmap</h2>
<p>Please write your own blog post for the <a href="https://blog.emberjs.com/2019/05/20/ember-2019-roadmap-call-for-posts.html">Ember 2019 Roadmap</a>! Even if it's just to say you agree/disagree with someone else, we want to hear your voice. It has a bearing on the outcome. Thanks for reading!</p>]]></description><link>https://jenweber.dev/the-ember-experience</link><guid isPermaLink="true">https://jenweber.dev/the-ember-experience</guid><pubDate>Sun, 02 Jun 2019 01:38:15 GMT</pubDate></item></channel></rss>