{"version":"https://jsonfeed.org/version/1","title":"Hashrocket - Javascript Posts","home_page_url":"https://hashrocket.com/blog","feed_url":"https://hashrocket.com/blog/tags/javascript.json","author":{"name":"Hashrocket","url":"https://hashrocket.com/","avatar":"https://hashrocket.com/favicon-228.png"},"items":[{"id":"https://hashrocket.com/blog/posts/optimizing-ux-and-efficiency-with-visibilitystate","url":"https://hashrocket.com/blog/posts/optimizing-ux-and-efficiency-with-visibilitystate","title":"Optimizing UX and Efficiency with VisibilityState","content_html":"Let's explore something super cool yet often overlooked in web development: the `visibilityState` property. This little gem is part of the [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API), and trust me, it's a game-changer for enhancing user experience and managing resources in web apps.\n\nSo, what's this `visibilityState` all about? Imagine you're juggling multiple tabs on your browser (we've all been there), and there's this one tab you're not looking at. You may have switched to chat with a friend, or you may be deep-diving into another webpage. That's where `visibilityState` steps in. It tells developers whether you're actively looking at their webpage or if it's chilling in the background.\r\n\r\nHere's the deal with its states:\r\n\r\n1. **Visible**: The page is right there in front of you. It's showtime for the webpage, and it can run animations, play videos, or do anything that needs your eyeballs on it.\r\n2. **Hidden**: You've moved to another tab or minimized the browser. The webpage goes into stealth mode, pausing things like videos or animations because, well, you're not there to see them.\r\n\r\nWhy care about this? Because it's all about making websites more intelligent and considerate of your device's resources. For instance, why play a video if no one's watching? Pausing non-essential tasks when you're not looking saves battery life and data—especially handy on mobile devices.\r\n\r\nLet's talk about real-life applications. Using `visibilityState` alongside the `visibilitychange` event is like having an intelligent assistant for your webpage. It can pause a video when you switch tabs and then, if you were watching it, resume playing once you come back. Neat, right?\r\n\r\nHere's a quick code snippet to show how you can pause and resume a video based on the page's visibility:\r\n\r\n```javascript\r\ndocument.addEventListener(\"visibilitychange\", function() {\r\n    var video = document.getElementById('exampleVideo');\r\n\r\n    if (document.visibilityState === 'hidden') {\r\n        video.pause();\r\n    } else {\r\n        video.play();\r\n    }\r\n});\r\n```\r\n\r\nThis approach enhances the user experience by not missing a beat of the video and smartly manages resources by not playing it to an empty audience.\r\n\r\nIn a nutshell, `visibilityState` and the Page Visibility API are your secret ingredients for creating responsive, efficient, and user-friendly web applications. By tuning in to a webpage's visibility, your applications can act more intelligently, offering a smoother ride for users while being kind to their device's resources.\r\n\r\nAnd that's a wrap! Remember, sometimes, the most impactful enhancements come from the simplest changes. Happy coding!","content_text":"Let's explore something super cool yet often overlooked in web development: the visibilityState property. This little gem is part of the Page Visibility API, and trust me, it's a game-changer for enhancing user experience and managing resources in web apps.\n\nSo, what's this visibilityState all about? Imagine you're juggling multiple tabs on your browser (we've all been there), and there's this one tab you're not looking at. You may have switched to chat with a friend, or you may be deep-diving into another webpage. That's where visibilityState steps in. It tells developers whether you're actively looking at their webpage or if it's chilling in the background.\n\nHere's the deal with its states:\n\n\nVisible: The page is right there in front of you. It's showtime for the webpage, and it can run animations, play videos, or do anything that needs your eyeballs on it.\nHidden: You've moved to another tab or minimized the browser. The webpage goes into stealth mode, pausing things like videos or animations because, well, you're not there to see them.\n\n\nWhy care about this? Because it's all about making websites more intelligent and considerate of your device's resources. For instance, why play a video if no one's watching? Pausing non-essential tasks when you're not looking saves battery life and data—especially handy on mobile devices.\n\nLet's talk about real-life applications. Using visibilityState alongside the visibilitychange event is like having an intelligent assistant for your webpage. It can pause a video when you switch tabs and then, if you were watching it, resume playing once you come back. Neat, right?\n\nHere's a quick code snippet to show how you can pause and resume a video based on the page's visibility:\ndocument.addEventListener(\"visibilitychange\", function() {\n    var video = document.getElementById('exampleVideo');\n\n    if (document.visibilityState === 'hidden') {\n        video.pause();\n    } else {\n        video.play();\n    }\n});\n\nThis approach enhances the user experience by not missing a beat of the video and smartly manages resources by not playing it to an empty audience.\n\nIn a nutshell, visibilityState and the Page Visibility API are your secret ingredients for creating responsive, efficient, and user-friendly web applications. By tuning in to a webpage's visibility, your applications can act more intelligently, offering a smoother ride for users while being kind to their device's resources.\n\nAnd that's a wrap! Remember, sometimes, the most impactful enhancements come from the simplest changes. Happy coding!\n","summary":"Let's explore something super cool yet often overlooked in web development: the visibilityState property. This little gem is part of the Page Visibility API, and trust me, it's a game-changer for enhancing user experience and managing resources in web apps.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/697/visibilitystate.jpg","date_published":"2024-03-19T09:00:00-04:00","data_modified":"2024-03-11T23:06:31-04:00","author":{"name":"Matt Polito","url":"https://hashrocket.com/team/matt-polito","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/35/matt-polito.jpg"},"tags":["Javascript","HTML"]},{"id":"https://hashrocket.com/blog/posts/the-impact-of-the-exclamation-point-in-typescript","url":"https://hashrocket.com/blog/posts/the-impact-of-the-exclamation-point-in-typescript","title":"The Impact of the Exclamation Point in TypeScript","content_html":"In TypeScript, a language built on top of JavaScript to add static typing, the exclamation point `!` is not just a symbol of excitement or emphasis as it is in English; it plays a critical role in the type system. This article delves into the significance of the exclamation point and how it influences TypeScript's static analysis of code.\n\n## Non-Null Assertion Operator\r\n\r\nThe primary use of the exclamation point in TypeScript is as the \"non-null assertion operator.\" In a language that embraces types to reduce runtime errors, TypeScript introduces a way to tell the compiler, \"trust me, I know what I'm doing.\" The `!` operator, when appended to the end of an expression, tells TypeScript that the value is not null or undefined.\r\n\r\nHere’s a quick example:\r\n\r\n```typescript\r\nlet maybeString: string | null = document.querySelector('.myInput').value;\r\nconsole.log(maybeString.length); // Error: Object is possibly 'null'.\r\nconsole.log(maybeString!.length); // No error\r\n```\r\n\r\nThe first console log will flag a potential error in this code snippet because `maybeString` could be `null`. However, when we append `!`, we assert that we know `maybeString` will have a value, thus negating the null check and preventing the TypeScript error.\r\n\r\n## Why Use It?\r\n\r\nThe question arises: why would you need to bypass TypeScript's safety net? In specific scenarios, a developer is more aware of the context than TypeScript can be. For instance, you might know that a DOM element exists because it's statically in the HTML, but TypeScript can't assume that as it checks the code without DOM context. In such cases, `!` allows for more flexible and assertive coding.\r\n\r\n## The Risks\r\n\r\nThe non-null assertion operator is powerful but should be used judiciously. Overuse or misuse can undermine the very purpose of TypeScript, which is to catch errors at compile time rather than at runtime. If you're wrong about your assertion, you will have silenced TypeScript's warning only to encounter a potential runtime error.\r\n\r\n## Best Practices\r\n\r\n- **Safety First**: Use the non-null assertion operator only when you have absolute certainty that a value will not be null or undefined.\r\n- **Runtime Guarantees**: Whenever possible, ensure runtime guarantees for the presence of values rather than relying on static assertions.\r\n- **Code Smells**: Frequent use of `!` might indicate a code smell. Consider if your code structure can be refactored to better utilize TypeScript's type system.\r\n\r\n## Conclusion\r\n\r\nThe exclamation point in TypeScript is a subtle but powerful tool. It exemplifies TypeScript's flexible approach to typing, allowing developers to override the compiler's strictness when they have specific knowledge about the values in their code. However, with great power comes great responsibility. It should be used sparingly and wisely, as an incorrect assertion can lead to the very bugs TypeScript aims to prevent. When used correctly, the `!` operator can be a declaration of confidence in the face of potential nullability, allowing seamless integration of static typing into the dynamic world of JavaScript.","content_text":"In TypeScript, a language built on top of JavaScript to add static typing, the exclamation point ! is not just a symbol of excitement or emphasis as it is in English; it plays a critical role in the type system. This article delves into the significance of the exclamation point and how it influences TypeScript's static analysis of code.\nNon-Null Assertion Operator\n\nThe primary use of the exclamation point in TypeScript is as the \"non-null assertion operator.\" In a language that embraces types to reduce runtime errors, TypeScript introduces a way to tell the compiler, \"trust me, I know what I'm doing.\" The ! operator, when appended to the end of an expression, tells TypeScript that the value is not null or undefined.\n\nHere’s a quick example:\nlet maybeString: string | null = document.querySelector('.myInput').value;\nconsole.log(maybeString.length); // Error: Object is possibly 'null'.\nconsole.log(maybeString!.length); // No error\n\nThe first console log will flag a potential error in this code snippet because maybeString could be null. However, when we append !, we assert that we know maybeString will have a value, thus negating the null check and preventing the TypeScript error.\nWhy Use It?\n\nThe question arises: why would you need to bypass TypeScript's safety net? In specific scenarios, a developer is more aware of the context than TypeScript can be. For instance, you might know that a DOM element exists because it's statically in the HTML, but TypeScript can't assume that as it checks the code without DOM context. In such cases, ! allows for more flexible and assertive coding.\nThe Risks\n\nThe non-null assertion operator is powerful but should be used judiciously. Overuse or misuse can undermine the very purpose of TypeScript, which is to catch errors at compile time rather than at runtime. If you're wrong about your assertion, you will have silenced TypeScript's warning only to encounter a potential runtime error.\nBest Practices\n\n\nSafety First: Use the non-null assertion operator only when you have absolute certainty that a value will not be null or undefined.\nRuntime Guarantees: Whenever possible, ensure runtime guarantees for the presence of values rather than relying on static assertions.\nCode Smells: Frequent use of ! might indicate a code smell. Consider if your code structure can be refactored to better utilize TypeScript's type system.\n\nConclusion\n\nThe exclamation point in TypeScript is a subtle but powerful tool. It exemplifies TypeScript's flexible approach to typing, allowing developers to override the compiler's strictness when they have specific knowledge about the values in their code. However, with great power comes great responsibility. It should be used sparingly and wisely, as an incorrect assertion can lead to the very bugs TypeScript aims to prevent. When used correctly, the ! operator can be a declaration of confidence in the face of potential nullability, allowing seamless integration of static typing into the dynamic world of JavaScript.\n","summary":"In TypeScript, a language built on top of JavaScript to add static typing, the exclamation point ! is not just a symbol of excitement or emphasis as it is in English; it plays a critical role in the type system. This article delves into the significance of the exclamation point and how it influences TypeScript's static analysis of code.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/658/exclamation_mark.jpeg","date_published":"2023-11-09T09:00:00-05:00","data_modified":"2023-11-09T00:06:35-05:00","author":{"name":"Matt Polito","url":"https://hashrocket.com/team/matt-polito","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/35/matt-polito.jpg"},"tags":["Javascript","Typescript"]},{"id":"https://hashrocket.com/blog/posts/an-alternative-for-tricky-nodejs-installations","url":"https://hashrocket.com/blog/posts/an-alternative-for-tricky-nodejs-installations","title":"An alternative for tricky Node.js installations","content_html":"Ever had a super difficult time getting a [Node](https://nodejs.org/) version to install? Especially on an Apple Silicon-powered device?\r\n\r\nI use [`asdf`](https://asdf-vm.com) with the [node plugin](https://github.com/asdf-vm/asdf-nodejs) and couldn't get a few older versions of Node to install. \n\nFor example:\r\n\r\n```\r\nasdf install nodejs 13.9.0\r\n```\r\n\r\nThe failed install angered me further when I realized that installing with [NVM](https://github.com/nvm-sh/nvm) did not have this issue.\r\n\r\nWhile this knowledge angered me, I had no intention of utilizing two version managers. That's when I noticed that `asdf` tries to install the node version with [`node-build`](https://github.com/nodenv/node-build) while `nvm` downloaded a version from nodejs.org.\r\n\r\nSo I looked at the NVM version, and the folder structure looked the same as I was used to seeing in `asdf`'s installed folder (`~/.asdf/installs/nodejs`), which gave me an idea.\r\n\r\nWhat would happen if I just placed the contents where `asdf` would expect them to be? It works!\r\n\r\nYou can 'url hack' your way to the version you need via `https://nodejs.org/dist/v{VERSION_NUMBER}/`. That URL will take you to a listing of all the compiled entries for that Node version.\r\n\r\n![Listing of compiled node entries for version 13.9.0](https://i.imgur.com/nGYKHPR.png)\r\n\r\nBeing on an M1, I chose the Darwin compiled entry, `node-v13.9.0-darwin-x64.tar.gz`.\r\n\r\nJust uncompress the file and place it inside the folder asdf expects installs to be, like so: `~/.asdf/installs/nodejs/13.9.0`\r\n\r\nAfter that, I saw that the new Node version was already available and ready for use.\r\n\r\n![Listing of installed NodeJS versions](https://i.imgur.com/fBvqnsO.png)\r\n\r\nIf you are having difficulties installing a particular NodeJS version, this method could save you considerable time.","content_text":"Ever had a super difficult time getting a Node version to install? Especially on an Apple Silicon-powered device?\n\nI use asdf with the node plugin and couldn't get a few older versions of Node to install. \n\nFor example:\nasdf install nodejs 13.9.0\n\nThe failed install angered me further when I realized that installing with NVM did not have this issue.\n\nWhile this knowledge angered me, I had no intention of utilizing two version managers. That's when I noticed that asdf tries to install the node version with node-build while nvm downloaded a version from nodejs.org.\n\nSo I looked at the NVM version, and the folder structure looked the same as I was used to seeing in asdf's installed folder (~/.asdf/installs/nodejs), which gave me an idea.\n\nWhat would happen if I just placed the contents where asdf would expect them to be? It works!\n\nYou can 'url hack' your way to the version you need via https://nodejs.org/dist/v{VERSION_NUMBER}/. That URL will take you to a listing of all the compiled entries for that Node version.\n\n\n\nBeing on an M1, I chose the Darwin compiled entry, node-v13.9.0-darwin-x64.tar.gz.\n\nJust uncompress the file and place it inside the folder asdf expects installs to be, like so: ~/.asdf/installs/nodejs/13.9.0\n\nAfter that, I saw that the new Node version was already available and ready for use.\n\n\n\nIf you are having difficulties installing a particular NodeJS version, this method could save you considerable time.\n","summary":"Ever had a super difficult time getting a Node version to install? Especially on an Apple Silicon-powered device?\n\nI use asdf with the node plugin and couldn't get a few older versions of Node to install. \n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/657/mattpolito_alternate_paths_in_the_style_of_conceptual_installat_92149454-69bb-4ace-8263-5bb7c47e517f__1__Large.jpeg","date_published":"2023-08-10T09:00:00-04:00","data_modified":"2023-08-09T22:00:03-04:00","author":{"name":"Matt Polito","url":"https://hashrocket.com/team/matt-polito","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/35/matt-polito.jpg"},"tags":["NodeJS","Javascript","asdf","NVM"]},{"id":"https://hashrocket.com/blog/posts/evaluating-javascript-dependencies-with-codesandbox","url":"https://hashrocket.com/blog/posts/evaluating-javascript-dependencies-with-codesandbox","title":"Evaluating JavaScript Dependencies with CodeSandbox","content_html":"While building a large React SPA, we reached a point where we needed to cut\r\ndown the bundle size a bit. We identified some candidate dependencies --\r\npackages that were being used sparingly but had a large footprint on the\r\nbundle. We then found some alternatives to those packages with a fraction of\r\nthe bundle size. We just needed to be sure that these packages could do what\r\nwe needed them to do. This is where [CodeSandbox](https://codesandbox.io/)\r\ncomes in.\n\nWe decided that we wanted to try to replace\r\n[`moment.js`](https://momentjs.com/) with\r\n[`day.js`](https://github.com/iamkun/dayjs). Before making the switch, we wanted\r\nto be sure that `day.js` could do the things we needed it to do.\r\n\r\nSo, we spun up a [fresh, vanilla Javascript\r\nCodeSandbox](https://codesandbox.io/s/vanilla).\r\n\r\n![Add Dependency Modal](https://i.imgur.com/NcYtSsS.png)\r\n\r\nUnder the _Dependencies_ tab in the _File Editor_ section is a big, blue\r\nbutton -- _Add Dependency_. That button pops open a search modal that allows\r\nyou to find packages from the [npm registry](https://www.npmjs.com/). Start\r\ntyping `day.js` and it will quickly show up as one of the results. Click on\r\nit and it will be added as a dependency of the project.\r\n\r\nWe can now give `day.js` a test run in this isolated environment. We can\r\ncompare what we are seeing in the docs with what we are able to get working\r\nin this sandbox environment. We were primarily using `moment.js` for\r\nformatting dates, so we went to work reproducing our formatting utilities\r\nwith `day.js`.\r\n\r\nThis not only gave us confidence that `day.js` could do the things we needed\r\nit to do. It also gave us a tangible artifact that we could pass around to\r\nothers on the team to get buy-in on the change. Once you save a CodeSandbox\r\ninstance, you get a unique URL that you can pass around to anyone.\r\n\r\nNow that we were satisfied with `day.js` and had buy-in from the rest of the\r\nteam, we could set out to create a pull request that would make the switch.\r\n\r\nCodeSandbox has become invaluable to many parts of my workflow. But I've\r\nespecially come to depend on it for evaluating my dependencies.\r\n\r\n---\r\n\r\nCover photo: \u003ca style=\"background-color:black;color:white;text-decoration:none;padding:4px 6px;font-family:-apple-system, BlinkMacSystemFont, \u0026quot;San Francisco\u0026quot;, \u0026quot;Helvetica Neue\u0026quot;, Helvetica, Ubuntu, Roboto, Noto, \u0026quot;Segoe UI\u0026quot;, Arial, sans-serif;font-size:12px;font-weight:bold;line-height:1.2;display:inline-block;border-radius:3px\" href=\"https://unsplash.com/@wolfgang_hasselmann?utm_medium=referral\u0026amp;utm_campaign=photographer-credit\u0026amp;utm_content=creditBadge\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Download free do whatever you want high-resolution photos from Wolfgang Hasselmann\"\u003e\u003cspan style=\"display:inline-block;padding:2px 3px\"\u003e\u003csvg xmlns=\"http://www.w3.org/2000/svg\" style=\"height:12px;width:auto;position:relative;vertical-align:middle;top:-2px;fill:white\" viewBox=\"0 0 32 32\"\u003e\u003ctitle\u003eunsplash-logo\u003c/title\u003e\u003cpath d=\"M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/span\u003e\u003cspan style=\"display:inline-block;padding:2px 3px\"\u003eWolfgang Hasselmann\u003c/span\u003e\u003c/a\u003e","content_text":"While building a large React SPA, we reached a point where we needed to cut\ndown the bundle size a bit. We identified some candidate dependencies --\npackages that were being used sparingly but had a large footprint on the\nbundle. We then found some alternatives to those packages with a fraction of\nthe bundle size. We just needed to be sure that these packages could do what\nwe needed them to do. This is where CodeSandbox\ncomes in.\n\nWe decided that we wanted to try to replace\nmoment.js with\nday.js. Before making the switch, we wanted\nto be sure that day.js could do the things we needed it to do.\n\nSo, we spun up a fresh, vanilla Javascript\nCodeSandbox.\n\n\n\nUnder the Dependencies tab in the File Editor section is a big, blue\nbutton -- Add Dependency. That button pops open a search modal that allows\nyou to find packages from the npm registry. Start\ntyping day.js and it will quickly show up as one of the results. Click on\nit and it will be added as a dependency of the project.\n\nWe can now give day.js a test run in this isolated environment. We can\ncompare what we are seeing in the docs with what we are able to get working\nin this sandbox environment. We were primarily using moment.js for\nformatting dates, so we went to work reproducing our formatting utilities\nwith day.js.\n\nThis not only gave us confidence that day.js could do the things we needed\nit to do. It also gave us a tangible artifact that we could pass around to\nothers on the team to get buy-in on the change. Once you save a CodeSandbox\ninstance, you get a unique URL that you can pass around to anyone.\n\nNow that we were satisfied with day.js and had buy-in from the rest of the\nteam, we could set out to create a pull request that would make the switch.\n\nCodeSandbox has become invaluable to many parts of my workflow. But I've\nespecially come to depend on it for evaluating my dependencies.\n\n\n\nCover photo: unsplash-logoWolfgang Hasselmann\n","summary":"While building a large React SPA, we reached a point where we needed to cut\ndown the bundle size a bit. We identified some candidate dependencies --\npackages that were being used sparingly but had a large footprint on the\nbundle. We then found some alternatives to those packages with a fraction of\nthe bundle size. We just needed to be sure that these packages could do what\nwe needed them to do. This is where CodeSandbox\ncomes in.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/465/evaluating-javascript-dependencies.jpg","date_published":"2019-03-26T09:00:00-04:00","data_modified":"2019-02-25T12:21:50-05:00","author":{"name":"Josh Branchaud","url":"https://hashrocket.com/team/josh-branchaud","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/88/josh-branchaud.jpg"},"tags":["Javascript"]},{"id":"https://hashrocket.com/blog/posts/write-a-reduce-function-from-scratch","url":"https://hashrocket.com/blog/posts/write-a-reduce-function-from-scratch","title":"Write A Reduce Function From Scratch","content_html":"JavaScript's `Array` object has a built-in [`reduce` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce). This is a handy,\r\nyet underutilized method. Let's take a closer look at what `reduce` does and\r\nsee if we can implement it from scratch.\n\n## Reduce?\r\n\r\nHere is what\r\n[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)\r\nhas to say about `reduce`:\r\n\r\n\u003e The `reduce()` method executes a *reducer* function (that you provide) on each\r\n\u003e member of the array resulting in a single output value.\r\n\r\nSo, we can take an array of things and turn it into something else using a\r\nfunction that we get to define.\r\n\r\n```javascript\r\nconst sum = list =\u003e list.reduce((total, val) =\u003e total + val, 0);\r\n\r\nsum([1,2,3,4]) // =\u003e 10\r\n```\r\n\r\nThe `reduce` method can seem pretty limiting at first when we read that it\r\nproduces \"a single output value.\" That makes it sound like all it is good\r\nfor are things like a `sum` function. The `reduce` method can also produce\r\nan array as its _single output value_. Objects too!\r\n\r\n```javascript\r\nconst countWords = wordList =\u003e {\r\n  return wordList.reduce((acc, word) =\u003e {\r\n    acc[word] = (acc[word] || 0) + 1;\r\n    return acc;\r\n  }, {});\r\n};\r\n\r\ncountWords([\"hello\", \"world\", \"hello\", \"dogs\", \"hello\", \"cats\"]);\r\n// =\u003e {hello: 3, world: 1, dogs: 1, cats: 1}\r\n```\r\n\r\nThis method can do just about anything. It's very versatile. I'll say more\r\nabout that at the end.\r\n\r\n## Implement Our Own\r\n\r\nLet's push our understanding of `reduce` a bit further by implementing our\r\nown.\r\n\r\nWhat we ought to have noticed in the examples above is that there are three\r\nstandard parts to a `reduce`. First, there is the _list_ to be reduced. Then\r\nthere is always some _initial value_. For `sum` it was `0` and for\r\n`countWords` it was `{}`. Third, and perhaps most importantly, is our\r\n_reducer function_.\r\n\r\nWe can stub out a naive version with just this knowledge:\r\n\r\n```javascript\r\nconst myReduce = (list, initialValue, reducer) =\u003e {\r\n  return initialValue;\r\n}\r\n```\r\n\r\nThis naive implementation will even work in cases where `list` is empty.\r\n\r\n```\r\nmyReduce([], 0, (total, val) =\u003e total + val); // =\u003e 0\r\n```\r\n\r\nThat there is an important observation. Once we have an empty list, we\r\nshould return whatever our `initialValue` is. This is what is known as our\r\n_base case_ or _terminating case_. The full implementation will involve\r\nrecursive calls of itself, so it is important to understand when and how we\r\n_terminate_ the process.\r\n\r\n```javascript\r\nconst myReduce = (list, initialValue, reducer) =\u003e {\r\n  if(list.length === 0) {\r\n    return initialValue;\r\n  }\r\n}\r\n```\r\n\r\nWe are still missing the meat of the function. We need to decide what\r\nhappens when there are items left in the list. This is where our `reducer`\r\nfunction comes into the picture.\r\n\r\n## The Reducer\r\n\r\nThe general shape of the `reducer` function is important. It takes two\r\nvalues as arguments: the _accumulator_, what everything is being reduced\r\ninto, and the current value we are reducing. It then performs some reduction\r\nlogic and returns a new version of the _accumulator_.\r\n\r\nLet's look back at our `reducer` function for `sum`:\r\n\r\n```javascript\r\n(total, val) =\u003e total + val\r\n```\r\n\r\nThe _accumulator_ is the `total` of our summing so far and the current value is\r\n`val`. We had those two values together, returning them as the new version\r\nof the _accumulator_.\r\n\r\nThe `reducer` function for `countWords` is a bit more complicated:\r\n\r\n```javascript\r\n(acc, word) =\u003e {\r\n  acc[word] = (acc[word] || 0) + 1;\r\n  return acc;\r\n}\r\n```\r\n\r\nThe _accumulator_ is some object full of words and respective counts. We\r\nhave to first update the _accumulator_ with the next word accounting for\r\nwhether or not it is the first time we've seen this word. Then the updated\r\n_accumulator_ is returned.\r\n\r\n## Now Reduce\r\n\r\nWith that in mind, let's handle the case where we get to use the given\r\n`reducer` function.\r\n\r\n\r\n```javascript\r\nconst myReduce = (list, initialValue, reducer) =\u003e {\r\n  if(list.length === 0) {\r\n    return initialValue;\r\n  } else {\r\n    const [first, ...rest] = list;\r\n    const updatedAcc = reducer(initialValue, first);\r\n    return myReduce(rest, updatedAcc, reducer);\r\n  }\r\n}\r\n```\r\n\r\nWe need to access the next value to be reduced, hence the list\r\ndestructuring. We then apply the `reducer` function with the `initialValue`\r\nand that next list value. Because this is a recursive function, we can think\r\nof the _accumulator_ value being the `initialValue` of that given step of\r\nthe reduce.\r\n\r\nWith our new _accumulator_ value (`updatedAcc`) in hand, we can continue to\r\nreduce with a recursive call on the `rest` of the list.\r\n\r\nAnd that's it. In just a couple lines of code we've implemented `reduce`\r\nwith JavaScript (ES6) primitives.\r\n\r\nLet's update `countWords` to use our version of `reduce`:\r\n\r\n```javascript\r\nconst countWords = wordList =\u003e {\r\n  return myReduce(wordList, {}, (acc, word) =\u003e {\r\n    acc[word] = (acc[word] || 0) + 1;\r\n    return acc;\r\n  });\r\n};\r\n```\r\n\r\n## Conclusion\r\n\r\nThe `reduce` method is a bit unapproachable. Yet it is a powerful and\r\nversatile method that can take the place of methods like `map`, `find`, and\r\n`filter`. We broke `reduce` down into its constituent parts and then\r\nimplemented our own from scratch. Go forth and reduce!\r\n\r\n---\r\n\r\nCover photo: \u003ca style=\"background-color:black;color:white;text-decoration:none;padding:4px 6px;font-family:-apple-system, BlinkMacSystemFont, \u0026quot;San Francisco\u0026quot;, \u0026quot;Helvetica Neue\u0026quot;, Helvetica, Ubuntu, Roboto, Noto, \u0026quot;Segoe UI\u0026quot;, Arial, sans-serif;font-size:12px;font-weight:bold;line-height:1.2;display:inline-block;border-radius:3px\" href=\"https://unsplash.com/@reubenteo?utm_medium=referral\u0026amp;utm_campaign=photographer-credit\u0026amp;utm_content=creditBadge\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Download free do whatever you want high-resolution photos from Reuben Teo\"\u003e\u003cspan style=\"display:inline-block;padding:2px 3px\"\u003e\u003csvg xmlns=\"http://www.w3.org/2000/svg\" style=\"height:12px;width:auto;position:relative;vertical-align:middle;top:-2px;fill:white\" viewBox=\"0 0 32 32\"\u003e\u003ctitle\u003eunsplash-logo\u003c/title\u003e\u003cpath d=\"M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/span\u003e\u003cspan style=\"display:inline-block;padding:2px 3px\"\u003eReuben Teo\u003c/span\u003e\u003c/a\u003e","content_text":"JavaScript's Array object has a built-in reduce method. This is a handy,\nyet underutilized method. Let's take a closer look at what reduce does and\nsee if we can implement it from scratch.\nReduce?\n\nHere is what\nMDN\nhas to say about reduce:\n\n\nThe reduce() method executes a reducer function (that you provide) on each\nmember of the array resulting in a single output value.\n\n\nSo, we can take an array of things and turn it into something else using a\nfunction that we get to define.\nconst sum = list =\u0026gt; list.reduce((total, val) =\u0026gt; total + val, 0);\n\nsum([1,2,3,4]) // =\u0026gt; 10\n\nThe reduce method can seem pretty limiting at first when we read that it\nproduces \"a single output value.\" That makes it sound like all it is good\nfor are things like a sum function. The reduce method can also produce\nan array as its single output value. Objects too!\nconst countWords = wordList =\u0026gt; {\n  return wordList.reduce((acc, word) =\u0026gt; {\n    acc[word] = (acc[word] || 0) + 1;\n    return acc;\n  }, {});\n};\n\ncountWords([\"hello\", \"world\", \"hello\", \"dogs\", \"hello\", \"cats\"]);\n// =\u0026gt; {hello: 3, world: 1, dogs: 1, cats: 1}\n\nThis method can do just about anything. It's very versatile. I'll say more\nabout that at the end.\nImplement Our Own\n\nLet's push our understanding of reduce a bit further by implementing our\nown.\n\nWhat we ought to have noticed in the examples above is that there are three\nstandard parts to a reduce. First, there is the list to be reduced. Then\nthere is always some initial value. For sum it was 0 and for\ncountWords it was {}. Third, and perhaps most importantly, is our\nreducer function.\n\nWe can stub out a naive version with just this knowledge:\nconst myReduce = (list, initialValue, reducer) =\u0026gt; {\n  return initialValue;\n}\n\nThis naive implementation will even work in cases where list is empty.\nmyReduce([], 0, (total, val) =\u0026gt; total + val); // =\u0026gt; 0\n\nThat there is an important observation. Once we have an empty list, we\nshould return whatever our initialValue is. This is what is known as our\nbase case or terminating case. The full implementation will involve\nrecursive calls of itself, so it is important to understand when and how we\nterminate the process.\nconst myReduce = (list, initialValue, reducer) =\u0026gt; {\n  if(list.length === 0) {\n    return initialValue;\n  }\n}\n\nWe are still missing the meat of the function. We need to decide what\nhappens when there are items left in the list. This is where our reducer\nfunction comes into the picture.\nThe Reducer\n\nThe general shape of the reducer function is important. It takes two\nvalues as arguments: the accumulator, what everything is being reduced\ninto, and the current value we are reducing. It then performs some reduction\nlogic and returns a new version of the accumulator.\n\nLet's look back at our reducer function for sum:\n(total, val) =\u0026gt; total + val\n\nThe accumulator is the total of our summing so far and the current value is\nval. We had those two values together, returning them as the new version\nof the accumulator.\n\nThe reducer function for countWords is a bit more complicated:\n(acc, word) =\u0026gt; {\n  acc[word] = (acc[word] || 0) + 1;\n  return acc;\n}\n\nThe accumulator is some object full of words and respective counts. We\nhave to first update the accumulator with the next word accounting for\nwhether or not it is the first time we've seen this word. Then the updated\naccumulator is returned.\nNow Reduce\n\nWith that in mind, let's handle the case where we get to use the given\nreducer function.\nconst myReduce = (list, initialValue, reducer) =\u0026gt; {\n  if(list.length === 0) {\n    return initialValue;\n  } else {\n    const [first, ...rest] = list;\n    const updatedAcc = reducer(initialValue, first);\n    return myReduce(rest, updatedAcc, reducer);\n  }\n}\n\nWe need to access the next value to be reduced, hence the list\ndestructuring. We then apply the reducer function with the initialValue\nand that next list value. Because this is a recursive function, we can think\nof the accumulator value being the initialValue of that given step of\nthe reduce.\n\nWith our new accumulator value (updatedAcc) in hand, we can continue to\nreduce with a recursive call on the rest of the list.\n\nAnd that's it. In just a couple lines of code we've implemented reduce\nwith JavaScript (ES6) primitives.\n\nLet's update countWords to use our version of reduce:\nconst countWords = wordList =\u0026gt; {\n  return myReduce(wordList, {}, (acc, word) =\u0026gt; {\n    acc[word] = (acc[word] || 0) + 1;\n    return acc;\n  });\n};\nConclusion\n\nThe reduce method is a bit unapproachable. Yet it is a powerful and\nversatile method that can take the place of methods like map, find, and\nfilter. We broke reduce down into its constituent parts and then\nimplemented our own from scratch. Go forth and reduce!\n\n\n\nCover photo: unsplash-logoReuben Teo\n","summary":"JavaScript's Array object has a built-in reduce method. This is a handy,\nyet underutilized method. Let's take a closer look at what reduce does and\nsee if we can implement it from scratch.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/464/write-reduce-from-scratch.jpg","date_published":"2019-02-26T09:00:00-05:00","data_modified":"2019-02-01T10:55:22-05:00","author":{"name":"Josh Branchaud","url":"https://hashrocket.com/team/josh-branchaud","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/88/josh-branchaud.jpg"},"tags":["ES6","Javascript"]},{"id":"https://hashrocket.com/blog/posts/use-yarn-like-a-pro","url":"https://hashrocket.com/blog/posts/use-yarn-like-a-pro","title":"Use Yarn Like a Pro","content_html":"[Yarn](https://yarnpkg) is a fast, reliable, and secure alternative to NPM. In this post, we'll look at techniques to use Yarn to its fullest.\n\n\u003e The expectations of life depend upon diligence; the mechanic that would\r\n\u003e perfect his work must first sharpen his tools. —Confucius\r\n\r\nYarn is a great tool. I'm won't wade into the Yarn vs. NPM debate,\r\nbecause the tradeoffs are always in flux. I just say that I've been\r\nusing it for two years on a variety of projects, and have nothing but praise.\r\n\r\nPart of that journey has been learning Yarn's nuances, and today I'm happy to be\r\nsharing them with you. Some of these epiphanies came to me through my own work,\r\nand some via our company knowledge base, [Today I\r\nLearned](https://til.hashrocket.com). \r\n\r\nI'm going to skip the setup and some common Yarn commands; head over to [Yarn's\r\nofficial site](https://yarnpkg.com/) to boot up. All of these commands were\r\nrun against Yarn 1.7.0.\r\n\r\n### Alias Yarn\r\n\r\nLet's start out with a quick win. Alias `yarn` to `y` and enjoy those saved keystrokes.\r\n\r\n```bash\r\n# .bashrc (or equivalent)\r\n\r\nalias y='yarn'\r\n```\r\n\r\nTo minimize confusion, be using the full `yarn` command throughout the rest of this post.\r\n\r\n### Initialize a Project\r\n\r\nI love initializers. Any heavy-handedness is outweighed by\r\nhaving the right files in place from the start. Like\r\nNPM, Yarn has a command for initializing a new JavaScript project.\r\n\r\n```sh\r\n$ yarn init\r\n```\r\n\r\nRun this from your project directory; it creates a `package.json` file with all the basic details.\r\n\r\n### Bootstrap an App\r\n\r\nYarn includes a `create` command for generating bootstrapped apps, following the `create-\u003cname\u003e-app` convention. Here's how you'd use it with [create-react-app](https://github.com/facebookincubator/create-react-app):\r\n\r\n```sh\r\n$ yarn create react-app my-app\r\n```\r\n\r\n### Type Less with the Default Command\r\n\r\nLike Bundler for Ruby, `yarn` with no other qualifier runs `yarn install`,\r\npassing through any provided flags. If updating JavaScript packages is your\r\nintention, run `yarn` and enjoy those saved keystrokes.\r\n\r\n### Update Packages Automatically\r\n\r\nThis isn't a Yarn-specific tip, but for me it's essential when using this tool.\r\nWhen I pull down changes to a JavaScript project, those changes can add,\r\nupdate, or remove dependencies. Unlike a language like Elixir, in JavaScript it's possible to start your server, run your tests,\r\nor do a number of activities without syncing your packages. When your\r\nlocal code chokes, which it will, the results can range from annoying to\r\ndisastrous.\r\n\r\nHere's my solution, which I borrowed from the internet a while\r\nago. This is the executable file `.git/hooks/post-merge` and it lives in the\r\nroot directory of all my collaborative JavaScript projects:\r\n\r\n```bash\r\n#/usr/bin/env bash\r\n\r\nchanged_files=\"$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)\"\r\n\r\ncheck_run() {\r\n  echo \"$changed_files\" | grep --quiet \"$1\" \u0026\u0026 eval \"$2\"\r\n}\r\n\r\ncheck_run yarn.lock \"yarn\"\r\n```\r\n\r\nWhen my Yarn lockfile changes in version control, this script runs `yarn` (AKA\r\n`yarn install`, as explained above), keeping me current. This has saved me many times, and it's just less mental overhead.\r\n\r\n### Autoclean\r\n\r\nTo quote Dorian Karter: \"The node_modules directory is often a resource-consuming hog, both in space and number of files. It is full of junk such as\r\ntest files, build scripts, and example directories.\" We want a repo full of quality code, not cruft.\r\n\r\n`yarn autoclean` to the rescue. Autoclean cleans and removes unnecessary files\r\nfrom package dependencies. To enable it, you first must generate the\r\n`.yarnclean` config file:\r\n\r\n```sh\r\n$ yarn autoclean --init\r\n```\r\n\r\n`.yarnclean` lists about forty files you should remove from a typical\r\nJavaScript project. Once enabled, autoclean will remove them when you install or add\r\ndependencies, or force it to run. Here's what happened when I set it up on a long-running React\r\nproject:\r\n\r\n```sh\r\n$ yarn autoclean --force\r\nyarn autoclean v1.13.0\r\n[1/1] Cleaning modules...\r\ninfo Removed 8588 files\r\ninfo Saved 53.06 MB.\r\n```\r\n\r\nEnjoy your pruned program.\r\n\r\n### Add an Alias\r\n\r\nNaming collision ahead? Alias a dependency with `yarn add`:\r\n\r\n```sh\r\n$ yarn add react-select@v1.2.0\r\n$ yarn add react-select-next@npm:react-select@next\r\n```\r\n\r\nNow you can import your aliased dependency like so:\r\n\r\n```javascript\r\nimport Select from 'react-select-next'\r\n```\r\n\r\n### Test Your Build\r\n\r\n[create-react-app](https://github.com/facebook/create-react-app) is an example\r\nof a project that requires all dependencies precompiled to ES5. If they aren't,\r\nthe build will fail.\r\n\r\nA bad time to discover this would be when your tooling is trying to build\r\nthe app on a staging or production server. Surface this issue in development like so:\r\n\r\n```sh\r\n$ yarn \u003cyour-build-step\u003e\r\n```\r\nJust run your build command anytime you add a new dependency. This could be even be added as a post-merge step.\r\n\r\n### Conclusion\r\n\r\nWe've barely scratched the surface of what Yarn can do. If you write a lot of JavaScript, I recommend exploring this tool!\r\n\r\nThank you to Andrew Vogel, Brian Dunn, Chris Erin, Dorian Karter, Gabe Reis, and Josh Branchaud for writing about Yarn on [Today I\r\nLearned](https://til.hashrocket.com/). We've all been learning together as this\r\nlibrary matures.\r\n\r\nWhat are your favorite Yarn hacks? Let us know on\r\n[Twitter](https://twitter.com/hashrocket).\r\n\r\n---\r\n\r\nPhoto by Steve Johnson on Unsplash","content_text":"Yarn is a fast, reliable, and secure alternative to NPM. In this post, we'll look at techniques to use Yarn to its fullest.\n\n\nThe expectations of life depend upon diligence; the mechanic that would\nperfect his work must first sharpen his tools. —Confucius\n\n\nYarn is a great tool. I'm won't wade into the Yarn vs. NPM debate,\nbecause the tradeoffs are always in flux. I just say that I've been\nusing it for two years on a variety of projects, and have nothing but praise.\n\nPart of that journey has been learning Yarn's nuances, and today I'm happy to be\nsharing them with you. Some of these epiphanies came to me through my own work,\nand some via our company knowledge base, Today I\nLearned. \n\nI'm going to skip the setup and some common Yarn commands; head over to Yarn's\nofficial site to boot up. All of these commands were\nrun against Yarn 1.7.0.\nAlias Yarn\n\nLet's start out with a quick win. Alias yarn to y and enjoy those saved keystrokes.\n# .bashrc (or equivalent)\n\nalias y='yarn'\n\nTo minimize confusion, be using the full yarn command throughout the rest of this post.\nInitialize a Project\n\nI love initializers. Any heavy-handedness is outweighed by\nhaving the right files in place from the start. Like\nNPM, Yarn has a command for initializing a new JavaScript project.\n$ yarn init\n\nRun this from your project directory; it creates a package.json file with all the basic details.\nBootstrap an App\n\nYarn includes a create command for generating bootstrapped apps, following the create-\u0026lt;name\u0026gt;-app convention. Here's how you'd use it with create-react-app:\n$ yarn create react-app my-app\nType Less with the Default Command\n\nLike Bundler for Ruby, yarn with no other qualifier runs yarn install,\npassing through any provided flags. If updating JavaScript packages is your\nintention, run yarn and enjoy those saved keystrokes.\nUpdate Packages Automatically\n\nThis isn't a Yarn-specific tip, but for me it's essential when using this tool.\nWhen I pull down changes to a JavaScript project, those changes can add,\nupdate, or remove dependencies. Unlike a language like Elixir, in JavaScript it's possible to start your server, run your tests,\nor do a number of activities without syncing your packages. When your\nlocal code chokes, which it will, the results can range from annoying to\ndisastrous.\n\nHere's my solution, which I borrowed from the internet a while\nago. This is the executable file .git/hooks/post-merge and it lives in the\nroot directory of all my collaborative JavaScript projects:\n#/usr/bin/env bash\n\nchanged_files=\"$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)\"\n\ncheck_run() {\n  echo \"$changed_files\" | grep --quiet \"$1\" \u0026amp;\u0026amp; eval \"$2\"\n}\n\ncheck_run yarn.lock \"yarn\"\n\nWhen my Yarn lockfile changes in version control, this script runs yarn (AKA\nyarn install, as explained above), keeping me current. This has saved me many times, and it's just less mental overhead.\nAutoclean\n\nTo quote Dorian Karter: \"The node_modules directory is often a resource-consuming hog, both in space and number of files. It is full of junk such as\ntest files, build scripts, and example directories.\" We want a repo full of quality code, not cruft.\n\nyarn autoclean to the rescue. Autoclean cleans and removes unnecessary files\nfrom package dependencies. To enable it, you first must generate the\n.yarnclean config file:\n$ yarn autoclean --init\n\n.yarnclean lists about forty files you should remove from a typical\nJavaScript project. Once enabled, autoclean will remove them when you install or add\ndependencies, or force it to run. Here's what happened when I set it up on a long-running React\nproject:\n$ yarn autoclean --force\nyarn autoclean v1.13.0\n[1/1] Cleaning modules...\ninfo Removed 8588 files\ninfo Saved 53.06 MB.\n\nEnjoy your pruned program.\nAdd an Alias\n\nNaming collision ahead? Alias a dependency with yarn add:\n$ yarn add react-select@v1.2.0\n$ yarn add react-select-next@npm:react-select@next\n\nNow you can import your aliased dependency like so:\nimport Select from 'react-select-next'\nTest Your Build\n\ncreate-react-app is an example\nof a project that requires all dependencies precompiled to ES5. If they aren't,\nthe build will fail.\n\nA bad time to discover this would be when your tooling is trying to build\nthe app on a staging or production server. Surface this issue in development like so:\n$ yarn \u0026lt;your-build-step\u0026gt;\n\nJust run your build command anytime you add a new dependency. This could be even be added as a post-merge step.\nConclusion\n\nWe've barely scratched the surface of what Yarn can do. If you write a lot of JavaScript, I recommend exploring this tool!\n\nThank you to Andrew Vogel, Brian Dunn, Chris Erin, Dorian Karter, Gabe Reis, and Josh Branchaud for writing about Yarn on Today I\nLearned. We've all been learning together as this\nlibrary matures.\n\nWhat are your favorite Yarn hacks? Let us know on\nTwitter.\n\n\n\nPhoto by Steve Johnson on Unsplash\n","summary":"Yarn is a fast, reliable, and secure alternative to NPM. In this post, we'll look at techniques to use Yarn to its fullest.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/463/steve-johnson-551225-unsplash.jpg","date_published":"2019-02-19T09:00:00-05:00","data_modified":"2019-02-11T14:06:57-05:00","author":{"name":"Jake Worth","url":"https://hashrocket.com/team/jake-worth","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/86/jake-worth-headshot-2020-cropped-for-hr-3.jpeg"},"tags":["Javascript"]},{"id":"https://hashrocket.com/blog/posts/5-javascript-object-destructuring-tricks","url":"https://hashrocket.com/blog/posts/5-javascript-object-destructuring-tricks","title":"5 JavaScript Object Destructuring Tricks","content_html":"The ES6 destructuring syntax is quite flexible. It allows us to do a lot of\r\nnice things with objects and arrays that in the past would have taken many\r\ndistracting lines of code.\r\n\r\nLet's look at five handy destructuring tricks for objects.\n\n## Trick #1\r\n\r\n\u003e Remove an item from an object\r\n\r\n```javascript\r\n// const data = { a: 1, b: 2, thingToRemove: \"bye\" };\r\n\r\nconst { thingToRemove, ...rest } = data;\r\n\r\nconsole.log(rest); // { a: 1, b: 2 }\r\n```\r\n\r\nThis is a concise way of getting a new copy of an object with a specific\r\nkey/value pair removed. Note that it is immutable and you are free to\r\nspecify as many keys as you'd like separated from the `...rest` object.\r\n\r\n## Trick #2\r\n\r\n\u003e Rename things in a destructuring\r\n\r\n```javascript\r\n// const data = { coordinates: { x: 5, y: 6 }};\r\n\r\nconst { x: xCoord, y: yCoord } = data.coordinates;\r\n\r\nconsole.log(xCoord, yCoord); // 5, 6\r\n```\r\n\r\nIt is not always desirable or possible to adjust naming within an object\r\nupstream. If we need different naming locally, we can do this inline with\r\nour destructuring. The colon syntax allows us to do this with specific keys.\r\n\r\n## Trick #3\r\n\r\n\u003e Nested Destructuring\r\n\r\n```javascript\r\n// const data = { coordinates: { x: 5, y: 6 }};\r\n\r\nconst { coordinates: { x, y }} = data;\r\n\r\nconsole.log(x, y); // 5, 6\r\n```\r\n\r\nWe can get at key/value pairs in a single destructuring regardless of how\r\nthey are nested. This again allows us to concisely access the data we want\r\nwithout having to make any upstream adjustments.\r\n\r\nThis can of course be combined with Trick #2:\r\n\r\n```javascript\r\n// const data = { coordinates: { x: 5, y: 6 } };\r\n\r\nconst { coordinates: { x: xCoord, y: yCoord } } = data;\r\n\r\nconsole.log(xCoord, yCoord); // 5, 6\r\n```\r\n\r\nFancy!\r\n\r\n## Trick #4\r\n\r\n\u003e Destructuring In Function Arguments\r\n\r\nAll the things we did above -- we can do those in the argument part of a\r\nfunction declaration.\r\n\r\n```javascript\r\nconst myFunction = ({ coordinates: { x: xCoord, y: yCoord } }) =\u003e {\r\n  console.log('Coords:', xCoord, yCoord);\r\n};\r\n```\r\n\r\nWe've accessed and renamed nested values all in the function declaration.\r\nThe body of the function can be focused strictly on the logic.\r\n\r\n## Trick #5\r\n\r\n\u003e Non-default Imports As Named Object\r\n\r\nConsider a growing list of imports that starts to look like this:\r\n\r\n```javascript\r\nimport {\r\n  rootPath,\r\n  blogPath,\r\n  aboutUsPath,\r\n  teamPath,\r\n  pricingPath,\r\n  contactPath,\r\n  signInPath,\r\n  signOutPath,\r\n} from '../routes';\r\n```\r\n\r\nIt's already unwieldy and its bound to get worse. This import destructuring\r\nsyntax allows us to tame those imports.\r\n\r\n```javascript\r\nimport { * as routes } from '../routes';\r\n\r\nconsole.log(routes.rootPath); // '/'\r\nconsole.log(routes.blogPath); // '/blog'\r\n```\r\n\r\nThis can be a nice way to not have to explicitly import dozens of things The\r\ntradeoff is that your compiler will no longer be able inform you if you are\r\nreferencing an undefined import.\r\n\r\n## Conclusion\r\n\r\nThese five object destructuring tricks allow us to write more concise,\r\ndirect code. I can attest to the usefulness of these tricks -- quickly\r\nbrowsing through a few files of a React app I've been maintaining for over a\r\nyear reveals each of these employed in a variety of contexts. Give them a\r\ntry, I think you'll find that it makes JavaScript even more fun to write.\r\n\r\n---\r\n\r\nCover Photo Credit: \u003ca style=\"background-color:black;color:white;text-decoration:none;padding:4px 6px;font-family:-apple-system, BlinkMacSystemFont, \u0026quot;San Francisco\u0026quot;, \u0026quot;Helvetica Neue\u0026quot;, Helvetica, Ubuntu, Roboto, Noto, \u0026quot;Segoe UI\u0026quot;, Arial, sans-serif;font-size:12px;font-weight:bold;line-height:1.2;display:inline-block;border-radius:3px\" href=\"https://unsplash.com/@franckinjapan?utm_medium=referral\u0026amp;utm_campaign=photographer-credit\u0026amp;utm_content=creditBadge\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Download free do whatever you want high-resolution photos from Franck V.\"\u003e\u003cspan style=\"display:inline-block;padding:2px 3px\"\u003e\u003csvg xmlns=\"http://www.w3.org/2000/svg\" style=\"height:12px;width:auto;position:relative;vertical-align:middle;top:-2px;fill:white\" viewBox=\"0 0 32 32\"\u003e\u003ctitle\u003eunsplash-logo\u003c/title\u003e\u003cpath d=\"M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/span\u003e\u003cspan style=\"display:inline-block;padding:2px 3px\"\u003eFranck V.\u003c/span\u003e\u003c/a\u003e","content_text":"The ES6 destructuring syntax is quite flexible. It allows us to do a lot of\nnice things with objects and arrays that in the past would have taken many\ndistracting lines of code.\n\nLet's look at five handy destructuring tricks for objects.\nTrick #1\n\n\nRemove an item from an object\n\n// const data = { a: 1, b: 2, thingToRemove: \"bye\" };\n\nconst { thingToRemove, ...rest } = data;\n\nconsole.log(rest); // { a: 1, b: 2 }\n\nThis is a concise way of getting a new copy of an object with a specific\nkey/value pair removed. Note that it is immutable and you are free to\nspecify as many keys as you'd like separated from the ...rest object.\nTrick #2\n\n\nRename things in a destructuring\n\n// const data = { coordinates: { x: 5, y: 6 }};\n\nconst { x: xCoord, y: yCoord } = data.coordinates;\n\nconsole.log(xCoord, yCoord); // 5, 6\n\nIt is not always desirable or possible to adjust naming within an object\nupstream. If we need different naming locally, we can do this inline with\nour destructuring. The colon syntax allows us to do this with specific keys.\nTrick #3\n\n\nNested Destructuring\n\n// const data = { coordinates: { x: 5, y: 6 }};\n\nconst { coordinates: { x, y }} = data;\n\nconsole.log(x, y); // 5, 6\n\nWe can get at key/value pairs in a single destructuring regardless of how\nthey are nested. This again allows us to concisely access the data we want\nwithout having to make any upstream adjustments.\n\nThis can of course be combined with Trick #2:\n// const data = { coordinates: { x: 5, y: 6 } };\n\nconst { coordinates: { x: xCoord, y: yCoord } } = data;\n\nconsole.log(xCoord, yCoord); // 5, 6\n\nFancy!\nTrick #4\n\n\nDestructuring In Function Arguments\n\n\nAll the things we did above -- we can do those in the argument part of a\nfunction declaration.\nconst myFunction = ({ coordinates: { x: xCoord, y: yCoord } }) =\u0026gt; {\n  console.log('Coords:', xCoord, yCoord);\n};\n\nWe've accessed and renamed nested values all in the function declaration.\nThe body of the function can be focused strictly on the logic.\nTrick #5\n\n\nNon-default Imports As Named Object\n\n\nConsider a growing list of imports that starts to look like this:\nimport {\n  rootPath,\n  blogPath,\n  aboutUsPath,\n  teamPath,\n  pricingPath,\n  contactPath,\n  signInPath,\n  signOutPath,\n} from '../routes';\n\nIt's already unwieldy and its bound to get worse. This import destructuring\nsyntax allows us to tame those imports.\nimport { * as routes } from '../routes';\n\nconsole.log(routes.rootPath); // '/'\nconsole.log(routes.blogPath); // '/blog'\n\nThis can be a nice way to not have to explicitly import dozens of things The\ntradeoff is that your compiler will no longer be able inform you if you are\nreferencing an undefined import.\nConclusion\n\nThese five object destructuring tricks allow us to write more concise,\ndirect code. I can attest to the usefulness of these tricks -- quickly\nbrowsing through a few files of a React app I've been maintaining for over a\nyear reveals each of these employed in a variety of contexts. Give them a\ntry, I think you'll find that it makes JavaScript even more fun to write.\n\n\n\nCover Photo Credit: unsplash-logoFranck V.\n","summary":"The ES6 destructuring syntax is quite flexible. It allows us to do a lot of\nnice things with objects and arrays that in the past would have taken many\ndistracting lines of code.\n\nLet's look at five handy destructuring tricks for objects.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/451/5-object-destructuring-tricks.jpg","date_published":"2019-01-22T09:00:00-05:00","data_modified":"2019-01-11T15:20:40-05:00","author":{"name":"Josh Branchaud","url":"https://hashrocket.com/team/josh-branchaud","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/88/josh-branchaud.jpg"},"tags":["Javascript","ES6"]},{"id":"https://hashrocket.com/blog/posts/managing-react-router-pathnames","url":"https://hashrocket.com/blog/posts/managing-react-router-pathnames","title":"Managing React Router Pathnames","content_html":"So, you've introduced\r\n[`react-router`](https://github.com/ReactTraining/react-router) into your\r\nsingle page React app as a way of managing routing. The app has started to\r\ngrow. There are many top-level routes for rendering different \"pages\". Some\r\nof those \"pages\" now have sub-routing within them for managing wizard\r\nworkflows and sections with multiple \"sub-pages\". This is great. The dream\r\nof routing within your React SPA has been realized.\r\n\r\nFast-forward a few months, and your elegant routing solution has become a brittle, error-prone maintenance nightmare. What happened?\n\n## A Simplified Example\r\n\r\nEarly into the development of a large React SPA, I noticed the above\r\nscenario beginning to emerge. Some of what I noticed can be summarized in\r\nthe following series of code snippets. _Note: these code snippets are not\r\nfull, working examples. Only the instructive parts have been included._\r\n\r\n```jsx\r\n/* App.js */\r\nreturn (\r\n  \u003cSwitch\u003e\r\n    \u003cRoute path=\"/\" component={Home} /\u003e\r\n    \u003cRoute path=\"/blog\" component={Blog} /\u003e\r\n    \u003cRoute path=\"/team\" component={Team} /\u003e\r\n    \u003cRoute path=\"/about-us\" component={AboutUs} /\u003e\r\n  \u003c/Switch\u003e\r\n);\r\n```\r\n\r\nThis is the top-level routing in our main app file.\r\n\r\n```jsx\r\n/* NavLinks.js */\r\n\u003cnav\u003e\r\n  \u003cLink to=\"/\"\u003eHome\u003c/Link\u003e\r\n  \u003cLink to=\"/blog\"\u003eBlog\u003c/Link\u003e\r\n  \u003cLink to=\"/team\"\u003eTeam\u003c/Link\u003e\r\n  \u003cLink to=\"/about-us\"\u003eAbout Us\u003c/Link\u003e\r\n\u003c/nav\u003e\r\n```\r\n\r\nThese are some navigational links that get used in a shared header.\r\n\r\n```jsx\r\n/* Footer.js */\r\n\u003cdiv className=\"footer\"\u003e\r\n  \u003cLink to=\"/about-us\"\u003eAbout Us\u003c/Link\u003e\r\n  \u003cLink to=\"/team\"\u003eTeam\u003c/Link\u003e\r\n  \u003cLink to=\"/blog\"\u003eBlog\u003c/Link\u003e\r\n\u003c/div\u003e\r\n```\r\n\r\nHere is a shared `Footer` component that provides links in a different layout\r\nthan the `NavLinks` component.\r\n\r\n```jsx\r\n/* Blog.js */\r\n\u003cdiv className=\"blog\"\u003e\r\n  \u003ch1\u003eOur Blog\u003c/h1\u003e\r\n  \u003cSwitch\u003e\r\n    \u003cRoute path=\"/blog\" component={BlogIndex} /\u003e\r\n    \u003cRoute path=\"/blog/:id\" component={BlogShow} /\u003e\r\n  \u003c/Switch\u003e\r\n\u003c/div\u003e\r\n```\r\n\r\nThis is the blog part of the site that gets routed to when one of the _Blog_\r\nlinks is clicked. It has a nested `Switch` which handles both the index path\r\n(`/blog`) and a path to a specific blog post (`/blog/123`).\r\n\r\n```jsx\r\n/* BlogShow */\r\n\u003cdiv className=\"blog-show\"\u003e\r\n  \u003cLink to={`/blog/${props.blogId}`}\u003e\u003ch2\u003e{props.title}\u003c/h2\u003e\u003c/Link\u003e\r\n\r\n  {/* blog content ... */}\r\n\r\n  \u003cLink to={`/blog/${props.prevBlogId}`}\u003ePrevious Post\u003c/Link\u003e\r\n  \u003cLink to={`/blog/${props.nextBlogId}`}\u003eNext Post\u003c/Link\u003e\r\n\u003c/div\u003e\r\n```\r\n\r\nThis is the component that shows the content of a specific blog post. It has\r\nlinks throughout for itself as well as the previous and next blog posts.\r\n\r\n## The Problem\r\n\r\nThere are two separate, but related issues emerging in the above code\r\nsnippets.\r\n\r\nThe first is the duplication of strings like `'/blog'` over and over across\r\nmultiple files in the codebase. Even in this simplified example you can\r\nimagine how this could quickly get out of hand. When a string has to be\r\ntyped the same way over and over, there is bound to be a time when it is\r\nmistyped. Babel cannot help with mistyped string constants.\r\n\r\nWithout a standardized way of managing pathnames, it is likely that a\r\nvariety of divergent approaches to defining and referencing pathnames will\r\nresult. This will become hard to maintain over time.\r\n\r\nThe second issue is that of terse, error-prone string concatenation.\r\nEverywhere you are building a parameterized pathname requires that you get\r\nall the details right. This is why many frameworks, like Rails, have URL\r\nbuilding APIs.\r\n\r\n## The Solution\r\n\r\nThe fix to all of this is to create a single source of truth for all of your\r\npathname needs.\r\n\r\nFirst, create a routes file -- `routes.js`. Then move all of the string\r\npathnames into that file, each one is its own exported constant.\r\n\r\n```javascript\r\nexport const blogPath = \"/blog\";\r\n```\r\n\r\nAdditionally, find everywhere that parameterized pathnames are being manually\r\nconcatenated together. Each of these can instead be a standard pathname\r\nbuilding function in the routes file.\r\n\r\n```javascript\r\nexport const buildBlogShowPath = blogId =\u003e `/blog/${blogId}`;\r\n```\r\n\r\nThese constants and parameterized path building functions can be imported\r\nwherever they are needed. If changes of any kind need to be applied, you are\r\nin a much more secure position to be making them. Mistyped string constants\r\nnow become mistyped variable names which will be flagged by Babel.\r\n\r\nHere is what some of the above simplified example could instead look like.\r\n\r\n```javascript\r\n/* routes.js */\r\nexport const rootPath = \"/\";\r\nexport const teamPath = \"/team\";\r\nexport const aboutPath = \"/about-us\";\r\n\r\nexport const blogPath = \"/blog\";\r\nexport const blogShowPath = \"/blog/:id\";\r\n\r\nexport const buildBlogShowPath = blogId =\u003e `/blog/${blogId}`;\r\n```\r\n\r\n```jsx\r\n/* App.js */\r\nimport { rootPath, blogPath, teamPath, aboutPath } from \"./routes\";\r\n\r\n\u003cSwitch\u003e\r\n  \u003cRoute path={rootPath} component={Home} /\u003e\r\n  \u003cRoute path={blogPath} component={Blog} /\u003e\r\n  \u003cRoute path={teamPath} component={Team} /\u003e\r\n  \u003cRoute path={aboutPath} component={AboutUs} /\u003e\r\n\u003c/Switch\u003e\r\n```\r\n\r\n```jsx\r\n/* NavLinks.js */\r\nimport { rootPath, blogPath, teamPath, aboutPath } from \"./routes\";\r\n\r\n\u003cnav\u003e\r\n  \u003cLink to={rootPath}\u003eHome\u003c/Link\u003e\r\n  \u003cLink to={blogPath}\u003eBlog\u003c/Link\u003e\r\n  \u003cLink to={teamPath}\u003eTeam\u003c/Link\u003e\r\n  \u003cLink to={aboutPath}\u003eAbout Us\u003c/Link\u003e\r\n\u003c/nav\u003e\r\n```\r\n\r\n```jsx\r\n/* Blog.js */\r\nimport { blogPath, blogShowPath } from \"./routes\";\r\n\r\n\u003cdiv className=\"blog\"\u003e\r\n  \u003ch1\u003eOur Blog\u003c/h1\u003e\r\n  \u003cSwitch\u003e\r\n    \u003cRoute path={blogPath} component={BlogIndex} /\u003e\r\n    \u003cRoute path={blogShowPath} component={BlogShow} /\u003e\r\n  \u003c/Switch\u003e\r\n\u003c/div\u003e\r\n```\r\n\r\n```jsx\r\n/* BlogShow */\r\nimport { buildBlogShowPath } from \"./routes\";\r\n\r\n\u003cdiv className=\"blog-show\"\u003e\r\n  \u003cLink to={buildBlogShowPath(props.blogId)}\u003e\u003ch2\u003e{props.title}\u003c/h2\u003e\u003c/Link\u003e\r\n\r\n  {/* blog content ... */}\r\n\r\n  \u003cLink to={buildBlogShowPath(props.prevBlogId)}\u003ePrevious Post\u003c/Link\u003e\r\n  \u003cLink to={buildBlogShowPath(props.nextBlogId)}\u003eNext Post\u003c/Link\u003e\r\n\u003c/div\u003e\r\n```\r\n\r\n## Conclusion\r\n\r\nThere are two general principles at play in this solution. The first is the\r\nidea of reducing the number of magic strings in a codebase. The second is\r\nnormalizing data so that there is a single source of truth. I've found this\r\nthinking to be well-suited to the large `react-router`-powered SPA on which\r\nI've been working. These, however, are broader ideas. I generally recommend\r\nlooking out for opportunities to apply them. They make code more robust and\r\nresilient to change.","content_text":"So, you've introduced\nreact-router into your\nsingle page React app as a way of managing routing. The app has started to\ngrow. There are many top-level routes for rendering different \"pages\". Some\nof those \"pages\" now have sub-routing within them for managing wizard\nworkflows and sections with multiple \"sub-pages\". This is great. The dream\nof routing within your React SPA has been realized.\n\nFast-forward a few months, and your elegant routing solution has become a brittle, error-prone maintenance nightmare. What happened?\nA Simplified Example\n\nEarly into the development of a large React SPA, I noticed the above\nscenario beginning to emerge. Some of what I noticed can be summarized in\nthe following series of code snippets. Note: these code snippets are not\nfull, working examples. Only the instructive parts have been included.\n/* App.js */\nreturn (\n  \u0026lt;Switch\u0026gt;\n    \u0026lt;Route path=\"/\" component={Home} /\u0026gt;\n    \u0026lt;Route path=\"/blog\" component={Blog} /\u0026gt;\n    \u0026lt;Route path=\"/team\" component={Team} /\u0026gt;\n    \u0026lt;Route path=\"/about-us\" component={AboutUs} /\u0026gt;\n  \u0026lt;/Switch\u0026gt;\n);\n\nThis is the top-level routing in our main app file.\n/* NavLinks.js */\n\u0026lt;nav\u0026gt;\n  \u0026lt;Link to=\"/\"\u0026gt;Home\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to=\"/blog\"\u0026gt;Blog\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to=\"/team\"\u0026gt;Team\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to=\"/about-us\"\u0026gt;About Us\u0026lt;/Link\u0026gt;\n\u0026lt;/nav\u0026gt;\n\nThese are some navigational links that get used in a shared header.\n/* Footer.js */\n\u0026lt;div className=\"footer\"\u0026gt;\n  \u0026lt;Link to=\"/about-us\"\u0026gt;About Us\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to=\"/team\"\u0026gt;Team\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to=\"/blog\"\u0026gt;Blog\u0026lt;/Link\u0026gt;\n\u0026lt;/div\u0026gt;\n\nHere is a shared Footer component that provides links in a different layout\nthan the NavLinks component.\n/* Blog.js */\n\u0026lt;div className=\"blog\"\u0026gt;\n  \u0026lt;h1\u0026gt;Our Blog\u0026lt;/h1\u0026gt;\n  \u0026lt;Switch\u0026gt;\n    \u0026lt;Route path=\"/blog\" component={BlogIndex} /\u0026gt;\n    \u0026lt;Route path=\"/blog/:id\" component={BlogShow} /\u0026gt;\n  \u0026lt;/Switch\u0026gt;\n\u0026lt;/div\u0026gt;\n\nThis is the blog part of the site that gets routed to when one of the Blog\nlinks is clicked. It has a nested Switch which handles both the index path\n(/blog) and a path to a specific blog post (/blog/123).\n/* BlogShow */\n\u0026lt;div className=\"blog-show\"\u0026gt;\n  \u0026lt;Link to={`/blog/${props.blogId}`}\u0026gt;\u0026lt;h2\u0026gt;{props.title}\u0026lt;/h2\u0026gt;\u0026lt;/Link\u0026gt;\n\n  {/* blog content ... */}\n\n  \u0026lt;Link to={`/blog/${props.prevBlogId}`}\u0026gt;Previous Post\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to={`/blog/${props.nextBlogId}`}\u0026gt;Next Post\u0026lt;/Link\u0026gt;\n\u0026lt;/div\u0026gt;\n\nThis is the component that shows the content of a specific blog post. It has\nlinks throughout for itself as well as the previous and next blog posts.\nThe Problem\n\nThere are two separate, but related issues emerging in the above code\nsnippets.\n\nThe first is the duplication of strings like '/blog' over and over across\nmultiple files in the codebase. Even in this simplified example you can\nimagine how this could quickly get out of hand. When a string has to be\ntyped the same way over and over, there is bound to be a time when it is\nmistyped. Babel cannot help with mistyped string constants.\n\nWithout a standardized way of managing pathnames, it is likely that a\nvariety of divergent approaches to defining and referencing pathnames will\nresult. This will become hard to maintain over time.\n\nThe second issue is that of terse, error-prone string concatenation.\nEverywhere you are building a parameterized pathname requires that you get\nall the details right. This is why many frameworks, like Rails, have URL\nbuilding APIs.\nThe Solution\n\nThe fix to all of this is to create a single source of truth for all of your\npathname needs.\n\nFirst, create a routes file -- routes.js. Then move all of the string\npathnames into that file, each one is its own exported constant.\nexport const blogPath = \"/blog\";\n\nAdditionally, find everywhere that parameterized pathnames are being manually\nconcatenated together. Each of these can instead be a standard pathname\nbuilding function in the routes file.\nexport const buildBlogShowPath = blogId =\u0026gt; `/blog/${blogId}`;\n\nThese constants and parameterized path building functions can be imported\nwherever they are needed. If changes of any kind need to be applied, you are\nin a much more secure position to be making them. Mistyped string constants\nnow become mistyped variable names which will be flagged by Babel.\n\nHere is what some of the above simplified example could instead look like.\n/* routes.js */\nexport const rootPath = \"/\";\nexport const teamPath = \"/team\";\nexport const aboutPath = \"/about-us\";\n\nexport const blogPath = \"/blog\";\nexport const blogShowPath = \"/blog/:id\";\n\nexport const buildBlogShowPath = blogId =\u0026gt; `/blog/${blogId}`;\n/* App.js */\nimport { rootPath, blogPath, teamPath, aboutPath } from \"./routes\";\n\n\u0026lt;Switch\u0026gt;\n  \u0026lt;Route path={rootPath} component={Home} /\u0026gt;\n  \u0026lt;Route path={blogPath} component={Blog} /\u0026gt;\n  \u0026lt;Route path={teamPath} component={Team} /\u0026gt;\n  \u0026lt;Route path={aboutPath} component={AboutUs} /\u0026gt;\n\u0026lt;/Switch\u0026gt;\n/* NavLinks.js */\nimport { rootPath, blogPath, teamPath, aboutPath } from \"./routes\";\n\n\u0026lt;nav\u0026gt;\n  \u0026lt;Link to={rootPath}\u0026gt;Home\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to={blogPath}\u0026gt;Blog\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to={teamPath}\u0026gt;Team\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to={aboutPath}\u0026gt;About Us\u0026lt;/Link\u0026gt;\n\u0026lt;/nav\u0026gt;\n/* Blog.js */\nimport { blogPath, blogShowPath } from \"./routes\";\n\n\u0026lt;div className=\"blog\"\u0026gt;\n  \u0026lt;h1\u0026gt;Our Blog\u0026lt;/h1\u0026gt;\n  \u0026lt;Switch\u0026gt;\n    \u0026lt;Route path={blogPath} component={BlogIndex} /\u0026gt;\n    \u0026lt;Route path={blogShowPath} component={BlogShow} /\u0026gt;\n  \u0026lt;/Switch\u0026gt;\n\u0026lt;/div\u0026gt;\n/* BlogShow */\nimport { buildBlogShowPath } from \"./routes\";\n\n\u0026lt;div className=\"blog-show\"\u0026gt;\n  \u0026lt;Link to={buildBlogShowPath(props.blogId)}\u0026gt;\u0026lt;h2\u0026gt;{props.title}\u0026lt;/h2\u0026gt;\u0026lt;/Link\u0026gt;\n\n  {/* blog content ... */}\n\n  \u0026lt;Link to={buildBlogShowPath(props.prevBlogId)}\u0026gt;Previous Post\u0026lt;/Link\u0026gt;\n  \u0026lt;Link to={buildBlogShowPath(props.nextBlogId)}\u0026gt;Next Post\u0026lt;/Link\u0026gt;\n\u0026lt;/div\u0026gt;\nConclusion\n\nThere are two general principles at play in this solution. The first is the\nidea of reducing the number of magic strings in a codebase. The second is\nnormalizing data so that there is a single source of truth. I've found this\nthinking to be well-suited to the large react-router-powered SPA on which\nI've been working. These, however, are broader ideas. I generally recommend\nlooking out for opportunities to apply them. They make code more robust and\nresilient to change.\n","summary":"So, you've introduced\nreact-router into your\nsingle page React app as a way of managing routing. The app has started to\ngrow. There are many top-level routes for rendering different \"pages\". Some\nof those \"pages\" now have sub-routing within them for managing wizard\nworkflows and sections with multiple \"sub-pages\". This is great. The dream\nof routing within your React SPA has been realized.\n\nFast-forward a few months, and your elegant routing solution has become a brittle, error-prone maintenance nightmare. What happened?\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/448/managing-react-router-pathnames.jpg","date_published":"2019-01-10T09:00:00-05:00","data_modified":"2025-10-10T15:15:15-04:00","author":{"name":"Josh Branchaud","url":"https://hashrocket.com/team/josh-branchaud","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/88/josh-branchaud.jpg"},"tags":["React","Javascript"]},{"id":"https://hashrocket.com/blog/posts/where-am-i-url-assertions-with-cypress","url":"https://hashrocket.com/blog/posts/where-am-i-url-assertions-with-cypress","title":"Where Am I: URL Assertions with Cypress","content_html":"The URL that appears in the browser's URL bar is one of the first and\r\nprimary ways that users interact with our web apps. The URL tells the app\r\nand the user of their location within the route hierarchy of the app. It\r\nsometimes even contains state. The URL consequently serves as an excellent\r\nfirst assertion when writing integration tests.\n\nWhen writing [Cypress](https://www.cypress.io/) integration tests, there are\r\nseveral ways for us to assert about the current URL.\r\n\r\nThe [`cy.url()`](https://docs.cypress.io/api/commands/url.html#Syntax) and\r\n[`cy.location()`](https://docs.cypress.io/api/commands/location.html#Syntax)\r\nfunctions are the primary ways for us to gain access to information about\r\nthe current state of the URL. From there, we can perform a variety of\r\nassertions.\r\n\r\nWe can assert about the entire URL:\r\n\r\n```javascript\r\n// URL: http://localhost:8000/pokemon\r\ncy.url().should('eq', 'http://localhost:8000/pokemon');\r\n// ✅ passes\r\n```\r\n\r\nThis is certain the most precise assertion. The tradeoff is that it requires\r\nus to know something about the environment configuration (namely,\r\n`baseUrl`). This can get verbose and make our tests brittle.\r\n\r\nAlternatively, we can make a partial assertion about the URL:\r\n\r\n```javascript\r\n// URL: http://localhost:8000/pokemon\r\ncy.url().should('contain', '/pokemon');\r\n// ✅ passes\r\n```\r\n\r\nThis frees us from having to specify the full path with the base URL. This,\r\ntoo, has a potential drawback. This way of asserting about the URL can be\r\noverly permissive and may result in a false-positive.\r\n\r\nImagine we are writing a test for our pokemon _show_ page. As part of that\r\ntest we want to be sure that the _back_ button works. We are on the show\r\npage for a specific pokemon (`/pokemon/2`) and our _back_ button is not\r\ncorrectly wired up yet. Here is our assertion:\r\n\r\n```javascript\r\n// Click: 'Back' -- no change in URL\r\n// URL: http://localhost:8000/pokemon/2\r\ncy.url().should('contain', '/pokemon');\r\n// ✅ passes (false positive)\r\n```\r\n\r\nWhat we'd like is a stricter assertion without the need to specify the base\r\nURL. This is where `cy.location()` can help.\r\n\r\n```javascript\r\n// Click: 'Back' -- no change in URL\r\n// URL: http://localhost:8000/pokemon/2\r\ncy.location('pathname').should('eq', '/pokemon');\r\n// ❌ fails\r\n```\r\n\r\nThis time our assertion correctly fails because `/pokemon` does not match\r\nthe current _pathname_ of `/pokemon/2`. Once we update our application code\r\nwith a working _back_ button we should see this test pass. Going forward we\r\nwill have the confidence that if the _back_ button breaks again, so will the\r\ntest.\r\n\r\nWe looked at a couple ways to assert about the current state of your app's\r\nURL. Though `cy.url()` can certainly be used to this end, I recommend\r\nutilizing `cy.location()`. There is a lot more to `cy.location()` than we\r\ncovered in this post. Check out the\r\n[examples](https://docs.cypress.io/api/commands/location.html#Examples) in\r\nthe Cypress docs for all the other ways this function can be used to assert\r\nabout the URL.\r\n\r\n---\r\n\r\nWant to see more of the code? Check out the [supplementary repository](https://github.com/jbranchaud/gatsby-cypress-example).\r\n\r\nCover photo credit: \u003ca style=\"background-color:black;color:white;text-decoration:none;padding:4px 6px;font-family:-apple-system, BlinkMacSystemFont, \u0026quot;San Francisco\u0026quot;, \u0026quot;Helvetica Neue\u0026quot;, Helvetica, Ubuntu, Roboto, Noto, \u0026quot;Segoe UI\u0026quot;, Arial, sans-serif;font-size:12px;font-weight:bold;line-height:1.2;display:inline-block;border-radius:3px\" href=\"https://unsplash.com/@jffortier?utm_medium=referral\u0026amp;utm_campaign=photographer-credit\u0026amp;utm_content=creditBadge\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Download free do whatever you want high-resolution photos from Jean-Frederic Fortier\"\u003e\u003cspan style=\"display:inline-block;padding:2px 3px\"\u003e\u003csvg xmlns=\"http://www.w3.org/2000/svg\" style=\"height:12px;width:auto;position:relative;vertical-align:middle;top:-1px;fill:white\" viewBox=\"0 0 32 32\"\u003e\u003ctitle\u003eunsplash-logo\u003c/title\u003e\u003cpath d=\"M20.8 18.1c0 2.7-2.2 4.8-4.8 4.8s-4.8-2.1-4.8-4.8c0-2.7 2.2-4.8 4.8-4.8 2.7.1 4.8 2.2 4.8 4.8zm11.2-7.4v14.9c0 2.3-1.9 4.3-4.3 4.3h-23.4c-2.4 0-4.3-1.9-4.3-4.3v-15c0-2.3 1.9-4.3 4.3-4.3h3.7l.8-2.3c.4-1.1 1.7-2 2.9-2h8.6c1.2 0 2.5.9 2.9 2l.8 2.4h3.7c2.4 0 4.3 1.9 4.3 4.3zm-8.6 7.5c0-4.1-3.3-7.5-7.5-7.5-4.1 0-7.5 3.4-7.5 7.5s3.3 7.5 7.5 7.5c4.2-.1 7.5-3.4 7.5-7.5z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/span\u003e\u003cspan style=\"display:inline-block;padding:2px 3px\"\u003eJean-Frederic Fortier\u003c/span\u003e\u003c/a\u003e","content_text":"The URL that appears in the browser's URL bar is one of the first and\nprimary ways that users interact with our web apps. The URL tells the app\nand the user of their location within the route hierarchy of the app. It\nsometimes even contains state. The URL consequently serves as an excellent\nfirst assertion when writing integration tests.\n\nWhen writing Cypress integration tests, there are\nseveral ways for us to assert about the current URL.\n\nThe cy.url() and\ncy.location()\nfunctions are the primary ways for us to gain access to information about\nthe current state of the URL. From there, we can perform a variety of\nassertions.\n\nWe can assert about the entire URL:\n// URL: http://localhost:8000/pokemon\ncy.url().should('eq', 'http://localhost:8000/pokemon');\n// ✅ passes\n\nThis is certain the most precise assertion. The tradeoff is that it requires\nus to know something about the environment configuration (namely,\nbaseUrl). This can get verbose and make our tests brittle.\n\nAlternatively, we can make a partial assertion about the URL:\n// URL: http://localhost:8000/pokemon\ncy.url().should('contain', '/pokemon');\n// ✅ passes\n\nThis frees us from having to specify the full path with the base URL. This,\ntoo, has a potential drawback. This way of asserting about the URL can be\noverly permissive and may result in a false-positive.\n\nImagine we are writing a test for our pokemon show page. As part of that\ntest we want to be sure that the back button works. We are on the show\npage for a specific pokemon (/pokemon/2) and our back button is not\ncorrectly wired up yet. Here is our assertion:\n// Click: 'Back' -- no change in URL\n// URL: http://localhost:8000/pokemon/2\ncy.url().should('contain', '/pokemon');\n// ✅ passes (false positive)\n\nWhat we'd like is a stricter assertion without the need to specify the base\nURL. This is where cy.location() can help.\n// Click: 'Back' -- no change in URL\n// URL: http://localhost:8000/pokemon/2\ncy.location('pathname').should('eq', '/pokemon');\n// ❌ fails\n\nThis time our assertion correctly fails because /pokemon does not match\nthe current pathname of /pokemon/2. Once we update our application code\nwith a working back button we should see this test pass. Going forward we\nwill have the confidence that if the back button breaks again, so will the\ntest.\n\nWe looked at a couple ways to assert about the current state of your app's\nURL. Though cy.url() can certainly be used to this end, I recommend\nutilizing cy.location(). There is a lot more to cy.location() than we\ncovered in this post. Check out the\nexamples in\nthe Cypress docs for all the other ways this function can be used to assert\nabout the URL.\n\n\n\nWant to see more of the code? Check out the supplementary repository.\n\nCover photo credit: unsplash-logoJean-Frederic Fortier\n","summary":"The URL that appears in the browser's URL bar is one of the first and\nprimary ways that users interact with our web apps. The URL tells the app\nand the user of their location within the route hierarchy of the app. It\nsometimes even contains state. The URL consequently serves as an excellent\nfirst assertion when writing integration tests.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/444/where-am-i.jpg","date_published":"2018-08-23T09:00:00-04:00","data_modified":"2018-08-21T15:59:54-04:00","author":{"name":"Josh Branchaud","url":"https://hashrocket.com/team/josh-branchaud","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/88/josh-branchaud.jpg"},"tags":["Javascript","Testing","React","Cypress"]},{"id":"https://hashrocket.com/blog/posts/when-to-use-redux","url":"https://hashrocket.com/blog/posts/when-to-use-redux","title":"When To Use Redux","content_html":"Have you had a conversation about [Redux](https://redux.js.org/) recently that has left you wondering — despite all of its praise and the assured recommendations — if you made a big mistake when you typed the words, `yarn add redux`?\n\n\u003cdiv style=\"display: flex; justify-content: center\"\u003e\u003cblockquote class=\"twitter-tweet\" data-lang=\"en\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eJust got home from the React thought leader meeting. Bad news, we apparently all hate Redux now so please start rewriting your app as soon as convenient, thanks\u003c/p\u003e\u0026mdash; Jani Eväkallio (@jevakallio) \u003ca href=\"https://twitter.com/jevakallio/status/962968823648391168?ref_src=twsrc%5Etfw\"\u003eFebruary 12, 2018\u003c/a\u003e\u003c/blockquote\u003e\r\n\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\u003c/div\u003e\r\n\r\nJani is being a bit facetious here. But if you’ve been following along on twitter, there has been a lot of discussion about redux. The crux of the conversation is aimed at whether most apps even call for the use of redux and consequently if we, as developers, are using it when we don’t need it.\r\n\r\n\u003cdiv style=\"display: flex; justify-content: center\"\u003e\u003cblockquote class=\"twitter-tweet\" data-lang=\"en\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eRealization: Putting Redux in our company framework by default was a mistake.\u003cbr\u003e\u003cbr\u003eResult:\u003cbr\u003e1 People connect *every* component.\u003cbr\u003e2 People embed Redux in \u0026quot;reusable\u0026quot; components.\u003cbr\u003e3 Everyone uses Redux. Even when they don\u0026#39;t need it.\u003cbr\u003e4 People don\u0026#39;t know how to build an app with just React.\u003c/p\u003e\u0026mdash; Cory House 🏠 (@housecor) \u003ca href=\"https://twitter.com/housecor/status/962754389533429760?ref_src=twsrc%5Etfw\"\u003eFebruary 11, 2018\u003c/a\u003e\u003c/blockquote\u003e\r\n\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\u003c/div\u003e\r\n\r\nI see this sentiment time and time again. We’ve been presented with this powerful tool and set of patterns for managing state in our apps. Then with wild abandon we plaster our entire app with reducers, actions, connected components, and tangled dispatch functions. Once you’ve mastered the Redux flow, everything starts to look like global state.\r\n\r\nIn my mind the conversation boils down to this: we tend to overuse Redux and that has some consequences for our apps and how we build them. I’ve had my share of moments staring at the beginnings of a component wondering if component state will suffice or if I might as well wire it up to Redux seeing as most everything else is. I’ve also spent plenty of time navigating between the reducer file, action file, container, and presentational component wondering what all the ceremony is getting me besides cramped fingers.\r\n\r\nSo, let’s leave the thought-leadery platitudes behind and (as [Scott Rogowsky](http://gaia.adage.com/images/bin/image/HQTrivia2.gif) would say) get down to the nitty gritty.\r\nWhen **should** we use Redux?\r\n\r\nIn the midst of a chorus of “don’t use Redux unless you need it”, “don’t use Redux for everything”, and “only pull in Redux once you feel the pain of managing state without it”, it is easy to be left scratching our heads wondering, “well, geez, I was told how great this library is, but I’m not sure if I’m even supposed to be using it.”\r\nIt’d be helpful to have a set of guidelines. I think Ken Wheeler gets us pointed in the right direction.\r\n\r\n\u003cdiv style=\"display: flex; justify-content: center\"\u003e\u003cblockquote class=\"twitter-tweet\" data-conversation=\"none\" data-lang=\"en\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eFWIW, I think a lot of it comes from people using it for EVERYTHING. My personal state preferences:\u003cbr\u003e\u003cbr\u003eServer data: Abstracted away or via something like a graphql client where it\u0026#39;s managed.\u003cbr\u003e\u003cbr\u003eForm state: setState\u003cbr\u003e\u003cbr\u003eLocal state: setState\u003cbr\u003e\u003cbr\u003eGlobal state: Redux or Mobx or Unstated\u003c/p\u003e\u0026mdash; JavaScript Kanye (@ken_wheeler) \u003ca href=\"https://twitter.com/ken_wheeler/status/963047171137384449?ref_src=twsrc%5Etfw\"\u003eFebruary 12, 2018\u003c/a\u003e\u003c/blockquote\u003e\r\n\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\u003c/div\u003e\r\n\r\nIf we have a component, such as a toggle element, that has some basic state that only it cares about, keep that in component state. Similarly, when building out an interactive form, we can treat the parent form component as the arbiter of the state of the form. Component state updated with `setState()` will again suffice.\r\n\r\nThe point at which we want to start thinking about a more managed solution, like Redux, is when that state is global. Most of our state probably isn’t global though. Which brings us to an important take away: we can and should use both local and global state in our apps. Managing everything in Redux is overkill. It may have negative performance implications, it will increase the complexity of your app, make it hard to refactor, and likely reduce the reusability of many of your components.\r\n\r\nSo if we ought to only use Redux for global state, what do we consider to be global state?\r\n\r\n\u003cdiv style=\"display: flex; justify-content: center\"\u003e\u003cblockquote class=\"twitter-tweet\" data-lang=\"en\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eMost common example: I see people using Redux to hold form values. \u003cbr\u003e\u003cbr\u003eRule: If only a single component cares about the data, use local state. \u003ca href=\"https://t.co/9LVAkBwNvN\"\u003ehttps://t.co/9LVAkBwNvN\u003c/a\u003e\u003c/p\u003e\u0026mdash; Cory House 🏠 (@housecor) \u003ca href=\"https://twitter.com/housecor/status/963058289624997888?ref_src=twsrc%5Etfw\"\u003eFebruary 12, 2018\u003c/a\u003e\u003c/blockquote\u003e\r\n\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\u003c/div\u003e\r\n\r\nLet’s invert this rule from Cory House. “If more than one component cares about the data, use global state.”\r\n\r\nBefore we get carried away let’s expand on this a bit more. Technically speaking, people were build big fancy complex React apps before Redux came along. When the same piece of state was needed in disparate parts of the app, they just pushed the state up and up into some parent component until that state could flow down to wherever it was needed. This works. But it can be unpleasant. Component after component has to pass pieces of the state that it doesn’t care about down because some great-great-grandchild needs it. This situation has a name — prop drilling.\r\n\r\nThis is the situation where we want to bring in Redux.\r\n\r\nLet’s make it a bit more concrete. Assume we are building an e-commerce site. We have a checkout page that shows the items we have purchased with their respective prices and the total shopping cart price. We manage all of this state with local component state in the `Checkout` component. Then, we go to implement the `NavBar` component which shows the number of items in the cart and the total shopping cart price. We quickly realize not only that we already have this data managed and rendered in the `Checkout` component, but also that we want both the `Checkout` component and `NavBar` component to stay in sync as the contents of the cart change. In other words, we have two disparate components that need to read and possibly update the same state.\r\n\r\nThis is a perfect time to pull the shopping cart state of our `Checkout` component up into Redux. Once this data is in Redux, the `Checkout` and `NavBar` components can individually connect to Redux with the state and dispatch functions they need.\r\n\r\nWe can assume that our e-commerce site has a bunch of other React-based functionality all of which at this point is managed in local component state. Leave it as is! There is no need to rewrite any of the existing codebase with Redux. If you’re feeling the urge to go on a Redux-ification spree, stifle it. Whatever development-time savings you were able to promise with the introduction of Redux will soon vanish with that ill-conceived refactoring.\r\n\r\nRedux is a powerful tool. It is most powerful if we know when to use it and we have a grasp of what our other options are. Our apps, especially SPAs, can get really complex really quickly. Much of our app state is local state and as such ought to be managed by a component. Once a slice of our state is needed in disparate components — components in distinct component hierarchies or components that are more than two levels apart — it may be time to bring in Redux.\r\n\r\n## Epilogue\r\n\r\nIt is worth noting that there are many other options for state management in the React ecosystem. As mentioned above, it can be perfectly reasonable to manage state with nothing more than `setState()`. A popular alternative to Redux is [Mob](https://mobx.js.org/). Certainly there are many others. This whole conversation is further complicated by the introduction of the Context API in React 16.3.0 as a formalized, first-class feature. Find an approach and tool that works for you, your team, and the particulars of your project.\r\n\r\n\u003cdiv style=\"display: flex; justify-content: center\"\u003e\u003cblockquote class=\"twitter-tweet\" data-lang=\"en\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eThere is no perfect tool / framework / language / process.  All software is created within a context, and trade-offs are made based on that context. \u003cbr\u003e\u003cbr\u003eLearning to see and evaluate technical decisions from this angle will help you ask better questions, and build better systems\u003c/p\u003e\u0026mdash; Caitie McCaffrey (@caitie) \u003ca href=\"https://twitter.com/caitie/status/965660565417807873?ref_src=twsrc%5Etfw\"\u003eFebruary 19, 2018\u003c/a\u003e\u003c/blockquote\u003e\r\n\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\u003c/div\u003e","content_text":"Have you had a conversation about Redux recently that has left you wondering — despite all of its praise and the assured recommendations — if you made a big mistake when you typed the words, yarn add redux?\n\nJust got home from the React thought leader meeting. Bad news, we apparently all hate Redux now so please start rewriting your app as soon as convenient, thanks— Jani Eväkallio (@jevakallio) February 12, 2018\n\n\nJani is being a bit facetious here. But if you’ve been following along on twitter, there has been a lot of discussion about redux. The crux of the conversation is aimed at whether most apps even call for the use of redux and consequently if we, as developers, are using it when we don’t need it.\n\nRealization: Putting Redux in our company framework by default was a mistake.Result:1 People connect *every* component.2 People embed Redux in \"reusable\" components.3 Everyone uses Redux. Even when they don't need it.4 People don't know how to build an app with just React.— Cory House 🏠 (@housecor) February 11, 2018\n\n\nI see this sentiment time and time again. We’ve been presented with this powerful tool and set of patterns for managing state in our apps. Then with wild abandon we plaster our entire app with reducers, actions, connected components, and tangled dispatch functions. Once you’ve mastered the Redux flow, everything starts to look like global state.\n\nIn my mind the conversation boils down to this: we tend to overuse Redux and that has some consequences for our apps and how we build them. I’ve had my share of moments staring at the beginnings of a component wondering if component state will suffice or if I might as well wire it up to Redux seeing as most everything else is. I’ve also spent plenty of time navigating between the reducer file, action file, container, and presentational component wondering what all the ceremony is getting me besides cramped fingers.\n\nSo, let’s leave the thought-leadery platitudes behind and (as Scott Rogowsky would say) get down to the nitty gritty.\nWhen should we use Redux?\n\nIn the midst of a chorus of “don’t use Redux unless you need it”, “don’t use Redux for everything”, and “only pull in Redux once you feel the pain of managing state without it”, it is easy to be left scratching our heads wondering, “well, geez, I was told how great this library is, but I’m not sure if I’m even supposed to be using it.”\nIt’d be helpful to have a set of guidelines. I think Ken Wheeler gets us pointed in the right direction.\n\nFWIW, I think a lot of it comes from people using it for EVERYTHING. My personal state preferences:Server data: Abstracted away or via something like a graphql client where it's managed.Form state: setStateLocal state: setStateGlobal state: Redux or Mobx or Unstated— JavaScript Kanye (@ken_wheeler) February 12, 2018\n\n\nIf we have a component, such as a toggle element, that has some basic state that only it cares about, keep that in component state. Similarly, when building out an interactive form, we can treat the parent form component as the arbiter of the state of the form. Component state updated with setState() will again suffice.\n\nThe point at which we want to start thinking about a more managed solution, like Redux, is when that state is global. Most of our state probably isn’t global though. Which brings us to an important take away: we can and should use both local and global state in our apps. Managing everything in Redux is overkill. It may have negative performance implications, it will increase the complexity of your app, make it hard to refactor, and likely reduce the reusability of many of your components.\n\nSo if we ought to only use Redux for global state, what do we consider to be global state?\n\nMost common example: I see people using Redux to hold form values. Rule: If only a single component cares about the data, use local state. https://t.co/9LVAkBwNvN— Cory House 🏠 (@housecor) February 12, 2018\n\n\nLet’s invert this rule from Cory House. “If more than one component cares about the data, use global state.”\n\nBefore we get carried away let’s expand on this a bit more. Technically speaking, people were build big fancy complex React apps before Redux came along. When the same piece of state was needed in disparate parts of the app, they just pushed the state up and up into some parent component until that state could flow down to wherever it was needed. This works. But it can be unpleasant. Component after component has to pass pieces of the state that it doesn’t care about down because some great-great-grandchild needs it. This situation has a name — prop drilling.\n\nThis is the situation where we want to bring in Redux.\n\nLet’s make it a bit more concrete. Assume we are building an e-commerce site. We have a checkout page that shows the items we have purchased with their respective prices and the total shopping cart price. We manage all of this state with local component state in the Checkout component. Then, we go to implement the NavBar component which shows the number of items in the cart and the total shopping cart price. We quickly realize not only that we already have this data managed and rendered in the Checkout component, but also that we want both the Checkout component and NavBar component to stay in sync as the contents of the cart change. In other words, we have two disparate components that need to read and possibly update the same state.\n\nThis is a perfect time to pull the shopping cart state of our Checkout component up into Redux. Once this data is in Redux, the Checkout and NavBar components can individually connect to Redux with the state and dispatch functions they need.\n\nWe can assume that our e-commerce site has a bunch of other React-based functionality all of which at this point is managed in local component state. Leave it as is! There is no need to rewrite any of the existing codebase with Redux. If you’re feeling the urge to go on a Redux-ification spree, stifle it. Whatever development-time savings you were able to promise with the introduction of Redux will soon vanish with that ill-conceived refactoring.\n\nRedux is a powerful tool. It is most powerful if we know when to use it and we have a grasp of what our other options are. Our apps, especially SPAs, can get really complex really quickly. Much of our app state is local state and as such ought to be managed by a component. Once a slice of our state is needed in disparate components — components in distinct component hierarchies or components that are more than two levels apart — it may be time to bring in Redux.\nEpilogue\n\nIt is worth noting that there are many other options for state management in the React ecosystem. As mentioned above, it can be perfectly reasonable to manage state with nothing more than setState(). A popular alternative to Redux is Mob. Certainly there are many others. This whole conversation is further complicated by the introduction of the Context API in React 16.3.0 as a formalized, first-class feature. Find an approach and tool that works for you, your team, and the particulars of your project.\n\nThere is no perfect tool / framework / language / process.  All software is created within a context, and trade-offs are made based on that context. Learning to see and evaluate technical decisions from this angle will help you ask better questions, and build better systems— Caitie McCaffrey (@caitie) February 19, 2018\n\n","summary":"Have you had a conversation about Redux recently that has left you wondering — despite all of its praise and the assured recommendations — if you made a big mistake when you typed the words, yarn add redux?\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/440/when-to-use-redux.jpg","date_published":"2018-02-20T09:00:00-05:00","data_modified":"2025-10-10T16:01:21-04:00","author":{"name":"Josh Branchaud","url":"https://hashrocket.com/team/josh-branchaud","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/88/josh-branchaud.jpg"},"tags":["React","Javascript"]},{"id":"https://hashrocket.com/blog/posts/go-0-to-60-with-create-react-app","url":"https://hashrocket.com/blog/posts/go-0-to-60-with-create-react-app","title":"Go 0 to 60 with create-react-app","content_html":"I remember the first time I tried out [React.js](https://facebook.github.io/react/). It was several years back. The promise of painlessly building interactive UIs was too enticing to ignore. As I worked through the first couple introductory tutorials, my computer screen began to shimmer in a way that it had never done before. The pure functions and composable components brought the browser to life.\n\nJust as my excitement for React was mounting, I was stopped in my tracks. Before I could go much further I was faced with tons of tangential decisions about how the project would be configured.\r\n\r\n_Can't someone just make these decisions for me?_\r\n\r\nThis is the impetus for [`create-react-app`](https://github.com/facebookincubator/create-react-app).\r\n\r\n\u003e Create React apps with no build configuration.\r\n\r\nThis tagline is another way of saying, \"Create React apps! We made all the build configuration decisions for you.\"\r\n\r\nSo, what kinds of things do we get for free with `create-react-app`?\r\n\r\n- ES6 features compiled by Babel\r\n- JavaScript code linting with ESLint\r\n- Live-reloading with Webpack Dev Server\r\n- Optimized production builds\r\n\r\n## Zero\r\n\r\n`create-react-app` is a CLI tool that we can install globally and use anytime we want to bootstrap a new React project.\r\n\r\nInstall it using `yarn` (or `npm`).\r\n\r\n```bash\r\n$ yarn global add create-react-app\r\n```\r\n\r\nThen create your first CRA-bootstrapped project.\r\n\r\n```bash\r\n$ create-react-app my-app\r\nCreating a new React app in /Users/dev/hashrocket/my-app.\r\n\r\nInstalling packages. This might take a couple of minutes.\r\nInstalling react, react-dom, and react-scripts...\r\n\r\nyarn add v0.27.5\r\ninfo No lockfile found.\r\n[1/4] Resolving packages...\r\n[2/4] Fetching packages...\r\n[3/4] Linking dependencies...\r\n[4/4] Building fresh packages...\r\nsuccess Saved lockfile.\r\nsuccess Saved 894 new dependencies.\r\n...\r\nSuccess! Created my-app at /Users/dev/hashrocket/my-app\r\n```\r\n\r\nOnce the project is set up and all the dependencies are pulled in, CRA let's us know about the main commands available to us.\r\n\r\n```\r\nInside that directory, you can run several commands:\r\n\r\n  yarn start\r\n    Starts the development server.\r\n\r\n  yarn build\r\n    Bundles the app into static files for production.\r\n\r\n  yarn test\r\n    Starts the test runner.\r\n\r\n  yarn eject\r\n    Removes this tool and copies build dependencies, configuration files\r\n    and scripts into the app directory. If you do this, you can’t go back!\r\n\r\nWe suggest that you begin by typing:\r\n\r\n  cd my-app\r\n  yarn start\r\n\r\nHappy hacking!\r\n```\r\n\r\n## Sixty\r\n\r\nThe `yarn start` command is where the magic is.\r\n\r\n\u003ciframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/9WFjyHz-czY\" frameborder=\"0\" allowfullscreen\u003e\u003c/iframe\u003e\r\n\r\nThis video highlights the main features of the [Webpack Dev Server](https://webpack.js.org/configuration/dev-server/) that is kicked off by `yarn start`.\r\n\r\nFirst off, [Webpack](https://webpack.js.org/) is configured with live-reloading. Make some changes to your app, save the file, and see your app update instantly in the browser. This includes state persistence.\r\n\r\nSecond, linting of our JavaScript is provided by [ESLint](http://eslint.org/). Any linting issues, such as an unused variable, will be flagged in the output of the dev server.\r\n\r\nThird, the entire app is being compiled by [Babel](https://babeljs.io/). This means we have access to [the latest ES6 features](http://es6-features.org/). If there are any issues with our JavaScript that prevent compilation, those will also be flagged in the output of the dev server.\r\n\r\nThe idea behind CRA is to present a fairly standard configuration. If you need to go beyond what it provides, you can `yarn eject` your app. This will unpack all of the hidden away parts of CRA such as the Webpack config files. [Read more about that here](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#npm-run-eject).\r\n\r\n## Onward!\r\n\r\nGo! Build that awesome React.js app. CRA gives you a great starting point.\r\n\r\nAn app bootstrapped with CRA can do a lot more than we've discussed here. The project `README.md` has a comprehensive table of contents and goes into lots of detail in each section. Check it out.\r\n\r\nAre you building something bootstrapped with CRA? [Tell me about it on Twitter](https://twitter.com/hashrocket/status/893117081914400768).\r\n\r\n---\r\n\r\nCover photo by \u003ca style=\"background-color:black;color:white;text-decoration:none;padding:4px 6px;font-family:-apple-system, BlinkMacSystemFont, \u0026quot;San Francisco\u0026quot;, \u0026quot;Helvetica Neue\u0026quot;, Helvetica, Ubuntu, Roboto, Noto, \u0026quot;Segoe UI\u0026quot;, Arial, sans-serif;font-size:12px;font-weight:bold;line-height:1.2;display:inline-block;border-radius:3px;\" href=\"https://unsplash.com/@geran?utm_medium=referral\u0026amp;utm_campaign=photographer-credit\u0026amp;utm_content=creditBadge\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Download free do whatever you want high-resolution photos from Geran de Klerk\"\u003e\u003cspan style=\"display:inline-block;padding:2px 3px;\"\u003e\u003csvg xmlns=\"http://www.w3.org/2000/svg\" style=\"height:12px;width:auto;position:relative;vertical-align:middle;top:-1px;fill:white;\" viewBox=\"0 0 32 32\"\u003e\u003ctitle\u003e\u003c/title\u003e\u003cpath d=\"M20.8 18.1c0 2.7-2.2 4.8-4.8 4.8s-4.8-2.1-4.8-4.8c0-2.7 2.2-4.8 4.8-4.8 2.7.1 4.8 2.2 4.8 4.8zm11.2-7.4v14.9c0 2.3-1.9 4.3-4.3 4.3h-23.4c-2.4 0-4.3-1.9-4.3-4.3v-15c0-2.3 1.9-4.3 4.3-4.3h3.7l.8-2.3c.4-1.1 1.7-2 2.9-2h8.6c1.2 0 2.5.9 2.9 2l.8 2.4h3.7c2.4 0 4.3 1.9 4.3 4.3zm-8.6 7.5c0-4.1-3.3-7.5-7.5-7.5-4.1 0-7.5 3.4-7.5 7.5s3.3 7.5 7.5 7.5c4.2-.1 7.5-3.4 7.5-7.5z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/span\u003e\u003cspan style=\"display:inline-block;padding:2px 3px;\"\u003eGeran de Klerk\u003c/span\u003e\u003c/a\u003e on Unsplash.com\r\n\r\nOther reading on `create-react-app`:\r\n\r\n- [Create Apps with No Configuration](https://facebook.github.io/react/blog/2016/07/22/create-apps-with-no-configuration.html)\r\n- [What's New in Create React App](https://facebook.github.io/react/blog/2017/05/18/whats-new-in-create-react-app.html)","content_text":"I remember the first time I tried out React.js. It was several years back. The promise of painlessly building interactive UIs was too enticing to ignore. As I worked through the first couple introductory tutorials, my computer screen began to shimmer in a way that it had never done before. The pure functions and composable components brought the browser to life.\n\nJust as my excitement for React was mounting, I was stopped in my tracks. Before I could go much further I was faced with tons of tangential decisions about how the project would be configured.\n\nCan't someone just make these decisions for me?\n\nThis is the impetus for create-react-app.\n\n\nCreate React apps with no build configuration.\n\n\nThis tagline is another way of saying, \"Create React apps! We made all the build configuration decisions for you.\"\n\nSo, what kinds of things do we get for free with create-react-app?\n\n\nES6 features compiled by Babel\nJavaScript code linting with ESLint\nLive-reloading with Webpack Dev Server\nOptimized production builds\n\nZero\n\ncreate-react-app is a CLI tool that we can install globally and use anytime we want to bootstrap a new React project.\n\nInstall it using yarn (or npm).\n$ yarn global add create-react-app\n\nThen create your first CRA-bootstrapped project.\n$ create-react-app my-app\nCreating a new React app in /Users/dev/hashrocket/my-app.\n\nInstalling packages. This might take a couple of minutes.\nInstalling react, react-dom, and react-scripts...\n\nyarn add v0.27.5\ninfo No lockfile found.\n[1/4] Resolving packages...\n[2/4] Fetching packages...\n[3/4] Linking dependencies...\n[4/4] Building fresh packages...\nsuccess Saved lockfile.\nsuccess Saved 894 new dependencies.\n...\nSuccess! Created my-app at /Users/dev/hashrocket/my-app\n\nOnce the project is set up and all the dependencies are pulled in, CRA let's us know about the main commands available to us.\nInside that directory, you can run several commands:\n\n  yarn start\n    Starts the development server.\n\n  yarn build\n    Bundles the app into static files for production.\n\n  yarn test\n    Starts the test runner.\n\n  yarn eject\n    Removes this tool and copies build dependencies, configuration files\n    and scripts into the app directory. If you do this, you can’t go back!\n\nWe suggest that you begin by typing:\n\n  cd my-app\n  yarn start\n\nHappy hacking!\nSixty\n\nThe yarn start command is where the magic is.\n\n\n\nThis video highlights the main features of the Webpack Dev Server that is kicked off by yarn start.\n\nFirst off, Webpack is configured with live-reloading. Make some changes to your app, save the file, and see your app update instantly in the browser. This includes state persistence.\n\nSecond, linting of our JavaScript is provided by ESLint. Any linting issues, such as an unused variable, will be flagged in the output of the dev server.\n\nThird, the entire app is being compiled by Babel. This means we have access to the latest ES6 features. If there are any issues with our JavaScript that prevent compilation, those will also be flagged in the output of the dev server.\n\nThe idea behind CRA is to present a fairly standard configuration. If you need to go beyond what it provides, you can yarn eject your app. This will unpack all of the hidden away parts of CRA such as the Webpack config files. Read more about that here.\nOnward!\n\nGo! Build that awesome React.js app. CRA gives you a great starting point.\n\nAn app bootstrapped with CRA can do a lot more than we've discussed here. The project README.md has a comprehensive table of contents and goes into lots of detail in each section. Check it out.\n\nAre you building something bootstrapped with CRA? Tell me about it on Twitter.\n\n\n\nCover photo by Geran de Klerk on Unsplash.com\n\nOther reading on create-react-app:\n\n\nCreate Apps with No Configuration\nWhat's New in Create React App\n\n","summary":"I remember the first time I tried out React.js. It was several years back. The promise of painlessly building interactive UIs was too enticing to ignore. As I worked through the first couple introductory tutorials, my computer screen began to shimmer in a way that it had never done before. The pure functions and composable components brought the browser to life.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/419/go-0-to-60-with-create-react-app.jpg","date_published":"2017-08-03T09:00:00-04:00","data_modified":"2025-10-10T15:04:38-04:00","author":{"name":"Josh Branchaud","url":"https://hashrocket.com/team/josh-branchaud","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/88/josh-branchaud.jpg"},"tags":["React","Javascript"]},{"id":"https://hashrocket.com/blog/posts/get-started-with-redux-form","url":"https://hashrocket.com/blog/posts/get-started-with-redux-form","title":"Get Started with redux-form","content_html":"The [`redux-form`](http://redux-form.com/7.0.1/) library bills itself as the best way to manage your form state in Redux. It provides a higher-order form component and a collection of container components for dealing with forms in a React and Redux powered application. Most importantly, it makes it easy to get a form up and running with state management and validations baked in.\n\n![redux-form sign in](http://i.imgur.com/DOqmaMN.gif)\r\n\r\nTo get a feel for what `redux-form` can do for us, let's build a simple Sign In form. You can follow along below or check out the [resulting source code](https://github.com/hashrocket/get-started-with-redux-form) directly.\r\n\r\n## Start with React\r\n\r\nWe are going to start by getting the React portion of this application setup. The easiest way to spin up a React app is with [`create-react-app`](https://github.com/facebookincubator/create-react-app). Install it with `npm` or `yarn` if you don't already have it globally available.\r\n\r\n```bash\r\n$ yarn global add create-react-app\r\n```\r\n\r\nLet's generate our project.\r\n\r\n```bash\r\n$ create-react-app redux-form-sign-in\r\n```\r\n\r\nThe `create-react-app` binary that is now available on our machine can be used to bootstrap a React app with all kinds of goodies -- live code reloading in development and production bundle building -- ready to go.\r\n\r\nLet's jump into our project and kick off a live reloading development server.\r\n\r\n```bash\r\n$ cd redux-form-sign-in\r\n$ yarn start\r\n```\r\n\r\nAt this point you should see your browser pointed to `localhost:3000` with a page reading _Welcome to React_.\r\n\r\n## Is This Thing Plugged In?\r\n\r\nWe can see the live-reload in action by altering `src/App.js`.\r\n\r\n```javascript\r\n    return (\r\n      \u003cdiv className=\"App\"\u003e\r\n        \u003cdiv className=\"App-header\"\u003e\r\n          \u003cimg src={logo} className=\"App-logo\" alt=\"logo\" /\u003e\r\n          \u003ch2\u003eRedux Form Sign In App\u003c/h2\u003e\r\n        \u003c/div\u003e\r\n        \u003cp className=\"App-intro\"\u003e\r\n          To get started, edit \u003ccode\u003esrc/App.js\u003c/code\u003e and save to reload.\r\n        \u003c/p\u003e\r\n      \u003c/div\u003e\r\n    );\r\n```\r\n\r\nChange the text in the `h2`, save the file, and then switch back to the browser to see the changes almost instantly.\r\n\r\n`create-react-app` made it really easy to get to a point where we can just iterate on our app. We didn't have to fiddle with Webpack configurations or any other project setup.\r\n\r\nNext, let's change the prompt in the app intro.\r\n\r\n```javascript\r\n        \u003cp className=\"App-intro\"\u003e\r\n          Sign in here if you already have an account\r\n        \u003c/p\u003e\r\n```\r\n\r\nOur app is now begging for a form which brings us to `redux-form`, the focus of this post.\r\n\r\n## Satisfying Some Dependencies\r\n\r\nLet's add the `redux-form` dependency to our project.\r\n\r\n```\r\n$ yarn add redux-form\r\n```\r\n\r\nIt is now available to our app, so we can import the `reduxForm` function at the top of `src/App.js` right after the `react` import.\r\n\r\n```javascript\r\nimport React, { Component } from 'react';\r\nimport { reduxForm } from 'redux-form';\r\n```\r\n\r\nIf we save the file, our development server will have a complaint for us regarding an unmet dependency.\r\n\r\n```\r\nModule not found: Can't resolve 'react-redux' in '/Users/dev/hashrocket/redux-form-sign-in/node_modules/redux-form/es'\r\n```\r\n\r\nThe same issue will arise for the dependency on `redux` itself. So, you'll want to add both to the project.\r\n\r\n```bash\r\n$ yarn add react-redux redux\r\n```\r\n\r\nTrigger a reload by saving and you'll see that the code is successfully compiling again. Not without warnings though. `reduxForm` is never being used, so let's use it.\r\n\r\n## A Basic Form\r\n\r\nThe `reduxForm` function is the higher-order component (HOC) that creates our super-charged form component. In particular, it wires this component up to redux for management of our form's state.\r\n\r\nFirst, let's add an empty form in a presentational component.\r\n\r\n```javascript\r\n// src/App.js\r\nlet SignInForm = props =\u003e {\r\n\treturn \u003cform /\u003e;\r\n};\r\n```\r\n\r\nThen, let's transform it into a redux-connected form using the `reduxForm` HOC.\r\n\r\n```javascript\r\n// src/App.js\r\nSignInForm = reduxForm({\r\n  form: 'signIn',\r\n})(SignInForm);\r\n```\r\n\r\nThe only required configuration property is `form` which specifies a unique name for the form component. If you wanted to create multiple instances of the same form on a page, each would need a separate name in order to manage their separate form states.\r\n\r\nLet's render our new form component into our existing React app.\r\n\r\n```javascript\r\n// src/App.js\r\n    return (\r\n      \u003cdiv className=\"App\"\u003e\r\n        \u003cdiv className=\"App-header\"\u003e\r\n          \u003cimg src={logo} className=\"App-logo\" alt=\"logo\" /\u003e\r\n          \u003ch2\u003eRedux Form Sign In App\u003c/h2\u003e\r\n        \u003c/div\u003e\r\n        \u003cp className=\"App-intro\"\u003eSign in here if you already have an account\u003c/p\u003e\r\n        \u003cSignInForm /\u003e\r\n      \u003c/div\u003e\r\n    );\r\n```\r\n\r\nSaving results in successful compilation, but there is a runtime error. The details of which are displayed in your browser.\r\n\r\n## A Redux Store\r\n\r\nThe first part of the error reads:\r\n\r\n\u003e Could not find \"store\" in either the context or props of \"Connect(Form(SignInForm))\".\r\n\r\nWe are missing a redux store to which `reduxForm` can connect our form component. The store is the place where redux will manage our form's state. The error helpfully suggests two ways to address this problem. Let's do the first and add a provider to `src/index.js`.\r\n\r\n```javascript\r\n// src/index.js\r\n// ... leave existing import statements intact\r\nimport { Provider } from 'react-redux';\r\nimport { createStore, combineReducers } from 'redux';\r\nimport { reducer as formReducer } from 'redux-form';\r\n\r\nconst rootReducer = combineReducers({\r\n  form: formReducer,\r\n});\r\n\r\nconst store = createStore(rootReducer);\r\n\r\nReactDOM.render(\r\n  \u003cProvider store={store}\u003e\r\n    \u003cApp /\u003e\r\n  \u003c/Provider\u003e,\r\n  document.getElementById('root')\r\n);\r\n// ...\r\n```\r\n\r\nThe `Provider` is a component from `react-redux` that makes our store available to child components. The exact details are abstracted away by `redux-form`. The [`react-redux` docs](https://github.com/reactjs/react-redux/blob/master/docs/api.md#provider-store) have more details if you are interested.\r\n\r\nThe `Provider` requires one prop, the `store`. We set up our store with `createStore` from redux. `createStore` is given a top-level reducer that it can use to update any of our state in response to actions, such as changes to the form. We construct this top-level reducer, `rootReducer`, using `combineReducers`. Don't worry if you are not familiar with Redux's core concepts, that is the extent of what we need to know for this post. If you want to know more I do highly recommend Dan Abramov's course, ['Getting Started with Redux'](https://egghead.io/courses/getting-started-with-redux).\r\n\r\nSave these changes and our form will render successfully. Because we didn't add any form elements yet, you'll have to inspect the DOM with dev tools to confirm that.\r\n\r\n## A Form with Fields\r\n\r\nA form without any fields isn't much of a form. So, let's add some with the `Field` component provided by `redux-form`. Because this is a Sign In form, we'll need an _Email_ field, a _Password_ field, and a submit button to sign in.\r\n\r\n```javascript\r\n// src/App.js\r\nlet SignInForm = props =\u003e {\r\n  return (\r\n    \u003cform\u003e\r\n      \u003clabel\u003eEmail\u003c/label\u003e\r\n      \u003cdiv\u003e\r\n        \u003cField type=\"text\" name=\"email\" component=\"input\" /\u003e\r\n      \u003c/div\u003e\r\n      \u003clabel\u003ePassword\u003c/label\u003e\r\n      \u003cdiv\u003e\r\n        \u003cField type=\"password\" name=\"password\" component=\"input\" /\u003e\r\n      \u003c/div\u003e\r\n      \u003cbutton type=\"submit\"\u003eSign in\u003c/button\u003e\r\n    \u003c/form\u003e\r\n  );\r\n};\r\n```\r\n\r\nThe `label` and `button` parts of the above JSX are pretty standard parts of a form. The interesting bit is the `Field` component. For each we specify the `component` prop as `input` because these, for the time being, are both simply `input` tags. The `type` prop gets passed down to each of the inputs specifying one as a standard `text` input and the other as a `password` input. The `name` is important because it will be used to identify the state of the fields in the redux store.\r\n\r\nOur app won't recognize the `Field` component until we import it, so let's update this statement at the top of the same file.\r\n\r\n```javascript\r\n// src/App.js\r\nimport { reduxForm, Field } from 'redux-form';\r\n```\r\n\r\nIf we save our file and check out the re-rendered page, we'll see our form. Go ahead and inspect the DOM to see how the `Field` components were translated to `\u003cinput\u003e` tags.\r\n\r\nUnfortunately, this form isn't much to look at right now. I'll leave styling it as an exercise for the reader.\r\n\r\n## Form State\r\n\r\nClicking our _Sign in_ button at this point isn't all that satisfying. We want to know that the values that have been entered into our form fields are being managed by redux. Let's pass down a callback function that our form can call when it is submitted.\r\n\r\n```javascript\r\n// src/App.js\r\n  handleSignIn = values =\u003e {\r\n    console.log('Submitting the following values:');\r\n    console.log(`Email: ${values.email}`);\r\n    console.log(`Password: ${values.password}`);\r\n  };\r\n```\r\n\r\nThis function will simply spit out the form values with `console.log`. This is more or less where you'd want to integrate the app to some backend system. We won't deal with a backend in this post.\r\n\r\nIf we give this handler function to our redux form, `SignInForm`, via the `onSubmit` prop, it will be available to us in the props of our form component.\r\n\r\n```javascript\r\n// src/App.js\r\n  render() {\r\n    return (\r\n      \u003cdiv className=\"App\"\u003e\r\n        \u003cdiv className=\"App-header\"\u003e\r\n          \u003cimg src={logo} className=\"App-logo\" alt=\"logo\" /\u003e\r\n          \u003ch2\u003eRedux Form Sign In App\u003c/h2\u003e\r\n        \u003c/div\u003e\r\n        \u003cp className=\"App-intro\"\u003eSign in here if you already have an account\u003c/p\u003e\r\n        \u003cSignInForm onSubmit={this.handleSignIn} /\u003e\r\n      \u003c/div\u003e\r\n    );\r\n  }\r\n```\r\n\r\nThe callback function that we passed in to our form is wrapped in the `handleSubmit` function. This can be destructured from our form's props and passed in to the `onSubmit` of our `\u003cform\u003e`.\r\n\r\n```javascript\r\n// src/App.js\r\n  const { handleSubmit } = props;\r\n  return (\r\n    \u003cform onSubmit={handleSubmit}\u003e\r\n      // ...\r\n    \u003c/form\u003e\r\n  );\r\n```\r\n\r\nEnter in a value for `Email` and `Password` and hit submit. You should see those values in the console output. Awesome!\r\n\r\nWe don't want our form to allow just any values though.\r\n\r\n## Do You Validate?\r\n\r\n`redux-form` comes with built-in support for both synchronous and asynchronous validations. To start, let's add synchronous validations to require both the email and password; that is, we won't allow blank values. This can be done by defining a `validate` function.\r\n\r\n```javascript\r\n// src/App.js\r\nconst validate = values =\u003e {\r\n  const errors = {};\r\n\r\n  if (!values.email) {\r\n    console.log('email is required');\r\n    errors.email = 'Required';\r\n  }\r\n\r\n  if (!values.password) {\r\n    console.log('password is required');\r\n    errors.password = 'Required';\r\n  }\r\n\r\n  return errors;\r\n};\r\n```\r\n\r\nThis function starts with `errors` as an empty object. If the function ultimately returns an empty object, then there were no errors and the form is valid. In fact, if you don't define the `validate` function, it defaults to `(values, props) =\u003e ({})` -- always valid.\r\n\r\nThe conditional logic in the middle of our function is what decides whether to flag any validation errors. Our current implementation checks for the presence of each field. Additionally, we `console.log` every time a validation check fails so that we can see it working as we develop.\r\n\r\nTo override the default implementation, we have to tell `SignInForm` to use our implementation. This is done in the form constructor using [es6's object literal property shorthand](http://es6-features.org/#PropertyShorthand).\r\n\r\n```javascript\r\n// src/App.js\r\nSignInForm = reduxForm({\r\n  form: 'signIn',\r\n  validate,\r\n})(SignInForm);\r\n```\r\n\r\nIf you open up the dev tools console and then try typing into the fields, you'll notice that every single change to the form triggers the `validate` function. This ensures that we always have fresh information about the validity of our form. It also means that our form defaults to invalid. As such, we need to be discerning in how and when we display those validation errors. Metadata provided to our `Field` component allows us to do that as we'll see in the next section.\r\n\r\nAdditionally, we may want a validation that ensures the value entered into the email field looks like an email address. To achieve that, we can update our `validate` function like so.\r\n\r\n```javascript\r\n  if (!values.email) {\r\n    console.log('email is required');\r\n    errors.email = 'Required';\r\n  } else if (!/^.+@.+$/i.test(values.email)) {\r\n    console.log('email is invalid');\r\n    errors.email = 'Invalid email address';\r\n  }\r\n```\r\n\r\nIt is a rather crude check of the email address field, but it will suffice for this post. Our `validate` function will now ensure that even if the `email` is present that it still conforms to the email address regex we've laid out.\r\n\r\nAs long as there are validation errors on the `errors` object, we will not be able to submit the form. That is, our `handleSignIn` function will not be triggered. But beyond that and our `console.log` statements, there is no outward expression of the validation errors.\r\n\r\n## Displaying Errors\r\n\r\nEarlier on I hinted that using `input` as the `component` for `Field` was only temporary. Let's extract a functional component with logic for displaying our errors. We can use that custom component, instead of `input`, with `Field`.\r\n\r\n```javascript\r\n// src/App.js\r\nconst InputField = ({\r\n  input,\r\n  label,\r\n  type,\r\n  meta: { touched, error, warning },\r\n}) =\u003e\r\n  \u003cdiv\u003e\r\n    \u003clabel\u003e\r\n      {label}\r\n    \u003c/label\u003e\r\n    \u003cdiv\u003e\r\n      \u003cinput {...input} type={type} /\u003e\r\n    \u003c/div\u003e\r\n  \u003c/div\u003e;\r\n```\r\n\r\nThe props passed into `InputField` include the `input` object, a label which we will need to specify, the type of input it is (e.g. `password`), and some `redux-form` specific metadata.\r\n\r\nUsing `InputField`, we are able to greatly simplify and _dry_ up the JSX in `SignUpForm`.\r\n\r\n```javascript\r\n// src/App.js\r\n  return (\r\n    \u003cform onSubmit={handleSubmit}\u003e\r\n      \u003cField type=\"text\" name=\"email\" component={InputField} label=\"Email\" /\u003e\r\n      \u003cField\r\n        type=\"password\"\r\n        name=\"password\"\r\n        component={InputField}\r\n        label=\"Password\"\r\n      /\u003e\r\n      \u003cbutton type=\"submit\"\u003eSign in\u003c/button\u003e\r\n    \u003c/form\u003e\r\n  );\r\n```\r\n\r\nWe are now in a position to decide how we want to render our errors. Better yet, the JSX for it only needs to be defined in one place, inside the `InputField` component.\r\n\r\n```javascript\r\n// src/App.js\r\nconst InputField = ({\r\n  input,\r\n  label,\r\n  type,\r\n  meta: { touched, error, warning },\r\n}) =\u003e\r\n  \u003cdiv\u003e\r\n    \u003clabel\u003e\r\n      {label}\r\n    \u003c/label\u003e\r\n    \u003cdiv\u003e\r\n      \u003cinput {...input} type={type} /\u003e\r\n    \u003c/div\u003e\r\n    {touched \u0026\u0026\r\n      error \u0026\u0026\r\n      \u003cdiv\u003e\r\n        {error}\r\n      \u003c/div\u003e}\r\n  \u003c/div\u003e;\r\n```\r\n\r\nWe can render a `div` with the error message when there is an error. That isn't quite good enough though. Remember when I mentioned being discerning about when to render errors. The user experience of seeing an error the second the page loads is not great. Fortunately, `redux-form` lets us know through the `touched` flag in the metadata if a particular field has been focused and then blurred. By combining `touched` and the presence of an `error` we are able to display an error message only when necessary.\r\n\r\nCheck it out in the browser to see the various validation messages we programmed into our form.\r\n\r\n\r\n## Conclusion and Next Steps\r\n\r\nThat's it. We made it. A lot was covered in this post. Having gone all the way through, you should now have a solid foundation for getting `redux-form` set up with `create-react-app`, building out a simple form, adding validations, and responding to a form submission.\r\n\r\n`redux-form` is capable of a _lot_ more than what we have explored in this post. There are a number of [examples on their site](http://redux-form.com/7.0.1/examples/) that I'd recommend checking out to see what is available.\r\n\r\nLooking for a next step? Try connecting the form to a backend system. Then, see if you can get _Async Form Validation_ working.\r\n\r\n---\r\n\r\nCover image by \u003ca style=\"background-color:black;color:white;text-decoration:none;padding:4px 6px;font-family:-apple-system, BlinkMacSystemFont, \u0026quot;San Francisco\u0026quot;, \u0026quot;Helvetica Neue\u0026quot;, Helvetica, Ubuntu, Roboto, Noto, \u0026quot;Segoe UI\u0026quot;, Arial, sans-serif;font-size:12px;font-weight:bold;line-height:1.2;display:inline-block;border-radius:3px;\" href=\"http://unsplash.com/@steinart?utm_medium=referral\u0026amp;utm_campaign=photographer-credit\u0026amp;utm_content=creditBadge\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Download free do whatever you want high-resolution photos from Steinar Engeland\"\u003e\u003cspan style=\"display:inline-block;padding:2px 3px;\"\u003e\u003csvg xmlns=\"http://www.w3.org/2000/svg\" style=\"height:12px;width:auto;position:relative;vertical-align:middle;top:-1px;fill:white;\" viewBox=\"0 0 32 32\"\u003e\u003ctitle\u003e\u003c/title\u003e\u003cpath d=\"M20.8 18.1c0 2.7-2.2 4.8-4.8 4.8s-4.8-2.1-4.8-4.8c0-2.7 2.2-4.8 4.8-4.8 2.7.1 4.8 2.2 4.8 4.8zm11.2-7.4v14.9c0 2.3-1.9 4.3-4.3 4.3h-23.4c-2.4 0-4.3-1.9-4.3-4.3v-15c0-2.3 1.9-4.3 4.3-4.3h3.7l.8-2.3c.4-1.1 1.7-2 2.9-2h8.6c1.2 0 2.5.9 2.9 2l.8 2.4h3.7c2.4 0 4.3 1.9 4.3 4.3zm-8.6 7.5c0-4.1-3.3-7.5-7.5-7.5-4.1 0-7.5 3.4-7.5 7.5s3.3 7.5 7.5 7.5c4.2-.1 7.5-3.4 7.5-7.5z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/span\u003e\u003cspan style=\"display:inline-block;padding:2px 3px;\"\u003eSteinar Engeland\u003c/span\u003e\u003c/a\u003e on Unsplash.","content_text":"The redux-form library bills itself as the best way to manage your form state in Redux. It provides a higher-order form component and a collection of container components for dealing with forms in a React and Redux powered application. Most importantly, it makes it easy to get a form up and running with state management and validations baked in.\n\n\n\nTo get a feel for what redux-form can do for us, let's build a simple Sign In form. You can follow along below or check out the resulting source code directly.\nStart with React\n\nWe are going to start by getting the React portion of this application setup. The easiest way to spin up a React app is with create-react-app. Install it with npm or yarn if you don't already have it globally available.\n$ yarn global add create-react-app\n\nLet's generate our project.\n$ create-react-app redux-form-sign-in\n\nThe create-react-app binary that is now available on our machine can be used to bootstrap a React app with all kinds of goodies -- live code reloading in development and production bundle building -- ready to go.\n\nLet's jump into our project and kick off a live reloading development server.\n$ cd redux-form-sign-in\n$ yarn start\n\nAt this point you should see your browser pointed to localhost:3000 with a page reading Welcome to React.\nIs This Thing Plugged In?\n\nWe can see the live-reload in action by altering src/App.js.\n    return (\n      \u0026lt;div className=\"App\"\u0026gt;\n        \u0026lt;div className=\"App-header\"\u0026gt;\n          \u0026lt;img src={logo} className=\"App-logo\" alt=\"logo\" /\u0026gt;\n          \u0026lt;h2\u0026gt;Redux Form Sign In App\u0026lt;/h2\u0026gt;\n        \u0026lt;/div\u0026gt;\n        \u0026lt;p className=\"App-intro\"\u0026gt;\n          To get started, edit \u0026lt;code\u0026gt;src/App.js\u0026lt;/code\u0026gt; and save to reload.\n        \u0026lt;/p\u0026gt;\n      \u0026lt;/div\u0026gt;\n    );\n\nChange the text in the h2, save the file, and then switch back to the browser to see the changes almost instantly.\n\ncreate-react-app made it really easy to get to a point where we can just iterate on our app. We didn't have to fiddle with Webpack configurations or any other project setup.\n\nNext, let's change the prompt in the app intro.\n        \u0026lt;p className=\"App-intro\"\u0026gt;\n          Sign in here if you already have an account\n        \u0026lt;/p\u0026gt;\n\nOur app is now begging for a form which brings us to redux-form, the focus of this post.\nSatisfying Some Dependencies\n\nLet's add the redux-form dependency to our project.\n$ yarn add redux-form\n\nIt is now available to our app, so we can import the reduxForm function at the top of src/App.js right after the react import.\nimport React, { Component } from 'react';\nimport { reduxForm } from 'redux-form';\n\nIf we save the file, our development server will have a complaint for us regarding an unmet dependency.\nModule not found: Can't resolve 'react-redux' in '/Users/dev/hashrocket/redux-form-sign-in/node_modules/redux-form/es'\n\nThe same issue will arise for the dependency on redux itself. So, you'll want to add both to the project.\n$ yarn add react-redux redux\n\nTrigger a reload by saving and you'll see that the code is successfully compiling again. Not without warnings though. reduxForm is never being used, so let's use it.\nA Basic Form\n\nThe reduxForm function is the higher-order component (HOC) that creates our super-charged form component. In particular, it wires this component up to redux for management of our form's state.\n\nFirst, let's add an empty form in a presentational component.\n// src/App.js\nlet SignInForm = props =\u0026gt; {\n    return \u0026lt;form /\u0026gt;;\n};\n\nThen, let's transform it into a redux-connected form using the reduxForm HOC.\n// src/App.js\nSignInForm = reduxForm({\n  form: 'signIn',\n})(SignInForm);\n\nThe only required configuration property is form which specifies a unique name for the form component. If you wanted to create multiple instances of the same form on a page, each would need a separate name in order to manage their separate form states.\n\nLet's render our new form component into our existing React app.\n// src/App.js\n    return (\n      \u0026lt;div className=\"App\"\u0026gt;\n        \u0026lt;div className=\"App-header\"\u0026gt;\n          \u0026lt;img src={logo} className=\"App-logo\" alt=\"logo\" /\u0026gt;\n          \u0026lt;h2\u0026gt;Redux Form Sign In App\u0026lt;/h2\u0026gt;\n        \u0026lt;/div\u0026gt;\n        \u0026lt;p className=\"App-intro\"\u0026gt;Sign in here if you already have an account\u0026lt;/p\u0026gt;\n        \u0026lt;SignInForm /\u0026gt;\n      \u0026lt;/div\u0026gt;\n    );\n\nSaving results in successful compilation, but there is a runtime error. The details of which are displayed in your browser.\nA Redux Store\n\nThe first part of the error reads:\n\n\nCould not find \"store\" in either the context or props of \"Connect(Form(SignInForm))\".\n\n\nWe are missing a redux store to which reduxForm can connect our form component. The store is the place where redux will manage our form's state. The error helpfully suggests two ways to address this problem. Let's do the first and add a provider to src/index.js.\n// src/index.js\n// ... leave existing import statements intact\nimport { Provider } from 'react-redux';\nimport { createStore, combineReducers } from 'redux';\nimport { reducer as formReducer } from 'redux-form';\n\nconst rootReducer = combineReducers({\n  form: formReducer,\n});\n\nconst store = createStore(rootReducer);\n\nReactDOM.render(\n  \u0026lt;Provider store={store}\u0026gt;\n    \u0026lt;App /\u0026gt;\n  \u0026lt;/Provider\u0026gt;,\n  document.getElementById('root')\n);\n// ...\n\nThe Provider is a component from react-redux that makes our store available to child components. The exact details are abstracted away by redux-form. The react-redux docs have more details if you are interested.\n\nThe Provider requires one prop, the store. We set up our store with createStore from redux. createStore is given a top-level reducer that it can use to update any of our state in response to actions, such as changes to the form. We construct this top-level reducer, rootReducer, using combineReducers. Don't worry if you are not familiar with Redux's core concepts, that is the extent of what we need to know for this post. If you want to know more I do highly recommend Dan Abramov's course, 'Getting Started with Redux'.\n\nSave these changes and our form will render successfully. Because we didn't add any form elements yet, you'll have to inspect the DOM with dev tools to confirm that.\nA Form with Fields\n\nA form without any fields isn't much of a form. So, let's add some with the Field component provided by redux-form. Because this is a Sign In form, we'll need an Email field, a Password field, and a submit button to sign in.\n// src/App.js\nlet SignInForm = props =\u0026gt; {\n  return (\n    \u0026lt;form\u0026gt;\n      \u0026lt;label\u0026gt;Email\u0026lt;/label\u0026gt;\n      \u0026lt;div\u0026gt;\n        \u0026lt;Field type=\"text\" name=\"email\" component=\"input\" /\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;label\u0026gt;Password\u0026lt;/label\u0026gt;\n      \u0026lt;div\u0026gt;\n        \u0026lt;Field type=\"password\" name=\"password\" component=\"input\" /\u0026gt;\n      \u0026lt;/div\u0026gt;\n      \u0026lt;button type=\"submit\"\u0026gt;Sign in\u0026lt;/button\u0026gt;\n    \u0026lt;/form\u0026gt;\n  );\n};\n\nThe label and button parts of the above JSX are pretty standard parts of a form. The interesting bit is the Field component. For each we specify the component prop as input because these, for the time being, are both simply input tags. The type prop gets passed down to each of the inputs specifying one as a standard text input and the other as a password input. The name is important because it will be used to identify the state of the fields in the redux store.\n\nOur app won't recognize the Field component until we import it, so let's update this statement at the top of the same file.\n// src/App.js\nimport { reduxForm, Field } from 'redux-form';\n\nIf we save our file and check out the re-rendered page, we'll see our form. Go ahead and inspect the DOM to see how the Field components were translated to \u0026lt;input\u0026gt; tags.\n\nUnfortunately, this form isn't much to look at right now. I'll leave styling it as an exercise for the reader.\nForm State\n\nClicking our Sign in button at this point isn't all that satisfying. We want to know that the values that have been entered into our form fields are being managed by redux. Let's pass down a callback function that our form can call when it is submitted.\n// src/App.js\n  handleSignIn = values =\u0026gt; {\n    console.log('Submitting the following values:');\n    console.log(`Email: ${values.email}`);\n    console.log(`Password: ${values.password}`);\n  };\n\nThis function will simply spit out the form values with console.log. This is more or less where you'd want to integrate the app to some backend system. We won't deal with a backend in this post.\n\nIf we give this handler function to our redux form, SignInForm, via the onSubmit prop, it will be available to us in the props of our form component.\n// src/App.js\n  render() {\n    return (\n      \u0026lt;div className=\"App\"\u0026gt;\n        \u0026lt;div className=\"App-header\"\u0026gt;\n          \u0026lt;img src={logo} className=\"App-logo\" alt=\"logo\" /\u0026gt;\n          \u0026lt;h2\u0026gt;Redux Form Sign In App\u0026lt;/h2\u0026gt;\n        \u0026lt;/div\u0026gt;\n        \u0026lt;p className=\"App-intro\"\u0026gt;Sign in here if you already have an account\u0026lt;/p\u0026gt;\n        \u0026lt;SignInForm onSubmit={this.handleSignIn} /\u0026gt;\n      \u0026lt;/div\u0026gt;\n    );\n  }\n\nThe callback function that we passed in to our form is wrapped in the handleSubmit function. This can be destructured from our form's props and passed in to the onSubmit of our \u0026lt;form\u0026gt;.\n// src/App.js\n  const { handleSubmit } = props;\n  return (\n    \u0026lt;form onSubmit={handleSubmit}\u0026gt;\n      // ...\n    \u0026lt;/form\u0026gt;\n  );\n\nEnter in a value for Email and Password and hit submit. You should see those values in the console output. Awesome!\n\nWe don't want our form to allow just any values though.\nDo You Validate?\n\nredux-form comes with built-in support for both synchronous and asynchronous validations. To start, let's add synchronous validations to require both the email and password; that is, we won't allow blank values. This can be done by defining a validate function.\n// src/App.js\nconst validate = values =\u0026gt; {\n  const errors = {};\n\n  if (!values.email) {\n    console.log('email is required');\n    errors.email = 'Required';\n  }\n\n  if (!values.password) {\n    console.log('password is required');\n    errors.password = 'Required';\n  }\n\n  return errors;\n};\n\nThis function starts with errors as an empty object. If the function ultimately returns an empty object, then there were no errors and the form is valid. In fact, if you don't define the validate function, it defaults to (values, props) =\u0026gt; ({}) -- always valid.\n\nThe conditional logic in the middle of our function is what decides whether to flag any validation errors. Our current implementation checks for the presence of each field. Additionally, we console.log every time a validation check fails so that we can see it working as we develop.\n\nTo override the default implementation, we have to tell SignInForm to use our implementation. This is done in the form constructor using es6's object literal property shorthand.\n// src/App.js\nSignInForm = reduxForm({\n  form: 'signIn',\n  validate,\n})(SignInForm);\n\nIf you open up the dev tools console and then try typing into the fields, you'll notice that every single change to the form triggers the validate function. This ensures that we always have fresh information about the validity of our form. It also means that our form defaults to invalid. As such, we need to be discerning in how and when we display those validation errors. Metadata provided to our Field component allows us to do that as we'll see in the next section.\n\nAdditionally, we may want a validation that ensures the value entered into the email field looks like an email address. To achieve that, we can update our validate function like so.\n  if (!values.email) {\n    console.log('email is required');\n    errors.email = 'Required';\n  } else if (!/^.+@.+$/i.test(values.email)) {\n    console.log('email is invalid');\n    errors.email = 'Invalid email address';\n  }\n\nIt is a rather crude check of the email address field, but it will suffice for this post. Our validate function will now ensure that even if the email is present that it still conforms to the email address regex we've laid out.\n\nAs long as there are validation errors on the errors object, we will not be able to submit the form. That is, our handleSignIn function will not be triggered. But beyond that and our console.log statements, there is no outward expression of the validation errors.\nDisplaying Errors\n\nEarlier on I hinted that using input as the component for Field was only temporary. Let's extract a functional component with logic for displaying our errors. We can use that custom component, instead of input, with Field.\n// src/App.js\nconst InputField = ({\n  input,\n  label,\n  type,\n  meta: { touched, error, warning },\n}) =\u0026gt;\n  \u0026lt;div\u0026gt;\n    \u0026lt;label\u0026gt;\n      {label}\n    \u0026lt;/label\u0026gt;\n    \u0026lt;div\u0026gt;\n      \u0026lt;input {...input} type={type} /\u0026gt;\n    \u0026lt;/div\u0026gt;\n  \u0026lt;/div\u0026gt;;\n\nThe props passed into InputField include the input object, a label which we will need to specify, the type of input it is (e.g. password), and some redux-form specific metadata.\n\nUsing InputField, we are able to greatly simplify and dry up the JSX in SignUpForm.\n// src/App.js\n  return (\n    \u0026lt;form onSubmit={handleSubmit}\u0026gt;\n      \u0026lt;Field type=\"text\" name=\"email\" component={InputField} label=\"Email\" /\u0026gt;\n      \u0026lt;Field\n        type=\"password\"\n        name=\"password\"\n        component={InputField}\n        label=\"Password\"\n      /\u0026gt;\n      \u0026lt;button type=\"submit\"\u0026gt;Sign in\u0026lt;/button\u0026gt;\n    \u0026lt;/form\u0026gt;\n  );\n\nWe are now in a position to decide how we want to render our errors. Better yet, the JSX for it only needs to be defined in one place, inside the InputField component.\n// src/App.js\nconst InputField = ({\n  input,\n  label,\n  type,\n  meta: { touched, error, warning },\n}) =\u0026gt;\n  \u0026lt;div\u0026gt;\n    \u0026lt;label\u0026gt;\n      {label}\n    \u0026lt;/label\u0026gt;\n    \u0026lt;div\u0026gt;\n      \u0026lt;input {...input} type={type} /\u0026gt;\n    \u0026lt;/div\u0026gt;\n    {touched \u0026amp;\u0026amp;\n      error \u0026amp;\u0026amp;\n      \u0026lt;div\u0026gt;\n        {error}\n      \u0026lt;/div\u0026gt;}\n  \u0026lt;/div\u0026gt;;\n\nWe can render a div with the error message when there is an error. That isn't quite good enough though. Remember when I mentioned being discerning about when to render errors. The user experience of seeing an error the second the page loads is not great. Fortunately, redux-form lets us know through the touched flag in the metadata if a particular field has been focused and then blurred. By combining touched and the presence of an error we are able to display an error message only when necessary.\n\nCheck it out in the browser to see the various validation messages we programmed into our form.\nConclusion and Next Steps\n\nThat's it. We made it. A lot was covered in this post. Having gone all the way through, you should now have a solid foundation for getting redux-form set up with create-react-app, building out a simple form, adding validations, and responding to a form submission.\n\nredux-form is capable of a lot more than what we have explored in this post. There are a number of examples on their site that I'd recommend checking out to see what is available.\n\nLooking for a next step? Try connecting the form to a backend system. Then, see if you can get Async Form Validation working.\n\n\n\nCover image by Steinar Engeland on Unsplash.\n","summary":"The redux-form library bills itself as the best way to manage your form state in Redux. It provides a higher-order form component and a collection of container components for dealing with forms in a React and Redux powered application. Most importantly, it makes it easy to get a form up and running with state management and validations baked in.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/416/get-started-with-redux-form.jpg","date_published":"2017-07-27T09:00:00-04:00","data_modified":"2025-10-10T16:01:14-04:00","author":{"name":"Josh Branchaud","url":"https://hashrocket.com/team/josh-branchaud","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/88/josh-branchaud.jpg"},"tags":["React","Javascript"]},{"id":"https://hashrocket.com/blog/posts/writing-prettier-javascript-in-vim","url":"https://hashrocket.com/blog/posts/writing-prettier-javascript-in-vim","title":"Writing Prettier JavaScript in Vim","content_html":"JavaScript is an incredibly powerful and fickle language. In the face of\r\nquickly evolving frameworks and tooling, it can be cognitively challenging\r\nto grapple even with a familiar codebase. Even just having one less thing to\r\nthink about goes a long way in helping us deal with the rest. Figuring out\r\nhow to format each line of code is accidental complexity and one less thing\r\nwe ought to think about.\n\nAs Brian Dunn put it, \"Once you stop formatting your code, life becomes\r\nbeautiful.\" And so does your code, but without any of the effort.\r\n\r\nThis is where [Prettier](https://github.com/prettier/prettier) comes in.\r\n\r\n![Using Prettier in Vim](http://i.imgur.com/RLbpNJ8.gif)\r\n\r\nAt Hashrocket, Vim is an essential part of our development tooling.\r\n[`neoformat`](https://github.com/sbdchd/neoformat) makes it easy to\r\nincorporate `prettier` into our existing workflow. As you can see in the\r\nabove gif, Prettier formats the file on save.\r\n\r\nHere is how we set it up.\r\n\r\n## Step 1\r\n\r\nInstall [`prettier`](https://github.com/prettier/prettier) if you haven't already.\r\n\r\n```bash\r\n$ yarn global add prettier\r\n```\r\n\r\n(or `npm install -g prettier` if you prefer)\r\n\r\n## Step 2\r\n\r\nInstall the [`neoformat`](https://github.com/sbdchd/neoformat) vim plugin.\r\n\r\n```vimscript\r\n\" ~/.vimrc\r\nPlug 'sbdchd/neoformat'\r\n```\r\n\r\nThis plugin is a general-purpose formatter that can be configured to work\r\nacross many filetypes. You can check out the repository README for the full\r\nlist of supported filetypes.\r\n\r\n## Step 3\r\n\r\nNext we need to configure Neoformat to use Vim's `formatprg` as its\r\nformatter.\r\n\r\n```vimscript\r\nlet g:neoformat_try_formatprg = 1\r\n```\r\n\r\nThis way in the following step we can specify some options for `prettier`.\r\n\r\n## Step 4\r\n\r\nWe want `neoformat` to be configured specifically to format our JavaScript\r\nfiles with `prettier` using a few configuration flags of our choosing. We\r\ncan set up an autogroup in our `~/.vimrc` file for that.\r\n\r\n```vimscript\r\n\" ~/.vimrc\r\naugroup NeoformatAutoFormat\r\n    autocmd!\r\n    autocmd FileType javascript setlocal formatprg=prettier\\\r\n                                             \\--stdin\\\r\n                                             \\--print-width\\ 80\\\r\n                                             \\--single-quote\\\r\n                                             \\--trailing-comma\\ es5\r\n    autocmd BufWritePre *.js Neoformat\r\naugroup END\r\n```\r\n\r\nThe `FileType` line sets `prettier` as the format program whenever a\r\n`javascript` file is encountered. `formatprg` will provide the file contents\r\nto `prettier` via `stdin`, so the `--stdin` flag tells `prettier` to expect\r\nas much. The `--print-width` flag tells `prettier` to keep lines under 80\r\ncharacters. And so on. The rest of the details are in the\r\n[Options](https://github.com/prettier/prettier#options) section of the\r\nPrettier README.\r\n\r\n## Step 5\r\n\r\nWe work on a lot of React.js projects, so having support for JSX is\r\nessential. The time it takes to reformat long lines of JSX means that we can\r\nfeel the time savings quite acutely when `prettier` does the formatting for\r\nus.\r\n\r\nTo accommodate React, we can update our existing autogroup by adding support\r\nfor the `jsx` filetype. This requires that you already have the\r\n[`vim-jsx`](https://github.com/mxw/vim-jsx) plugin installed.\r\n\r\n```vimscript\r\n\" ~/.vimrc\r\naugroup NeoformatAutoFormat\r\n    autocmd!\r\n    autocmd FileType javascript,javascript.jsx setlocal formatprg=prettier\\\r\n                                                            \\--stdin\\\r\n                                                            \\--print-width\\ 80\\\r\n                                                            \\--single-quote\\\r\n                                                            \\--trailing-comma\\ es5\r\n    autocmd BufWritePre *.js,*.jsx Neoformat\r\naugroup END\r\n```\r\n\r\n## All Set\r\n\r\nGive this setup a try and enjoy some consistent, well-formatted code. Forget\r\nabout the formatting and focus on the function.\r\n\r\n---\r\n\r\nShoutout to [Dorian Karter](https://twitter.com/dorian_escplan) for\r\nintroducing me to Prettier in Vim.\r\n\r\nCover photo by \u003ca\r\nstyle=\"background-color:black;color:white;text-decoration:none;padding:4px\r\n6px;font-family:-apple-system, BlinkMacSystemFont, \u0026quot;San\r\nFrancisco\u0026quot;, \u0026quot;Helvetica Neue\u0026quot;, Helvetica, Ubuntu, Roboto,\r\nNoto, \u0026quot;Segoe UI\u0026quot;, Arial,\r\nsans-serif;font-size:12px;font-weight:bold;line-height:1.2;display:inline-block;border-radius:3px;\"\r\nhref=\"http://unsplash.com/@heytowner?utm_medium=referral\u0026amp;utm_campaign=photographer-credit\u0026amp;utm_content=creditBadge\"\r\ntarget=\"_blank\" rel=\"noopener noreferrer\" title=\"Download free do whatever\r\nyou want high-resolution photos from JOHN TOWNER\"\u003e\u003cspan\r\nstyle=\"display:inline-block;padding:2px 3px;\"\u003e\u003csvg\r\nxmlns=\"http://www.w3.org/2000/svg\"\r\nstyle=\"height:12px;width:auto;position:relative;vertical-align:middle;top:-1px;fill:white;\"\r\nviewBox=\"0 0 32 32\"\u003e\u003ctitle\u003e\u003c/title\u003e\u003cpath d=\"M20.8 18.1c0 2.7-2.2 4.8-4.8\r\n4.8s-4.8-2.1-4.8-4.8c0-2.7 2.2-4.8 4.8-4.8 2.7.1 4.8 2.2 4.8\r\n4.8zm11.2-7.4v14.9c0 2.3-1.9 4.3-4.3 4.3h-23.4c-2.4\r\n0-4.3-1.9-4.3-4.3v-15c0-2.3 1.9-4.3 4.3-4.3h3.7l.8-2.3c.4-1.1 1.7-2\r\n2.9-2h8.6c1.2 0 2.5.9 2.9 2l.8 2.4h3.7c2.4 0 4.3 1.9 4.3 4.3zm-8.6\r\n7.5c0-4.1-3.3-7.5-7.5-7.5-4.1 0-7.5 3.4-7.5 7.5s3.3 7.5 7.5 7.5c4.2-.1\r\n7.5-3.4 7.5-7.5z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/span\u003e\u003cspan\r\nstyle=\"display:inline-block;padding:2px 3px;\"\u003eJOHN TOWNER\u003c/span\u003e\u003c/a\u003e on\r\nUnsplash","content_text":"JavaScript is an incredibly powerful and fickle language. In the face of\nquickly evolving frameworks and tooling, it can be cognitively challenging\nto grapple even with a familiar codebase. Even just having one less thing to\nthink about goes a long way in helping us deal with the rest. Figuring out\nhow to format each line of code is accidental complexity and one less thing\nwe ought to think about.\n\nAs Brian Dunn put it, \"Once you stop formatting your code, life becomes\nbeautiful.\" And so does your code, but without any of the effort.\n\nThis is where Prettier comes in.\n\n\n\nAt Hashrocket, Vim is an essential part of our development tooling.\nneoformat makes it easy to\nincorporate prettier into our existing workflow. As you can see in the\nabove gif, Prettier formats the file on save.\n\nHere is how we set it up.\nStep 1\n\nInstall prettier if you haven't already.\n$ yarn global add prettier\n\n(or npm install -g prettier if you prefer)\nStep 2\n\nInstall the neoformat vim plugin.\n\" ~/.vimrc\nPlug 'sbdchd/neoformat'\n\nThis plugin is a general-purpose formatter that can be configured to work\nacross many filetypes. You can check out the repository README for the full\nlist of supported filetypes.\nStep 3\n\nNext we need to configure Neoformat to use Vim's formatprg as its\nformatter.\nlet g:neoformat_try_formatprg = 1\n\nThis way in the following step we can specify some options for prettier.\nStep 4\n\nWe want neoformat to be configured specifically to format our JavaScript\nfiles with prettier using a few configuration flags of our choosing. We\ncan set up an autogroup in our ~/.vimrc file for that.\n\" ~/.vimrc\naugroup NeoformatAutoFormat\n    autocmd!\n    autocmd FileType javascript setlocal formatprg=prettier\\\n                                             \\--stdin\\\n                                             \\--print-width\\ 80\\\n                                             \\--single-quote\\\n                                             \\--trailing-comma\\ es5\n    autocmd BufWritePre *.js Neoformat\naugroup END\n\nThe FileType line sets prettier as the format program whenever a\njavascript file is encountered. formatprg will provide the file contents\nto prettier via stdin, so the --stdin flag tells prettier to expect\nas much. The --print-width flag tells prettier to keep lines under 80\ncharacters. And so on. The rest of the details are in the\nOptions section of the\nPrettier README.\nStep 5\n\nWe work on a lot of React.js projects, so having support for JSX is\nessential. The time it takes to reformat long lines of JSX means that we can\nfeel the time savings quite acutely when prettier does the formatting for\nus.\n\nTo accommodate React, we can update our existing autogroup by adding support\nfor the jsx filetype. This requires that you already have the\nvim-jsx plugin installed.\n\" ~/.vimrc\naugroup NeoformatAutoFormat\n    autocmd!\n    autocmd FileType javascript,javascript.jsx setlocal formatprg=prettier\\\n                                                            \\--stdin\\\n                                                            \\--print-width\\ 80\\\n                                                            \\--single-quote\\\n                                                            \\--trailing-comma\\ es5\n    autocmd BufWritePre *.js,*.jsx Neoformat\naugroup END\nAll Set\n\nGive this setup a try and enjoy some consistent, well-formatted code. Forget\nabout the formatting and focus on the function.\n\n\n\nShoutout to Dorian Karter for\nintroducing me to Prettier in Vim.\n\nCover photo by JOHN TOWNER on\nUnsplash\n","summary":"JavaScript is an incredibly powerful and fickle language. In the face of\nquickly evolving frameworks and tooling, it can be cognitively challenging\nto grapple even with a familiar codebase. Even just having one less thing to\nthink about goes a long way in helping us deal with the rest. Figuring out\nhow to format each line of code is accidental complexity and one less thing\nwe ought to think about.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/415/prettier-javascript.jpg","date_published":"2017-07-13T09:00:00-04:00","data_modified":"2017-08-04T12:00:36-04:00","author":{"name":"Josh Branchaud","url":"https://hashrocket.com/team/josh-branchaud","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/88/josh-branchaud.jpg"},"tags":["Javascript","React","Prettier"]},{"id":"https://hashrocket.com/blog/posts/elm-by-example-soup-to-nuts-part-1","url":"https://hashrocket.com/blog/posts/elm-by-example-soup-to-nuts-part-1","title":"Elm by Example: Soup to Nuts","content_html":"I've been experimenting with Elm for the past few months and have come to really appreciate its style of programming. It is very similar to React in the sense that you can render modular components based on DOM events, but the functional style and syntactic sugar are a pleasure to work with. In this blog post I will guide you in building your first Slack inspired component.\n\nBy now you have probably heard about Elm, the statically typed, immutable, Haskell inspired, polite and helpful, functional reactive language for the web.\r\n\r\nIt's extremely FAST too. It consistently performs better than React, Ember, Angular and others in the TODO MVC performance tests.\r\n\r\n![performance comparison](http://elm-lang.org/diagrams/sampleResults.png)\r\n\r\n\r\nI've been experimenting with Elm for the past few months and have come to really appreciate its style of programming. It is very similar to React in the sense that you can render modular components based on DOM events, but the functional style and syntactic sugar are a pleasure to work with.\r\n\r\nMy favorite thing about Elm is that it is statically typed, yet type inferred. What that means is you can prototype quickly, and don't have to use type annotations, but the compiler will infer the types for you by flowing through your code and failing to compile when you did something wrong. This gives rise to Elm's best feature:\r\n\r\n**NO RUNTIME EXCEPTIONS!**\r\n\r\nThis is a really big deal! After having written a fair amount of Elm it almost feels irresponsible writing JavaScript without this feature. Elm accomplishes this by forcing you to handle values that can be null before allowing you to compile your project. It also makes sure that you handle all potential values when using conditionals/pattern matching.\r\n\r\nAs you work with Elm its awesomeness unfolds before you, and you will learn interesting Computer Science concepts, particularly if you have never worked with Haskell or other functional languages. Although Haskell can be hard to learn, Elm is very pragmatic and approachable and can be used to replace both standalone JavaScript libraries and rich UI components. Elm also lends itself really well for game programming due to its rich HTML5 Canvas abstraction and input interaction using signals.\r\n\r\n## Motivation\r\nMy reason for writing this blog post is that I was struggling with some of the more advanced concepts of Elm, namely Signals, Mailboxes, and Ports. I started writing a post about how to roll out your own Model View Update pattern in Elm without the StartApp but it was hard to start without an initial example, so I decided to write this post first to lead into the next one.\r\n\r\nIn this two-part blog post I will take you through building your first Elm component - a Slack inspired quick channel switcher (Cmd+k).\r\n\r\nI chose this component because it was small, practical, and combined multiple Signals, namely HTML Signals and Keyboard Signals, making it an ideal candidate for introducing Signals and Mailboxes.\r\n\r\n![Slack channel switcher](https://www.dropbox.com/s/5fndqdbkoqf3y5r/Screenshot%202016-01-05%2009.23.54.png?dl=1)\r\n\r\n\r\n## Prerequisites\r\nI'm assuming basic familiarity with the Elm syntax. If you are not familiar with the Elm syntax see the [official syntax documentation](http://elm-lang.org/docs/syntax).\r\n\r\nConsider the above to be Part 0.\r\n\r\n## Getting Elm\r\nTo get started you will need to install Elm on your machine.\r\n\r\n```bash\r\nnpm update \u0026\u0026 npm install -g elm\r\n```\r\n\r\nThis article is written for Elm v0.16\r\n\r\nYou can also download the .pkg installer from the elm-lang.org website.\r\n\r\n_You will also need a syntax highlighter for your editor. Here's the one I use for Vim: https://github.com/ElmCast/elm-vim_\r\n\r\n## Installing required packages\r\nElm comes with an especially \"polite\" and quite \"intelligent\" package manager. It takes a Github relative url as an argument.\r\n\r\nCreate a project directory and cd into it.\r\n\r\nInstall the following packages:\r\n\r\n```bash\r\nelm package install evancz/elm-html\r\nelm package install evancz/start-app\r\nelm package install circuithub/elm-html-extra\r\n```\r\n\r\n## Bootstrapping the component\r\nIn your favorite code editor, create a `ChannelSwitcher.elm` file.\r\n\r\nFirst we need to declare the component, this is done with one line in elm which should be at the top of your file.\r\n\r\n```elm\r\nmodule ChannelSwitcher where\r\n```\r\n\r\nNow, below that, we need to import all the necessary modules:\r\n\r\n```elm\r\n-- IMPORTS\r\nimport Html exposing (..)\r\nimport Html.Attributes exposing (..)\r\nimport Html.Events exposing (..)\r\nimport Html.Events.Extra exposing (..)\r\nimport String\r\nimport StartApp.Simple as StartApp\r\n```\r\n\r\nNote that I'm using `exposing (..)` on some of the imports, the `..` exposes all public functions from that module into the current scope so you can call them without prefixing with the module name.\r\n\r\nIt is usually a best practice to avoid this type of exposure as much as possible to prevent naming collisions and ambiguity. However in this case it provides convenience when writing HTML.\r\n\r\n## MODEL VIEW UPDATE\r\nElm uses a Model View Update architecture which dictates the way data flows through an Elm application. You can think of an Elm application as a stream of events which are converted into actions, which then calculate the new state and render HTML.\r\n\r\nI like annotating my code with sections so that it is organized and I know where to look for things so I label it like so:\r\n\r\n\r\n```elm\r\nmodule ChannelSwitcher where\r\n\r\n-- IMPORTS\r\nimport Html exposing (..)\r\nimport Html.Attributes exposing (..)\r\nimport Html.Events exposing (..)\r\nimport Signal\r\nimport StartApp.Simple as StartApp\r\n\r\n-- MODEL\r\n\r\n-- UPDATE\r\n\r\n-- VIEW\r\n```\r\n\r\n## Writing the HTML in Elm\r\nNow that we imported all of the HTML attributes it's time to write some Elm-flavored HTML.\r\n\r\nUnder the view section declare a `view` function, follow that function with a `main` function, the entry point for any component. For now we will just use it to call view so we can see the HTML we generated.\r\n\r\n```elm\r\n-- VIEW\r\nview =\r\n  div [ class \"container\" ] [\r\n    input [ class \"search-box\" ] [ ],\r\n    ul [ class \"collection\" ] [\r\n        li [ class \"collection-item active\" ] [ text \"#Elm\" ],\r\n        li [ class \"collection-item\" ] [ text \"#react.js\" ],\r\n        li [ class \"collection-item\" ] [ text \"#ember\" ]\r\n    ]\r\n  ]\r\n\r\nmain =\r\n\tview\r\n```\r\n\r\nIf you are familiar with React the code above should seem familiar. Think of it as the `render` function in react.\r\n\r\nThe code above should be pretty self explanatory, the first square bracket of each element is its attributes, and the second is the tag content.\r\n\r\nThe `text` function and all other HTML elements (div, input, ul, li) are exposed and available to us by exposing the `Html` module.   The `class` function used in the square brackets is imported from `Html.Attributes`.\r\n\r\nIt is important at this point to think of the markup representing html elements as functions, because they are. When you call `Html.div [] []` in the repl you would get an Elm record representing the DOM element as data. It will look something like this:\r\n\r\n```elm\r\n{ type = \"node\", tag = \"div\", facts = {}, children = {}, namespace = \u003cinternal structure\u003e, descendantsCount = 0 }\r\n```\r\n\r\nThis is important because you cannot have two adjacent elements (e.g. `\u003ch1\u003e\u003c/h1\u003e\u003ch2\u003e\u003c/h2\u003e`) without a top level wrapping element (e.g.  `\u003cdiv\u003e\u003ch1\u003e\u003c/h1\u003e\u003ch2\u003e\u003c/h2\u003e\u003c/div\u003e`) and thinking of `h1` and `h2` as functions you quickly realize there is no nice way to return them as a function result in an immutable programming language without wrapping them with a third function.\r\n\r\n## Compiling and Running in your browser\r\nNow we are ready to compile and run our application. In the terminal run elm make ChannelSwitcher.elm. This will generate an _index.html_ for you, go ahead and open that in your browser.\r\n\r\nYou should be able to see the following interface:\r\n\r\n![First Step](https://www.dropbox.com/s/05upf35d98g91q3/Screenshot%202015-12-30%2014.09.10.png?dl=1)\r\n\r\n## Defining the Model\r\nThe data that we need to flow through our component for it to generate the correct output should represent a list of channels.\r\n\r\nWe start by defining a record, and we will also define an initial model so we have something to work with.\r\n\r\n```elm\r\n-- MODEL\r\ntype alias Model =\r\n  { channels: List String\r\n  , selectedChannel: Int\r\n  , query: String\r\n  }\r\n\r\ninitialModel : Model\r\ninitialModel =\r\n  { channels = [\"Elm\", \"React.js\", \"Ember\", \"Angular 2\", \"Om\", \"OffTopic\" ]\r\n  , selectedChannel = -1\r\n  , query = \"\"\r\n  }\r\n```\r\n\r\nYou don't have to call your type `Model` it can be anything.\r\n\r\n## Defining Actions and the Update function\r\nAs the user is interacting with the component, a new \"state\" of the model will be calculated. For example, `selectedChannel` will start at `-1` and as we press the arrow keys up and down it will change to 0, 1, 2 etc.. Elm creates that new model using the `update` function. Our update function will take an `action` and return a new version of the model with a small modification.\r\n\r\nThis makes it very convenient since you can look at the action type definition (see below) and immediately know what kind of transformations can happen to the model in this component.\r\n\r\n```elm\r\n-- UPDATE\r\ntype Action = NoOp | Filter String | Select Int\r\n\r\nupdate action model =\r\n  case action of\r\n    NoOp -\u003e\r\n      model\r\n\r\n    Filter query -\u003e\r\n      { model | query = query }\r\n\r\n    Select index -\u003e\r\n      { model | selectedChannel = index }\r\n```\r\n\r\nThe `Action` type we defined is a [Union Type](http://elm-lang.org/docs/syntax#union-types) which allows us to perform [Pattern Matching](https://en.wikipedia.org/wiki/Pattern_matching) in the `update` function with the `case` statement.\r\n\r\nThe `Filter String` part is basically a [Tagged Union](https://en.wikipedia.org/wiki/Tagged_union) where `Filter` is the action tag with a `String` argument. This helps us differentiate it from other actions that may have a one string argument.\r\n\r\n## Putting it all together with StartApp\r\nIt's time to put it all together using StartApp.\r\nStartApp lets us declare which methods correspond with our model, update and view parts of our component.\r\n\r\nReplace the `main` function with the following:\r\n\r\n```elm\r\nmain : Signal Html\r\nmain =\r\n  StartApp.start\r\n    { model = initialModel\r\n    , update = update\r\n    , view = view\r\n    }\r\n```\r\n\r\nIf you try and compile the code so far you will get the following message:\r\n\r\n```\r\n==================================== ERRORS ====================================\r\n\r\n-- TYPE MISMATCH ------------------------------------------- ChannelSwitcher.elm\r\n\r\nThe argument to function `start` is causing a mismatch.\r\n\r\n51│   StartApp.start\r\n52│\u003e    { model = initialModel\r\n53│\u003e    , update = update\r\n54│\u003e    , view = view\r\n55│\u003e    }\r\n\r\nFunction `start` is expecting the argument to be:\r\n\r\n    { ..., view : Signal.Address Action -\u003e Model -\u003e Html }\r\n\r\nBut it is:\r\n\r\n    { ..., view : VirtualDom.Node }\r\n\r\nDetected errors in 1 module.\r\n```\r\n\r\nThat's because StartApp is passing a `Mailbox` address and the currently computed model to the view. Don't worry about understanding Mailboxes just yet, we will cover those in the second part of the tutorial. For now, to fix this error let's refactor our `view` function signature to the following, and add a type annotation while we are at it:\r\n\r\n```elm\r\nview : Signal.Address Action -\u003e Model -\u003e Html\r\nview address model =\r\n```\r\n\r\nNow you should be able to compile but as you notice when you open `index.html` the component still does not filter the list. Next we will render the model and implement the search/filter functionality.\r\n\r\n### Rendering the model\r\nLet's render the model now instead of static data. Under the VIEW section we will add a new method that renders the `li` elements using the `channels` list on the model.\r\n\r\n```elm\r\n-- VIEW\r\nrenderChannel : String -\u003e Html\r\nrenderChannel name =\r\n  li [ class \"collection-item\" ] [ text \u003c| \"#\" ++ name ]\r\n\r\nrenderChannels : List String -\u003e Html\r\nrenderChannels channels =\r\n  let\r\n    channelItems = List.map renderChannel channels\r\n  in\r\n    ul [ class \"collection\" ] channelItems\r\n\r\nview : Signal.Address Action -\u003e Model -\u003e Html\r\nview address model =\r\n  div [ class \"card-panel\" ] [\r\n    input [ ] [],\r\n    renderChannels model.channels\r\n  ]\r\n```\r\n\r\nWe created two new methods `renderChannel`, which renders an individual `li` representing a channel with the hash symbol (#), and `renderChannels` which uses a `List.map` to return a list of `li` elements. We then pass that list as the second argument of `ul`. Lastly, we call `renderChannels` from the `view` function, passing in the `model.channels`.\r\n\r\n*Note: If you are wondering about the `\u003c|` operator: it is a reverse pipe and it means the result of everything on the right of that operator (until the closure) will be piped into the function to the left of the operator. It's just a way to avoid parens.*\r\n\r\n## Filtering the list\r\nWe are displaying the list rendered directly from the model, now it's time to filter the view according to the user input in the search box.\r\n\r\nFirst we need to store the filter the user types in on the model. For that we will add an `onInput` event on the input box, so we can filter the list as the user is typing.\r\n\r\n```elm\r\nview : Signal.Address Action -\u003e Model -\u003e Html\r\nview address model =\r\n  div [ class \"card-panel\" ] [\r\n    input [ onInput address Filter ] [],\r\n    renderChannels model.channels\r\n  ]\r\n```\r\n\r\nThe Filter action tag provides us with a free \"constructor\" that takes in a string, that string will be passed in to onInput from the browser as `event.target.value` or in elm-html `targetValue`.\r\n\r\n*Note: `onInput` is not yet part of the `elm-html` package, which is why we imported the `Html.Events.Extra` package which comes from [circuithub/elm-html-extra](https://github.com/circuithub/elm-html-extra)*\r\n\r\nThen we will use the `List.filter` method to only display channels starting with the input text.\r\n\r\n```elm\r\nfilterChannels : List String -\u003e String -\u003e List String\r\nfilterChannels channels query =\r\n  List.filter (String.contains query) channels\r\n```\r\n\r\nThen use this function in the `view` function:\r\n\r\n```elm\r\nview : Signal.Address Action -\u003e Model -\u003e Html\r\nview address model =\r\n  div [ class \"card-panel\" ] [\r\n    input [ onInput address Filter ] [],\r\n    renderChannels (filterChannels model.channels model.query)\r\n  ]\r\n```\r\n\r\nTo make sure that the filter is case insensitive we will need to refactor the `filterChannels` and pass both the query and list item to `String.toLower`.\r\n\r\n```elm\r\nfilterChannels : List String -\u003e String -\u003e List String\r\nfilterChannels channels query =\r\n  let\r\n    containsCaseInsensitive str1 str2 =\r\n      String.contains (String.toLower str1) (String.toLower str2)\r\n  in\r\n    List.filter (containsCaseInsensitive query) channels\r\n```\r\n\r\n## Adding style\r\nFor styling the component we will create a new HTML file and include the [Materialize](https://github.com/Dogfalo/materialize) CSS library.\r\n\r\nThis is what your HTML should look like:\r\n\r\n```html\r\n\u003c!DOCTYPE html\u003e\r\n\u003chtml\u003e\r\n  \u003chead\u003e\r\n    \u003clink rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/css/materialize.min.css\"\u003e\r\n    \u003cscript src=\"channel_switcher.js\"\u003e\u003c/script\u003e\r\n  \u003c/head\u003e\r\n  \u003cbody\u003e\r\n    \u003cdiv id=\"elm-goes-here\" class=\"container\"\u003e\u003c/div\u003e\r\n    \u003cscript\u003e\r\n      Elm.embed(\r\n          Elm.ChannelSwitcher,\r\n          document.getElementById('elm-goes-here')\r\n      );\r\n    \u003c/script\u003e\r\n  \u003c/body\u003e\r\n \u003c/html\u003e\r\n```\r\n\r\nTo compile the Elm file into `channel_switcher.js` use the `--output` flag:\r\n\r\n```bash\r\nelm make ChannelSwitcher.elm --output channel_switcher.js\r\n```\r\n\r\nWhen you open the HTML file you should see something like this:\r\n\r\n![Final](https://www.dropbox.com/s/jcq1a3k1kitfgpd/Screenshot%202016-01-05%2008.46.21.png?dl=1)\r\n\r\n\r\nAnd here is our Elm code so far:\r\n\r\n```elm\r\nmodule ChannelSwitcher where\r\n\r\n-- IMPORTS\r\nimport Html exposing (..)\r\nimport Html.Attributes exposing (..)\r\nimport Html.Events exposing (..)\r\nimport Html.Events.Extra exposing (..)\r\nimport String\r\nimport StartApp.Simple as StartApp\r\n\r\n-- MODEL\r\ntype alias Model =\r\n  { channels: List String\r\n  , selectedChannel: Int\r\n  , query: String\r\n  }\r\n\r\ninitialModel : Model\r\ninitialModel =\r\n  { channels = [\"Elm\", \"React.js\", \"Ember\", \"Angular 2\", \"Om\", \"OffTopic\" ]\r\n  , selectedChannel = -1\r\n  , query = \"\"\r\n  }\r\n\r\n-- UPDATE\r\ntype Action = NoOp | Filter String | Select Int\r\n\r\nupdate action model =\r\n  case action of\r\n    NoOp -\u003e\r\n      model\r\n\r\n    Filter query -\u003e\r\n      { model | query = query }\r\n\r\n    Select index -\u003e\r\n      { model | selectedChannel = index }\r\n\r\n\r\n-- VIEW\r\nfilterChannels : List String -\u003e String -\u003e List String\r\nfilterChannels channels query =\r\n  let\r\n    containsCaseInsensitive str1 str2 =\r\n      String.contains (String.toLower str1) (String.toLower str2)\r\n  in\r\n    List.filter (containsCaseInsensitive query) channels\r\n\r\nrenderChannel : String -\u003e Html\r\nrenderChannel name =\r\n  li [ class \"collection-item\" ] [ text \u003c| \"#\" ++ name ]\r\n\r\nrenderChannels : List String -\u003e Html\r\nrenderChannels channels =\r\n  let\r\n    channelItems = List.map renderChannel channels\r\n  in\r\n    ul [ class \"collection\" ] channelItems\r\n\r\nview : Signal.Address Action -\u003e Model -\u003e Html\r\nview address model =\r\n  div [ class \"card-panel\" ] [\r\n    input [ onInput address Filter ] [],\r\n    renderChannels (filterChannels model.channels model.query)\r\n  ]\r\n\r\nmain : Signal Html\r\nmain =\r\n  StartApp.start\r\n    { model = initialModel\r\n    , update = update\r\n    , view = view\r\n    }\r\n```\r\n\r\n\r\n# What's Next?\r\nIn the next post I will build a version of this component utilizing Messages, Effects and Ports. This will allow us to add keyboard interaction and JavaScript interop.","content_text":"I've been experimenting with Elm for the past few months and have come to really appreciate its style of programming. It is very similar to React in the sense that you can render modular components based on DOM events, but the functional style and syntactic sugar are a pleasure to work with. In this blog post I will guide you in building your first Slack inspired component.\n\nBy now you have probably heard about Elm, the statically typed, immutable, Haskell inspired, polite and helpful, functional reactive language for the web.\n\nIt's extremely FAST too. It consistently performs better than React, Ember, Angular and others in the TODO MVC performance tests.\n\n\n\nI've been experimenting with Elm for the past few months and have come to really appreciate its style of programming. It is very similar to React in the sense that you can render modular components based on DOM events, but the functional style and syntactic sugar are a pleasure to work with.\n\nMy favorite thing about Elm is that it is statically typed, yet type inferred. What that means is you can prototype quickly, and don't have to use type annotations, but the compiler will infer the types for you by flowing through your code and failing to compile when you did something wrong. This gives rise to Elm's best feature:\n\nNO RUNTIME EXCEPTIONS!\n\nThis is a really big deal! After having written a fair amount of Elm it almost feels irresponsible writing JavaScript without this feature. Elm accomplishes this by forcing you to handle values that can be null before allowing you to compile your project. It also makes sure that you handle all potential values when using conditionals/pattern matching.\n\nAs you work with Elm its awesomeness unfolds before you, and you will learn interesting Computer Science concepts, particularly if you have never worked with Haskell or other functional languages. Although Haskell can be hard to learn, Elm is very pragmatic and approachable and can be used to replace both standalone JavaScript libraries and rich UI components. Elm also lends itself really well for game programming due to its rich HTML5 Canvas abstraction and input interaction using signals.\nMotivation\n\nMy reason for writing this blog post is that I was struggling with some of the more advanced concepts of Elm, namely Signals, Mailboxes, and Ports. I started writing a post about how to roll out your own Model View Update pattern in Elm without the StartApp but it was hard to start without an initial example, so I decided to write this post first to lead into the next one.\n\nIn this two-part blog post I will take you through building your first Elm component - a Slack inspired quick channel switcher (Cmd+k).\n\nI chose this component because it was small, practical, and combined multiple Signals, namely HTML Signals and Keyboard Signals, making it an ideal candidate for introducing Signals and Mailboxes.\n\n\nPrerequisites\n\nI'm assuming basic familiarity with the Elm syntax. If you are not familiar with the Elm syntax see the official syntax documentation.\n\nConsider the above to be Part 0.\nGetting Elm\n\nTo get started you will need to install Elm on your machine.\nnpm update \u0026amp;\u0026amp; npm install -g elm\n\nThis article is written for Elm v0.16\n\nYou can also download the .pkg installer from the elm-lang.org website.\n\nYou will also need a syntax highlighter for your editor. Here's the one I use for Vim: https://github.com/ElmCast/elm-vim\nInstalling required packages\n\nElm comes with an especially \"polite\" and quite \"intelligent\" package manager. It takes a Github relative url as an argument.\n\nCreate a project directory and cd into it.\n\nInstall the following packages:\nelm package install evancz/elm-html\nelm package install evancz/start-app\nelm package install circuithub/elm-html-extra\nBootstrapping the component\n\nIn your favorite code editor, create a ChannelSwitcher.elm file.\n\nFirst we need to declare the component, this is done with one line in elm which should be at the top of your file.\nmodule ChannelSwitcher where\n\nNow, below that, we need to import all the necessary modules:\n-- IMPORTS\nimport Html exposing (..)\nimport Html.Attributes exposing (..)\nimport Html.Events exposing (..)\nimport Html.Events.Extra exposing (..)\nimport String\nimport StartApp.Simple as StartApp\n\nNote that I'm using exposing (..) on some of the imports, the .. exposes all public functions from that module into the current scope so you can call them without prefixing with the module name.\n\nIt is usually a best practice to avoid this type of exposure as much as possible to prevent naming collisions and ambiguity. However in this case it provides convenience when writing HTML.\nMODEL VIEW UPDATE\n\nElm uses a Model View Update architecture which dictates the way data flows through an Elm application. You can think of an Elm application as a stream of events which are converted into actions, which then calculate the new state and render HTML.\n\nI like annotating my code with sections so that it is organized and I know where to look for things so I label it like so:\nmodule ChannelSwitcher where\n\n-- IMPORTS\nimport Html exposing (..)\nimport Html.Attributes exposing (..)\nimport Html.Events exposing (..)\nimport Signal\nimport StartApp.Simple as StartApp\n\n-- MODEL\n\n-- UPDATE\n\n-- VIEW\nWriting the HTML in Elm\n\nNow that we imported all of the HTML attributes it's time to write some Elm-flavored HTML.\n\nUnder the view section declare a view function, follow that function with a main function, the entry point for any component. For now we will just use it to call view so we can see the HTML we generated.\n-- VIEW\nview =\n  div [ class \"container\" ] [\n    input [ class \"search-box\" ] [ ],\n    ul [ class \"collection\" ] [\n        li [ class \"collection-item active\" ] [ text \"#Elm\" ],\n        li [ class \"collection-item\" ] [ text \"#react.js\" ],\n        li [ class \"collection-item\" ] [ text \"#ember\" ]\n    ]\n  ]\n\nmain =\n    view\n\nIf you are familiar with React the code above should seem familiar. Think of it as the render function in react.\n\nThe code above should be pretty self explanatory, the first square bracket of each element is its attributes, and the second is the tag content.\n\nThe text function and all other HTML elements (div, input, ul, li) are exposed and available to us by exposing the Html module.   The class function used in the square brackets is imported from Html.Attributes.\n\nIt is important at this point to think of the markup representing html elements as functions, because they are. When you call Html.div [] [] in the repl you would get an Elm record representing the DOM element as data. It will look something like this:\n{ type = \"node\", tag = \"div\", facts = {}, children = {}, namespace = \u0026lt;internal structure\u0026gt;, descendantsCount = 0 }\n\nThis is important because you cannot have two adjacent elements (e.g. \u0026lt;h1\u0026gt;\u0026lt;/h1\u0026gt;\u0026lt;h2\u0026gt;\u0026lt;/h2\u0026gt;) without a top level wrapping element (e.g.  \u0026lt;div\u0026gt;\u0026lt;h1\u0026gt;\u0026lt;/h1\u0026gt;\u0026lt;h2\u0026gt;\u0026lt;/h2\u0026gt;\u0026lt;/div\u0026gt;) and thinking of h1 and h2 as functions you quickly realize there is no nice way to return them as a function result in an immutable programming language without wrapping them with a third function.\nCompiling and Running in your browser\n\nNow we are ready to compile and run our application. In the terminal run elm make ChannelSwitcher.elm. This will generate an index.html for you, go ahead and open that in your browser.\n\nYou should be able to see the following interface:\n\n\nDefining the Model\n\nThe data that we need to flow through our component for it to generate the correct output should represent a list of channels.\n\nWe start by defining a record, and we will also define an initial model so we have something to work with.\n-- MODEL\ntype alias Model =\n  { channels: List String\n  , selectedChannel: Int\n  , query: String\n  }\n\ninitialModel : Model\ninitialModel =\n  { channels = [\"Elm\", \"React.js\", \"Ember\", \"Angular 2\", \"Om\", \"OffTopic\" ]\n  , selectedChannel = -1\n  , query = \"\"\n  }\n\nYou don't have to call your type Model it can be anything.\nDefining Actions and the Update function\n\nAs the user is interacting with the component, a new \"state\" of the model will be calculated. For example, selectedChannel will start at -1 and as we press the arrow keys up and down it will change to 0, 1, 2 etc.. Elm creates that new model using the update function. Our update function will take an action and return a new version of the model with a small modification.\n\nThis makes it very convenient since you can look at the action type definition (see below) and immediately know what kind of transformations can happen to the model in this component.\n-- UPDATE\ntype Action = NoOp | Filter String | Select Int\n\nupdate action model =\n  case action of\n    NoOp -\u0026gt;\n      model\n\n    Filter query -\u0026gt;\n      { model | query = query }\n\n    Select index -\u0026gt;\n      { model | selectedChannel = index }\n\nThe Action type we defined is a Union Type which allows us to perform Pattern Matching in the update function with the case statement.\n\nThe Filter String part is basically a Tagged Union where Filter is the action tag with a String argument. This helps us differentiate it from other actions that may have a one string argument.\nPutting it all together with StartApp\n\nIt's time to put it all together using StartApp.\nStartApp lets us declare which methods correspond with our model, update and view parts of our component.\n\nReplace the main function with the following:\nmain : Signal Html\nmain =\n  StartApp.start\n    { model = initialModel\n    , update = update\n    , view = view\n    }\n\nIf you try and compile the code so far you will get the following message:\n==================================== ERRORS ====================================\n\n-- TYPE MISMATCH ------------------------------------------- ChannelSwitcher.elm\n\nThe argument to function `start` is causing a mismatch.\n\n51│   StartApp.start\n52│\u0026gt;    { model = initialModel\n53│\u0026gt;    , update = update\n54│\u0026gt;    , view = view\n55│\u0026gt;    }\n\nFunction `start` is expecting the argument to be:\n\n    { ..., view : Signal.Address Action -\u0026gt; Model -\u0026gt; Html }\n\nBut it is:\n\n    { ..., view : VirtualDom.Node }\n\nDetected errors in 1 module.\n\nThat's because StartApp is passing a Mailbox address and the currently computed model to the view. Don't worry about understanding Mailboxes just yet, we will cover those in the second part of the tutorial. For now, to fix this error let's refactor our view function signature to the following, and add a type annotation while we are at it:\nview : Signal.Address Action -\u0026gt; Model -\u0026gt; Html\nview address model =\n\nNow you should be able to compile but as you notice when you open index.html the component still does not filter the list. Next we will render the model and implement the search/filter functionality.\nRendering the model\n\nLet's render the model now instead of static data. Under the VIEW section we will add a new method that renders the li elements using the channels list on the model.\n-- VIEW\nrenderChannel : String -\u0026gt; Html\nrenderChannel name =\n  li [ class \"collection-item\" ] [ text \u0026lt;| \"#\" ++ name ]\n\nrenderChannels : List String -\u0026gt; Html\nrenderChannels channels =\n  let\n    channelItems = List.map renderChannel channels\n  in\n    ul [ class \"collection\" ] channelItems\n\nview : Signal.Address Action -\u0026gt; Model -\u0026gt; Html\nview address model =\n  div [ class \"card-panel\" ] [\n    input [ ] [],\n    renderChannels model.channels\n  ]\n\nWe created two new methods renderChannel, which renders an individual li representing a channel with the hash symbol (#), and renderChannels which uses a List.map to return a list of li elements. We then pass that list as the second argument of ul. Lastly, we call renderChannels from the view function, passing in the model.channels.\n\nNote: If you are wondering about the \u0026lt;| operator: it is a reverse pipe and it means the result of everything on the right of that operator (until the closure) will be piped into the function to the left of the operator. It's just a way to avoid parens.\nFiltering the list\n\nWe are displaying the list rendered directly from the model, now it's time to filter the view according to the user input in the search box.\n\nFirst we need to store the filter the user types in on the model. For that we will add an onInput event on the input box, so we can filter the list as the user is typing.\nview : Signal.Address Action -\u0026gt; Model -\u0026gt; Html\nview address model =\n  div [ class \"card-panel\" ] [\n    input [ onInput address Filter ] [],\n    renderChannels model.channels\n  ]\n\nThe Filter action tag provides us with a free \"constructor\" that takes in a string, that string will be passed in to onInput from the browser as event.target.value or in elm-html targetValue.\n\nNote: onInput is not yet part of the elm-html package, which is why we imported the Html.Events.Extra package which comes from circuithub/elm-html-extra\n\nThen we will use the List.filter method to only display channels starting with the input text.\nfilterChannels : List String -\u0026gt; String -\u0026gt; List String\nfilterChannels channels query =\n  List.filter (String.contains query) channels\n\nThen use this function in the view function:\nview : Signal.Address Action -\u0026gt; Model -\u0026gt; Html\nview address model =\n  div [ class \"card-panel\" ] [\n    input [ onInput address Filter ] [],\n    renderChannels (filterChannels model.channels model.query)\n  ]\n\nTo make sure that the filter is case insensitive we will need to refactor the filterChannels and pass both the query and list item to String.toLower.\nfilterChannels : List String -\u0026gt; String -\u0026gt; List String\nfilterChannels channels query =\n  let\n    containsCaseInsensitive str1 str2 =\n      String.contains (String.toLower str1) (String.toLower str2)\n  in\n    List.filter (containsCaseInsensitive query) channels\nAdding style\n\nFor styling the component we will create a new HTML file and include the Materialize CSS library.\n\nThis is what your HTML should look like:\n\u0026lt;!DOCTYPE html\u0026gt;\n\u0026lt;html\u0026gt;\n  \u0026lt;head\u0026gt;\n    \u0026lt;link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/css/materialize.min.css\"\u0026gt;\n    \u0026lt;script src=\"channel_switcher.js\"\u0026gt;\u0026lt;/script\u0026gt;\n  \u0026lt;/head\u0026gt;\n  \u0026lt;body\u0026gt;\n    \u0026lt;div id=\"elm-goes-here\" class=\"container\"\u0026gt;\u0026lt;/div\u0026gt;\n    \u0026lt;script\u0026gt;\n      Elm.embed(\n          Elm.ChannelSwitcher,\n          document.getElementById('elm-goes-here')\n      );\n    \u0026lt;/script\u0026gt;\n  \u0026lt;/body\u0026gt;\n \u0026lt;/html\u0026gt;\n\nTo compile the Elm file into channel_switcher.js use the --output flag:\nelm make ChannelSwitcher.elm --output channel_switcher.js\n\nWhen you open the HTML file you should see something like this:\n\n\n\nAnd here is our Elm code so far:\nmodule ChannelSwitcher where\n\n-- IMPORTS\nimport Html exposing (..)\nimport Html.Attributes exposing (..)\nimport Html.Events exposing (..)\nimport Html.Events.Extra exposing (..)\nimport String\nimport StartApp.Simple as StartApp\n\n-- MODEL\ntype alias Model =\n  { channels: List String\n  , selectedChannel: Int\n  , query: String\n  }\n\ninitialModel : Model\ninitialModel =\n  { channels = [\"Elm\", \"React.js\", \"Ember\", \"Angular 2\", \"Om\", \"OffTopic\" ]\n  , selectedChannel = -1\n  , query = \"\"\n  }\n\n-- UPDATE\ntype Action = NoOp | Filter String | Select Int\n\nupdate action model =\n  case action of\n    NoOp -\u0026gt;\n      model\n\n    Filter query -\u0026gt;\n      { model | query = query }\n\n    Select index -\u0026gt;\n      { model | selectedChannel = index }\n\n\n-- VIEW\nfilterChannels : List String -\u0026gt; String -\u0026gt; List String\nfilterChannels channels query =\n  let\n    containsCaseInsensitive str1 str2 =\n      String.contains (String.toLower str1) (String.toLower str2)\n  in\n    List.filter (containsCaseInsensitive query) channels\n\nrenderChannel : String -\u0026gt; Html\nrenderChannel name =\n  li [ class \"collection-item\" ] [ text \u0026lt;| \"#\" ++ name ]\n\nrenderChannels : List String -\u0026gt; Html\nrenderChannels channels =\n  let\n    channelItems = List.map renderChannel channels\n  in\n    ul [ class \"collection\" ] channelItems\n\nview : Signal.Address Action -\u0026gt; Model -\u0026gt; Html\nview address model =\n  div [ class \"card-panel\" ] [\n    input [ onInput address Filter ] [],\n    renderChannels (filterChannels model.channels model.query)\n  ]\n\nmain : Signal Html\nmain =\n  StartApp.start\n    { model = initialModel\n    , update = update\n    , view = view\n    }\nWhat's Next?\n\nIn the next post I will build a version of this component utilizing Messages, Effects and Ports. This will allow us to add keyboard interaction and JavaScript interop.\n","summary":"I've been experimenting with Elm for the past few months and have come to really appreciate its style of programming. It is very similar to React in the sense that you can render modular components based on DOM events, but the functional style and syntactic sugar are a pleasure to work with. In this blog post I will guide you in building your first Slack inspired component.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/280/elm-post-header.jpg","date_published":"2016-01-18T09:00:00-05:00","data_modified":"2025-10-10T15:07:02-04:00","author":{"name":"Dorian Karter","url":"https://hashrocket.com/team/dorian-karter","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/91/dorian-karter.jpg"},"tags":["Elm","Javascript"]},{"id":"https://hashrocket.com/blog/posts/test-driving-a-stubbed-api-in-ember-with-ember-cli-mirage","url":"https://hashrocket.com/blog/posts/test-driving-a-stubbed-api-in-ember-with-ember-cli-mirage","title":"Test Driving a Stubbed API in Ember with Ember-CLI-Mirage","content_html":"When developing a client side javascript app, you won’t always have an API available before you start. Even when you do, you probably don’t want to have your tests reliant on the the API end-points.\n\nLuckily, there is a great solution to stubbing out an API while building your Ember app; [Ember CLI Mirage](http://www.ember-cli-mirage.com/). Mirage works great when Ember Data is expecting a REST API, but there's some manual conversion that must be done if you want to consume JSON API [1](http://jsonapi.org/), which I ran into recently on a project.\r\n\r\nIn this tutorial we will leverage QUnit and Mirage's factories and API DSL to craft explicit acceptance tests as we build our application.\r\n\r\nI’m going to assume you have some basic knowledge of Ember for this.\r\n\r\n\u003chr/\u003e\r\n\r\n### Setup\r\n```bash\r\n$ ember new mirage-tutorial\r\n$ cd mirage-tutorial\r\n```\r\n\r\nVim users who use [Vim Projectionist](https://github.com/tpope/vim-projectionist) can curl a set of projections from my Github repo.\r\n\r\n```bash\r\n$ curl -G https://raw.githubusercontent.com/jsncmgs1/ember-vim-projections/\r\nmaster/.projections.json -o .projections.json\r\n```\r\n\r\nWe will use Ember/Ember Data 2.1.0 for this app, so let's update.\r\n\r\nIn your bower.json file:\r\n\r\nchange:\r\n\"ember\": \"{your version}\" to \"ember\": \"2.1.0\"\r\n\r\nand\r\n\r\n\"ember-data\": \"{your version}\" to \"ember-data\": \"2.1.0\"\r\n\r\nThen `nombom` with:\r\n\r\n```bash\r\n$ npm cache clear \u0026\u0026 bower cache clean \u0026\u0026 rm -rf node_modules bower_components \u0026\u0026 npm install \u0026\u0026 bower install\r\n```\r\n\r\nEmber and Ember Data should be updated.  To check, start your ember server and go to localhost:/4200 and you’ll see the “Welcome to Ember” page. Pull up the Ember\r\ninspector, and click the left sub-nav “Info” button. Ember and Ember-data should both be at 2.1.0.\r\n\r\nWe will use the JSONAPI adapter, generate your adapter:\r\n\r\n```bash\r\n$ ember g adapter application\r\n```\r\nIn the adapter file, change RESTAdapter to JSONAPIAdapter.\r\n\r\nNow install mirage, then restart your server.\r\n\r\n```bash\r\n$ ember install ember-cli-mirage\r\n```\r\n\r\nMirage will create a mirage directory under app/. It contains a `config.js` file, a factories directory, and a scenarios directory.\r\n\r\n**Config file**: Mirage wraps Pretender, which intercepts requests that would normally hit your API, and allows you to specify the response that should be sent back.  This file is where you specify your API end-points.\r\n\r\nMirage gives you shorthand syntax for simple routes, but you can create manual routes when shorthand won’t work. [Mirage docs](http://www.ember-cli-mirage.com/docs/v0.1.x/defining-routes/) have a\r\nshort and clear description of how to handle your routes.\r\n\r\n**Scenarios**: Mirage creates a `default.js` scenario for you.  Inside the scenario you declare all the data you want to seed your development environment with.  This data will not be in the test environment.\r\n\r\n**Factories**: Your mirage scenario will use the factories you define to generate your data, and you should use them in your tests as well.\r\n\r\nWe will create a simple app that will list our cars and let us create new ones.  Our cars also contain parts, which can also be created. While the API team builds their their end, we’ll get started on our end.\r\n\r\n\u003chr/\u003e\r\n### Listing our cars\r\nLet's create a cars acceptance test.\r\n\r\n```bash\r\n$ ember g acceptance-test cars\r\n```\r\n\r\nEmber generates a test for us at `tests/acceptance/cars-test.js`, with a generated test which checks to make sure our route functions. Let's change it to test a link to the cars index on the application template.  When writing QUnit, you'll simulate all your user navigations ('click', 'visit', etc), which run asynchronously. Assertions are called in the `andThen()` callback, which will run after all the async operations are complete.\r\n[2](http://coryforsyth.com/2014/07/10/demystifing-ember-async-testing/)\r\n\r\n```javascript\r\n//app/tests/acceptance/cars.js\r\ntest('visiting /cars', function(assert) {\r\n  visit('/');\r\n\r\n  click('#all-cars');\r\n\r\n  andThen(() =\u003e {\r\n    assert.equal(currentURL(), '/cars');\r\n  });\r\n});\r\n```\r\n\r\nOur tests run at localhost:4200/tests. When you go to that page, in the Module drop down in the upper right and corner, choose 'Acceptance | cars'. We will get an error because we don’t have the `#all-cars` link.\r\n\r\nLets make our test pass. First, we need to create the link.\r\n\r\n```html\r\n\u003c!-- app/templates/application.hbs --\u003e\r\n\u003ch2 id=\"title\"\u003eWelcome to Ember\u003c/h2\u003e\r\n{{link-to 'Cars' 'cars.index'}}\r\n\r\n{{outlet}}\r\n```\r\n\r\nNow QUnit tells us there's no `cars.index` route.\r\n\r\n```bash\r\n$ ember g route cars\r\n```\r\n\r\nEmber will add the route for you in the `router.js` file.  It adds the empty object, but we also need to pass an empty function so that an `cars/index` route is generated. Unfortunately, `this.route('cars', {})` would not create it.\r\n\r\n```javascript\r\n//router.js\r\nRouter.map(function() {\r\n  this.route('cars', {}, function(){});\r\n});\r\n```\r\n\r\nNow check your test page, it passes.\r\n\r\nLets test that when we go to the cars page, we will actually see some cars. At the bottom of your cars acceptance test:\r\n\r\n```javascript\r\n//tests/acceptance/cars.js\r\n\r\ntest('I see all cars on the index page', (assert) =\u003e {\r\n  server.create('car');\r\n  visit('/cars');\r\n\r\n  andThen(() =\u003e {\r\n    const cars = find('li.car');\r\n    assert.equal(cars.length, 1);\r\n  });\r\n});\r\n\r\n```\r\n\r\n`server.create('car')` is telling Mirage to find a factory named 'car', create 1 of those cars, and put them in the Mirage database. When you run the test, it will die due to a Mirage error.  I recommend running\r\nyour tests with the Chrome debugger open so you can see the errors.\r\n\r\nMirage will log an error saying it tried to find a ‘car’ factory, and it was not defined.  Lets make one at `app/mirage/factories/car.js`.\r\n\r\n```javascript\r\n// /app/mirage/factories/car.js\r\nimport Mirage from 'ember-cli-mirage';\r\n\r\nexport default Mirage.Factory.extend({\r\n  name(i) { return `Car ${i + 1}`;}\r\n});\r\n```\r\nThis will create a car with a name attribute. This `(i)` syntax is used for Mirage sequences, the first name will be \"Car 1\", then \"Car 2\", etc.\r\n\r\nIf we check our tests again, it will fail, finding 0 cars when expecting 1. To get the cars on the page, our `car/index` route will need to load the car model.\r\n\r\nLet’s create our car model. The Ember CLI generators are fantastic, but they will generate some tests that are not in the scope of this tutorial (unit tests).  You can remove them, or ignore them for now.  However, I wouldn't recommend leaving unused tests around.\r\n\r\n```bash\r\n$ ember g model car\r\n```\r\n\r\n```javascript\r\n// /app/models/car.js\r\nimport DS from 'ember-data';\r\n\r\nexport default DS.Model.extend({\r\n  name: DS.attr('string')\r\n});\r\n```\r\n\r\nAnd our route/template:\r\n\r\n```bash\r\n$ ember g route cars/index\r\n```\r\n\r\n```javascript\r\n// /app/routes/cars/index.js\r\nimport Ember from 'ember';\r\n\r\nexport default Ember.Route.extend({\r\n  model(){\r\n    return this.store.findAll('car');\r\n  }\r\n});\r\n```\r\n\r\n```html\r\n\u003c!--app/templates/cars/index.hbs--\u003e\r\n\r\n\u003cul class='cars'\u003e\r\n  {{#each model as |car|}}\r\n    \u003cli class='car'\u003e\r\n      {{car.name}}\r\n    \u003c/li\u003e\r\n  {{/each}}\r\n\u003c/ul\u003e\r\n\r\n```\r\n\r\nWhen we hit the model hook in our route, Ember Data sends out a `GET` request to `/cars`. If you let the test run, the test will seem frozen without the chrome debugger open.  Mirage will log an error to the console saying there's no end point for `GET /cars`.\r\n\r\nLet’s create a route for Mirage so it can intercept this request.  For the tutorial we will use the longer syntax, because Mirage doesn’t handle JSON API in the shorthand syntax - yet. When the json-api-serializer branch of Mirage gets merged (which should be soon), Mirage will be able to take care of a lot of the payload transforming itself.\r\n\r\nJSON API expects a response with a top level key named 'data', which contains an array of the resources returned.  Each resource should have a specified type,\r\nthe id of the resource, and the resource attributes. When Mirage responds to a request, it will log the response object in the console for inspection. The object should look like this:\r\n\r\n```javascript\r\n  data: {\r\n    [\r\n      {\r\n        attributes: {\r\n          id: 1,\r\n          name: 'Car 1'\r\n        },\r\n        id: 1,\r\n        type: 'cars'\r\n      },\r\n      {\r\n        attributes: {\r\n          id: 2,\r\n          name: 'Car 2'\r\n        },\r\n        id: 2,\r\n        type: 'cars'\r\n      },\r\n      //....\r\n    ]\r\n  }\r\n\r\n```\r\n\r\nThere are other keys as well, such as errors, and relationships. We will expand on relations further in the tutorial.\r\n\r\n```javascript\r\n// /app/mirage/config.js\r\n\r\nexport default function() {\r\n  this.get('/cars', (db, request) =\u003e {\r\n    let data = {};\r\n    data = db.cars.map((attrs) =\u003e {\r\n      let rec = {type: 'cars', id: attrs.id, attributes: attrs};\r\n      return rec;\r\n    });\r\n\r\n    return { data };\r\n  });\r\n};\r\n```\r\n\r\nWhen we run our tests again, they pass.  If you’d like to see it work in development, generate some cars in `scenarios/default.js`, and go to `localhost:4200/cars`.\r\n\r\n```javascript\r\n// /app/mirage/scenarios/default.js\r\nexport default function(server) {\r\n\r\n    // Seed your development database using your factories. This data will not be loaded in your tests.\r\n    server.createList('car', 10);\r\n}\r\n```\r\n\r\nWhats going on here?\r\n\r\nWhen we visit the cars route, ember sends us to the cars/index route. The route fires the model hook, where ember data sends out a `GET` request for all of the cars.  The mirage route in `mirage/config.js` intercepts the request, gets the cars that we generated in the test, adds them to a JSON API formatted object, and sends it back as the response.  No api needed!\r\n\r\nNow that we have a working acceptance test, lets create a car component for our cars to live in.\r\n\r\n```bash\r\n$ ember g component a-car\r\n```\r\n\r\nEmber created a component integration test, which we'll use. It's easy to setup Mirage for an integration tests.  Under `tests/helpers/`, create a file called `mirage-integration.js`\r\n\r\n```javascript\r\n//tests/helpers/mirage-integration.js\r\nimport mirageInitializer from '../../initializers/ember-cli-mirage';\r\n\r\nexport default function setupMirage(container) {\r\n  mirageInitializer.initialize(container);\r\n}\r\n```\r\n\r\nand in your component test, import the setupMirage function, you will invoke in the moduleForComponent setup hook, passing in this.container.\r\n\r\n```javascript\r\n//app/tests/integration/components/a-car-test.js\r\n\r\nimport { moduleForComponent, test } from 'ember-qunit';\r\nimport setupMirage from '../../helpers/mirage-integration';\r\nimport hbs from 'htmlbars-inline-precompile';\r\n\r\nmoduleForComponent('a-car', 'Integration | Component | a car', {\r\n  integration: true,\r\n  setup() {\r\n    setupMirage(this.container);\r\n  }\r\n});\r\n\r\ntest('it renders', function(assert) {\r\n  const car = server.create('car');\r\n  this.set('car', car);\r\n  this.render(hbs`{{a-car car=car}}`);\r\n\r\n  assert.equal(this.$().text().trim(), 'Car 1');\r\n});\r\n```\r\n\r\nIn this test, we create a car, and a component (`this`) and set it on the component. Then we can actually render the template, and assert what the components text should be. Of course we haven't done anything with our component\r\nyet, so the test fails.\r\n\r\nIn our `cars/index` template, we're rendering our component inside of an li, with a class of 'car'.  Add those attributes to the component.\r\n\r\n```javascript\r\nimport Ember from 'ember';\r\n\r\nexport default Ember.Component.extend({\r\n  tagName: 'li',\r\n  classNames: ['car']\r\n});\r\n```\r\n\r\nMove the `{{car.name}}` expression into the component template, and render the component in the each loop, passing the model into the component.\r\n\r\n```html\r\n\u003c!-- templates/components/a-car.hbs --\u003e\r\n{{car.name}}\r\n```\r\n\r\n```html\r\n\u003c!-- templates/cars/index.hbs --\u003e\r\nCars/Index\r\n\r\n\u003cul class='cars'\u003e\r\n  {{#each model as |car|}}\r\n    {{a-car car=car}}\r\n  {{/each}}\r\n\u003c/ul\u003e\r\n```\r\n\r\nRun the tests, they should pass.\r\n\r\n\u003chr/\u003e\r\n### Adding New Cars\r\nNow that our cars index is tested and working, we need to be able to add more cars to our collection. Let's make a test.\r\n\r\n```javascript\r\n//tests/acceptance/cars-test.js\r\ntest('I can add a new car', function(assert){\r\n  server.createList('car', 10); visit('/cars');\r\n\r\n  click('#add-car'); fillIn('input[name=\"car-name\"]', 'My new car');\r\n  click('button');\r\n\r\n  andThen(() =\u003e {\r\n    const newCar = find('li.car:contains(\"My new car\")');\r\n    assert.equal(newCar.text().trim(), \"My new car\");\r\n  });\r\n});\r\n```\r\n\r\nOur test fails because there's no link with an id of add-car. This link should take us to the cars.new route. In your cars/index template at the bottom of the file, add:\r\n\r\n```html\r\n\u003c!-- app/templates/cars/index.hbs --\u003e\r\n\u003c!-- ... --\u003e\r\n\r\n{{#link-to 'cars.new' id='add-car'}}\r\n  Add new car\r\n{{/link-to}}\r\n```\r\n\r\nNow our test fails because we don't have the specified input field. We'll need the cars/new template, we also know that we will need that route. Generating the route will create both for us, as well as adding the route to our router.\r\n\r\n```bash\r\nember g route cars/new\r\n```\r\n\r\nThe router should now look like:\r\n\r\n```javascript\r\n//router.js\r\nimport Ember from 'ember';\r\nimport config from './config/environment';\r\n\r\nvar Router = Ember.Router.extend({\r\n  location: config.locationType\r\n});\r\n\r\n\r\nRouter.map(function() {\r\n  this.route('cars', function() {\r\n    this.route('new', {});\r\n  });\r\n});\r\n\r\nexport default Router;\r\n```\r\n\r\nAdd the form for creating a car to our cars/new template:\r\n\r\n```html\r\n\u003c!--app/templates/cars/new.hbs--\u003e\r\nNew Car\r\n\r\n\u003cform {{action 'createCar' name on='submit'}}\u003e\r\n  {{input name='car-name' value=name}}\r\n  \u003cbutton\u003e Create Car \u003c/button\u003e\r\n\u003c/form\u003e\r\n```\r\n\r\nWe know we'll need an action to handle the creation of the car, so we'll go ahead and declare that now.  Our test will fail because there's nothing to handle the action named createCar yet. My preference is to handle anything related to data in the route when I can, so we'll do that.\r\n\r\n```javascript\r\n// /app/routes/cars/new.hbs\r\nimport Ember from 'ember';\r\nexport default Ember.Route.extend({\r\n  actions: {\r\n     createCar(name){\r\n      const car = this.store.createRecord('car', { name });\r\n\r\n      car.save()\r\n        .then(() =\u003e {\r\n          this.transitionTo('cars');\r\n        }).catch(() =\u003e {\r\n          // something that handles failures\r\n        });\r\n     }\r\n   }\r\n\r\n});\r\n```\r\n\r\nNow our Ember pieces are hooked up, but the test fails because mirage doesn't see a route that specifies a `POST` request to `/cars`. Add it to the Mirage config file.\r\n\r\n```javascript\r\n// /app/mirage/config.js\r\n\r\nexport default function() {\r\n  //...\r\n\r\n  this.post('/cars', (db, request) =\u003e {\r\n    return JSON.parse(request.requestBody);\r\n  });\r\n};\r\n```\r\n\r\nOur JSONAPIAdapter sends the serialized data in the correct format, so all we have to do is parse it, and return it.\r\n\r\nAnd with that our test should pass.\r\n\r\n\u003chr/\u003e\r\n### Viewing Parts\r\n\r\nI mentioned earlier that our cars contain parts. We'll make it so that when we click our car, we will be taken to that that car's parts page. Let's generate a test for parts.\r\n\r\n```bash\r\n$ ember g acceptance-test parts\r\n```\r\n\r\nDelete the generated test and add the following.\r\n\r\n```javascript\r\n//tests/acceptance/parts.js\r\n\r\ntest('when I click a car, I see its parts', (assert) =\u003e {\r\n  const car = server.create('car');\r\n  const parts = server.createList('part', 4, { car_id: car.id });\r\n  visit('/cars');\r\n  click('.car-link');\r\n\r\n  andThen(() =\u003e {\r\n    assert.equal(currentURL(), `/car/${car.id}/parts`);\r\n    assert.equal(find('.part').length, parts.length);\r\n  });\r\n});\r\n```\r\n\r\nOur first breakage occurs because Mirage has no part factory.\r\n\r\n```javascript\r\n//mirage/factories/part.js\r\n\r\nimport Mirage from 'ember-cli-mirage';\r\n\r\nexport default Mirage.Factory.extend({\r\n  name(i) { return `Part ${i}`; }\r\n});\r\n```\r\n\r\nNow QUnit yells because we have no links. Turn our list of cars into links, so that when we click on one, we can see that car's parts.\r\n\r\n```html\r\n\u003c!-- templates/components/a-car.hbs --\u003e\r\n{{#link-to 'car.parts' car class='car-link'}}\r\n  {{car.name}}\r\n{{/link-to}}\r\n```\r\n\r\nQUnit shames us for not having a car.parts route.\r\n\r\n```bash\r\n$ ember g route car/parts\r\n```\r\n\r\nThe router should look like:\r\n\r\n```javascript\r\n//router.js\r\nimport Ember from 'ember';\r\nimport config from './config/environment';\r\n\r\nvar Router = Ember.Router.extend({\r\n  location: config.locationType\r\n});\r\n\r\nRouter.map(function() {\r\n  this.route('cars', function() {\r\n    this.route('new', {});\r\n  });\r\n\r\n  this.route('car', function(){\r\n    this.route('parts', {});\r\n  });\r\n});\r\n\r\nexport default Router;\r\n```\r\n\r\nWe'll add a dynamic segment of id to the car path.\r\n\r\n```javascript\r\n//...\r\n  this.route('car', { path: '/car/:id'}, function(){\r\n    this.route('parts');\r\n  });\r\n//...\r\n});\r\n\r\nexport default Router;\r\n```\r\n\r\nSince our route is nested, we need to specify the model for the parent route.\r\n\r\n```bash\r\n$ ember g route car\r\n\r\n```\r\n\r\nIn the car route, return the car specified by the id dynamic segment.\r\n\r\n```javascript\r\n//routes/car.js\r\nimport Ember from 'ember';\r\n\r\nexport default Ember.Route.extend({\r\n  model(params){\r\n    return this.store.find('car', params.id);\r\n  }\r\n});\r\n```\r\n\r\nWe also have to create a Mirage route to `GET` a single car. At this point in the app, we have had our cars loaded from visiting the index, but a user could go straight to a `car/:id` url, so we need to handle that.\r\nJSON API requires relationship information to be stored in a 'relationships' object. Add it to your mirage config file.\r\n\r\n```javascript\r\n//mirage/config.js\r\nexport default function() {\r\n  //...\r\n\r\n  this.get('/cars/:id', (db, request) =\u003e {\r\n    let car = db.cars.find(request.params.id);\r\n    let parts = db.parts.where({car_id: car.id});\r\n\r\n    let data = {\r\n      type: 'car',\r\n      id: request.params.id,\r\n      attributes: car,\r\n      relationships: {\r\n        parts:{\r\n          data:{}\r\n        }\r\n      }\r\n    }\r\n\r\n    data.relationships.parts.data = parts.map((attrs) =\u003e {\r\n      return { type: 'parts', id: attrs.id, attributes: attrs };\r\n    });\r\n\r\n    return { data };\r\n  });\r\n\r\n}\r\n\r\n```\r\n\r\nAdditionally, in our Mirage `/cars` route, we are only\r\nreturning the car information, not the associated parts.  What this means is, if the first page we visit is the `/cars` page, those cars will already be loaded in the store (with no knowledge of any associated parts).\r\nWhen we go to the cars/part page, the store won't fetch the model, because it's already in the store, so there will be no parts available to render.  We should load a cars parts in the `cars/index` route.\r\n\r\n```javascript\r\n//mirage/config.js\r\nexport default function() {\r\n  this.get('/cars', (db, request) =\u003e {\r\n    let data = {};\r\n    data = db.cars.map((attrs) =\u003e {\r\n\r\n      let car = {\r\n        type: 'cars',\r\n        id: attrs.id,\r\n        attributes: attrs ,\r\n        relationships: {\r\n          parts: {\r\n            data: {}\r\n          }\r\n        },\r\n      };\r\n\r\n      data.relationships.parts.data = db.parts\r\n        .where({car_id: attrs.id})\r\n        .map((attrs) =\u003e {\r\n          return {\r\n            type: 'parts',\r\n            id: attrs.id,\r\n            attributes: attrs\r\n          };\r\n        });\r\n\r\n      return car;\r\n\r\n    });\r\n    return { data };\r\n  });\r\n//....\r\n```\r\n\r\nWe also need the Mirage end-points for getting a part.\r\n\r\n```javascript\r\n//mirage/config.js\r\nexport default function() {\r\n//...\r\n  this.get('parts/:id', (db, request) =\u003e {\r\n    let part = db.parts.find(request.params.id);\r\n\r\n    let data = {\r\n      type: 'parts',\r\n      id: request.params.id,\r\n      attributes: part,\r\n    };\r\n\r\n    return { data };\r\n  });\r\n//...\r\n```\r\n\r\nNow we need a part model, and a factory.\r\n\r\n```javascript\r\n//models/part.js\r\nimport DS from 'ember-data';\r\n\r\nexport default DS.Model.extend({\r\n  name: DS.attr('string'),\r\n  car: DS.belongsTo('car')\r\n});\r\n```\r\n\r\n```javascript\r\nimport Mirage from 'ember-cli-mirage';\r\n\r\nexport default Mirage.Factory.extend({\r\n  name(i) { return `Part ${i}`; }\r\n});\r\n```\r\n\r\nAnd update our car model to show the association.\r\n\r\n```javascript\r\n//models/car.js\r\nimport DS from 'ember-data';\r\n\r\nexport default DS.Model.extend({\r\n  name: DS.attr('string'),\r\n  parts: DS.hasMany('part')\r\n});\r\n```\r\n\r\nAnd our template:\r\n\r\n```html\r\n\r\n\u003c!-- car/parts.hbs --\u003e\r\nParts\r\n\u003cul\u003e\r\n  {{model.name}}\r\n  {{#each model.parts as |part|}}\r\n    \u003cli class='part'\u003e\r\n      {{part.name}}\r\n    \u003c/li\u003e\r\n  {{/each}}\r\n\u003c/ul\u003e\r\n```\r\nAnd now our test should be green.\r\n\r\nI'll leave converting the part into a component with an integration test as an exercise for you to complete. The steps are the same as they were for cars.\r\n\r\n\u003chr/\u003e\r\n### Adding Parts\r\nOur last test will cover adding parts. At the bottom of your parts acceptance test:\r\n\r\n```javascript\r\n//tests/acceptance/parts.js\r\n\r\ntest('I can add a new part to a car', (assert) =\u003e {\r\n  server.create('car');\r\n  visit('/cars');\r\n  click('.car-link');\r\n  click('.new-part');\r\n\r\n  fillIn('input[name=\"part-name\"]', \"My new part\");\r\n  click('button');\r\n  andThen(() =\u003e {\r\n    assert.equal(find('.part').text().trim(), \"My new part\");\r\n  });\r\n});\r\n```\r\n\r\nOur test tells us we don't have a '.new-part' link. in our template:\r\n\r\n```html\r\nParts\r\n\u003c!-- car/parts.hbs --\u003e\r\n\u003cul\u003e\r\n  {{model.name}}\r\n  {{#each model.parts as |part|}}\r\n    \u003cli class='part'\u003e\r\n      {{part.name}}\r\n    \u003c/li\u003e\r\n  {{/each}}\r\n\u003c/ul\u003e\r\n\r\n{{#link-to 'car.new-part' model class='new-part'}}\r\n  Add new Part\r\n{{/link-to}}\r\n```\r\n\r\n```bash\r\n$ ember g route car/new-part\r\n```\r\nNow we need a 'car.new-part' route.\r\n\r\n```javascript\r\n//router.js\r\nRouter.map(function() {\r\n  this.route('cars', function() {\r\n    this.route('new', {});\r\n  });\r\n\r\n  this.route('car', { path: '/car/:id' }, function(){\r\n    this.route('parts', {});\r\n    this.route('new-part', {});\r\n  });\r\n});\r\n```\r\n\r\nAnd a template for our route to render.\r\n\r\n```html\r\n\u003c!--templates/car/new-part--\u003e\r\nNew Part\r\n\r\n\u003cform {{action 'newPart' name on='submit'}}\u003e\r\n  {{input name='part-name' value=name}}\r\n  \u003cbutton\u003e Create Part \u003c/button\u003e\r\n\u003c/form\u003e\r\n```\r\n\r\n```javascript\r\n//routes/car/new-part.js\r\nimport Ember from 'ember';\r\n\r\nexport default Ember.Route.extend({\r\n  actions: {\r\n    newPart(name){\r\n      const car = this.modelFor('car');\r\n      const part = this.store.createRecord('part', { name, car });\r\n      part.save().then(() =\u003e {\r\n        this.transitionTo('car.parts', car);\r\n      });\r\n    }\r\n  }\r\n});\r\n```\r\n\r\nAnd a Mirage endpoint:\r\n\r\n```javascript\r\nthis.post('parts', (db, request) =\u003e {\r\n  return JSON.parse(request.requestBody);\r\n});\r\n```\r\n\r\nAnd we're done! This process will be even easier once Mirage supports JSON API, which is on its way.  You can view the source at https://github.com/jsncmgs1/mirage-tutorial.git.\r\n\u003chr/\u003e\r\n\r\n1. [Demystifying Ember Async Testing](http://coryforsyth.com/2012/07/10/demystifing-ember-async-testing/)\r\n\r\n2. [JSON API](http://jsonapi.org/)\r\n\r\n3. [Ember CLI Mirage](http://www.ember-cli-mirage.com/)","content_text":"When developing a client side javascript app, you won’t always have an API available before you start. Even when you do, you probably don’t want to have your tests reliant on the the API end-points.\n\nLuckily, there is a great solution to stubbing out an API while building your Ember app; Ember CLI Mirage. Mirage works great when Ember Data is expecting a REST API, but there's some manual conversion that must be done if you want to consume JSON API 1, which I ran into recently on a project.\n\nIn this tutorial we will leverage QUnit and Mirage's factories and API DSL to craft explicit acceptance tests as we build our application.\n\nI’m going to assume you have some basic knowledge of Ember for this.\n\n\nSetup\n$ ember new mirage-tutorial\n$ cd mirage-tutorial\n\nVim users who use Vim Projectionist can curl a set of projections from my Github repo.\n$ curl -G https://raw.githubusercontent.com/jsncmgs1/ember-vim-projections/\nmaster/.projections.json -o .projections.json\n\nWe will use Ember/Ember Data 2.1.0 for this app, so let's update.\n\nIn your bower.json file:\n\nchange:\n\"ember\": \"{your version}\" to \"ember\": \"2.1.0\"\n\nand\n\n\"ember-data\": \"{your version}\" to \"ember-data\": \"2.1.0\"\n\nThen nombom with:\n$ npm cache clear \u0026amp;\u0026amp; bower cache clean \u0026amp;\u0026amp; rm -rf node_modules bower_components \u0026amp;\u0026amp; npm install \u0026amp;\u0026amp; bower install\n\nEmber and Ember Data should be updated.  To check, start your ember server and go to localhost:/4200 and you’ll see the “Welcome to Ember” page. Pull up the Ember\ninspector, and click the left sub-nav “Info” button. Ember and Ember-data should both be at 2.1.0.\n\nWe will use the JSONAPI adapter, generate your adapter:\n$ ember g adapter application\n\nIn the adapter file, change RESTAdapter to JSONAPIAdapter.\n\nNow install mirage, then restart your server.\n$ ember install ember-cli-mirage\n\nMirage will create a mirage directory under app/. It contains a config.js file, a factories directory, and a scenarios directory.\n\nConfig file: Mirage wraps Pretender, which intercepts requests that would normally hit your API, and allows you to specify the response that should be sent back.  This file is where you specify your API end-points.\n\nMirage gives you shorthand syntax for simple routes, but you can create manual routes when shorthand won’t work. Mirage docs have a\nshort and clear description of how to handle your routes.\n\nScenarios: Mirage creates a default.js scenario for you.  Inside the scenario you declare all the data you want to seed your development environment with.  This data will not be in the test environment.\n\nFactories: Your mirage scenario will use the factories you define to generate your data, and you should use them in your tests as well.\n\nWe will create a simple app that will list our cars and let us create new ones.  Our cars also contain parts, which can also be created. While the API team builds their their end, we’ll get started on our end.\n\n\nListing our cars\n\nLet's create a cars acceptance test.\n$ ember g acceptance-test cars\n\nEmber generates a test for us at tests/acceptance/cars-test.js, with a generated test which checks to make sure our route functions. Let's change it to test a link to the cars index on the application template.  When writing QUnit, you'll simulate all your user navigations ('click', 'visit', etc), which run asynchronously. Assertions are called in the andThen() callback, which will run after all the async operations are complete.\n2\n//app/tests/acceptance/cars.js\ntest('visiting /cars', function(assert) {\n  visit('/');\n\n  click('#all-cars');\n\n  andThen(() =\u0026gt; {\n    assert.equal(currentURL(), '/cars');\n  });\n});\n\nOur tests run at localhost:4200/tests. When you go to that page, in the Module drop down in the upper right and corner, choose 'Acceptance | cars'. We will get an error because we don’t have the #all-cars link.\n\nLets make our test pass. First, we need to create the link.\n\u0026lt;!-- app/templates/application.hbs --\u0026gt;\n\u0026lt;h2 id=\"title\"\u0026gt;Welcome to Ember\u0026lt;/h2\u0026gt;\n{{link-to 'Cars' 'cars.index'}}\n\n{{outlet}}\n\nNow QUnit tells us there's no cars.index route.\n$ ember g route cars\n\nEmber will add the route for you in the router.js file.  It adds the empty object, but we also need to pass an empty function so that an cars/index route is generated. Unfortunately, this.route('cars', {}) would not create it.\n//router.js\nRouter.map(function() {\n  this.route('cars', {}, function(){});\n});\n\nNow check your test page, it passes.\n\nLets test that when we go to the cars page, we will actually see some cars. At the bottom of your cars acceptance test:\n//tests/acceptance/cars.js\n\ntest('I see all cars on the index page', (assert) =\u0026gt; {\n  server.create('car');\n  visit('/cars');\n\n  andThen(() =\u0026gt; {\n    const cars = find('li.car');\n    assert.equal(cars.length, 1);\n  });\n});\n\n\nserver.create('car') is telling Mirage to find a factory named 'car', create 1 of those cars, and put them in the Mirage database. When you run the test, it will die due to a Mirage error.  I recommend running\nyour tests with the Chrome debugger open so you can see the errors.\n\nMirage will log an error saying it tried to find a ‘car’ factory, and it was not defined.  Lets make one at app/mirage/factories/car.js.\n// /app/mirage/factories/car.js\nimport Mirage from 'ember-cli-mirage';\n\nexport default Mirage.Factory.extend({\n  name(i) { return `Car ${i + 1}`;}\n});\n\nThis will create a car with a name attribute. This (i) syntax is used for Mirage sequences, the first name will be \"Car 1\", then \"Car 2\", etc.\n\nIf we check our tests again, it will fail, finding 0 cars when expecting 1. To get the cars on the page, our car/index route will need to load the car model.\n\nLet’s create our car model. The Ember CLI generators are fantastic, but they will generate some tests that are not in the scope of this tutorial (unit tests).  You can remove them, or ignore them for now.  However, I wouldn't recommend leaving unused tests around.\n$ ember g model car\n// /app/models/car.js\nimport DS from 'ember-data';\n\nexport default DS.Model.extend({\n  name: DS.attr('string')\n});\n\nAnd our route/template:\n$ ember g route cars/index\n// /app/routes/cars/index.js\nimport Ember from 'ember';\n\nexport default Ember.Route.extend({\n  model(){\n    return this.store.findAll('car');\n  }\n});\n\u0026lt;!--app/templates/cars/index.hbs--\u0026gt;\n\n\u0026lt;ul class='cars'\u0026gt;\n  {{#each model as |car|}}\n    \u0026lt;li class='car'\u0026gt;\n      {{car.name}}\n    \u0026lt;/li\u0026gt;\n  {{/each}}\n\u0026lt;/ul\u0026gt;\n\n\nWhen we hit the model hook in our route, Ember Data sends out a GET request to /cars. If you let the test run, the test will seem frozen without the chrome debugger open.  Mirage will log an error to the console saying there's no end point for GET /cars.\n\nLet’s create a route for Mirage so it can intercept this request.  For the tutorial we will use the longer syntax, because Mirage doesn’t handle JSON API in the shorthand syntax - yet. When the json-api-serializer branch of Mirage gets merged (which should be soon), Mirage will be able to take care of a lot of the payload transforming itself.\n\nJSON API expects a response with a top level key named 'data', which contains an array of the resources returned.  Each resource should have a specified type,\nthe id of the resource, and the resource attributes. When Mirage responds to a request, it will log the response object in the console for inspection. The object should look like this:\n  data: {\n    [\n      {\n        attributes: {\n          id: 1,\n          name: 'Car 1'\n        },\n        id: 1,\n        type: 'cars'\n      },\n      {\n        attributes: {\n          id: 2,\n          name: 'Car 2'\n        },\n        id: 2,\n        type: 'cars'\n      },\n      //....\n    ]\n  }\n\n\nThere are other keys as well, such as errors, and relationships. We will expand on relations further in the tutorial.\n// /app/mirage/config.js\n\nexport default function() {\n  this.get('/cars', (db, request) =\u0026gt; {\n    let data = {};\n    data = db.cars.map((attrs) =\u0026gt; {\n      let rec = {type: 'cars', id: attrs.id, attributes: attrs};\n      return rec;\n    });\n\n    return { data };\n  });\n};\n\nWhen we run our tests again, they pass.  If you’d like to see it work in development, generate some cars in scenarios/default.js, and go to localhost:4200/cars.\n// /app/mirage/scenarios/default.js\nexport default function(server) {\n\n    // Seed your development database using your factories. This data will not be loaded in your tests.\n    server.createList('car', 10);\n}\n\nWhats going on here?\n\nWhen we visit the cars route, ember sends us to the cars/index route. The route fires the model hook, where ember data sends out a GET request for all of the cars.  The mirage route in mirage/config.js intercepts the request, gets the cars that we generated in the test, adds them to a JSON API formatted object, and sends it back as the response.  No api needed!\n\nNow that we have a working acceptance test, lets create a car component for our cars to live in.\n$ ember g component a-car\n\nEmber created a component integration test, which we'll use. It's easy to setup Mirage for an integration tests.  Under tests/helpers/, create a file called mirage-integration.js\n//tests/helpers/mirage-integration.js\nimport mirageInitializer from '../../initializers/ember-cli-mirage';\n\nexport default function setupMirage(container) {\n  mirageInitializer.initialize(container);\n}\n\nand in your component test, import the setupMirage function, you will invoke in the moduleForComponent setup hook, passing in this.container.\n//app/tests/integration/components/a-car-test.js\n\nimport { moduleForComponent, test } from 'ember-qunit';\nimport setupMirage from '../../helpers/mirage-integration';\nimport hbs from 'htmlbars-inline-precompile';\n\nmoduleForComponent('a-car', 'Integration | Component | a car', {\n  integration: true,\n  setup() {\n    setupMirage(this.container);\n  }\n});\n\ntest('it renders', function(assert) {\n  const car = server.create('car');\n  this.set('car', car);\n  this.render(hbs`{{a-car car=car}}`);\n\n  assert.equal(this.$().text().trim(), 'Car 1');\n});\n\nIn this test, we create a car, and a component (this) and set it on the component. Then we can actually render the template, and assert what the components text should be. Of course we haven't done anything with our component\nyet, so the test fails.\n\nIn our cars/index template, we're rendering our component inside of an li, with a class of 'car'.  Add those attributes to the component.\nimport Ember from 'ember';\n\nexport default Ember.Component.extend({\n  tagName: 'li',\n  classNames: ['car']\n});\n\nMove the {{car.name}} expression into the component template, and render the component in the each loop, passing the model into the component.\n\u0026lt;!-- templates/components/a-car.hbs --\u0026gt;\n{{car.name}}\n\u0026lt;!-- templates/cars/index.hbs --\u0026gt;\nCars/Index\n\n\u0026lt;ul class='cars'\u0026gt;\n  {{#each model as |car|}}\n    {{a-car car=car}}\n  {{/each}}\n\u0026lt;/ul\u0026gt;\n\nRun the tests, they should pass.\n\n\nAdding New Cars\n\nNow that our cars index is tested and working, we need to be able to add more cars to our collection. Let's make a test.\n//tests/acceptance/cars-test.js\ntest('I can add a new car', function(assert){\n  server.createList('car', 10); visit('/cars');\n\n  click('#add-car'); fillIn('input[name=\"car-name\"]', 'My new car');\n  click('button');\n\n  andThen(() =\u0026gt; {\n    const newCar = find('li.car:contains(\"My new car\")');\n    assert.equal(newCar.text().trim(), \"My new car\");\n  });\n});\n\nOur test fails because there's no link with an id of add-car. This link should take us to the cars.new route. In your cars/index template at the bottom of the file, add:\n\u0026lt;!-- app/templates/cars/index.hbs --\u0026gt;\n\u0026lt;!-- ... --\u0026gt;\n\n{{#link-to 'cars.new' id='add-car'}}\n  Add new car\n{{/link-to}}\n\nNow our test fails because we don't have the specified input field. We'll need the cars/new template, we also know that we will need that route. Generating the route will create both for us, as well as adding the route to our router.\nember g route cars/new\n\nThe router should now look like:\n//router.js\nimport Ember from 'ember';\nimport config from './config/environment';\n\nvar Router = Ember.Router.extend({\n  location: config.locationType\n});\n\n\nRouter.map(function() {\n  this.route('cars', function() {\n    this.route('new', {});\n  });\n});\n\nexport default Router;\n\nAdd the form for creating a car to our cars/new template:\n\u0026lt;!--app/templates/cars/new.hbs--\u0026gt;\nNew Car\n\n\u0026lt;form {{action 'createCar' name on='submit'}}\u0026gt;\n  {{input name='car-name' value=name}}\n  \u0026lt;button\u0026gt; Create Car \u0026lt;/button\u0026gt;\n\u0026lt;/form\u0026gt;\n\nWe know we'll need an action to handle the creation of the car, so we'll go ahead and declare that now.  Our test will fail because there's nothing to handle the action named createCar yet. My preference is to handle anything related to data in the route when I can, so we'll do that.\n// /app/routes/cars/new.hbs\nimport Ember from 'ember';\nexport default Ember.Route.extend({\n  actions: {\n     createCar(name){\n      const car = this.store.createRecord('car', { name });\n\n      car.save()\n        .then(() =\u0026gt; {\n          this.transitionTo('cars');\n        }).catch(() =\u0026gt; {\n          // something that handles failures\n        });\n     }\n   }\n\n});\n\nNow our Ember pieces are hooked up, but the test fails because mirage doesn't see a route that specifies a POST request to /cars. Add it to the Mirage config file.\n// /app/mirage/config.js\n\nexport default function() {\n  //...\n\n  this.post('/cars', (db, request) =\u0026gt; {\n    return JSON.parse(request.requestBody);\n  });\n};\n\nOur JSONAPIAdapter sends the serialized data in the correct format, so all we have to do is parse it, and return it.\n\nAnd with that our test should pass.\n\n\nViewing Parts\n\nI mentioned earlier that our cars contain parts. We'll make it so that when we click our car, we will be taken to that that car's parts page. Let's generate a test for parts.\n$ ember g acceptance-test parts\n\nDelete the generated test and add the following.\n//tests/acceptance/parts.js\n\ntest('when I click a car, I see its parts', (assert) =\u0026gt; {\n  const car = server.create('car');\n  const parts = server.createList('part', 4, { car_id: car.id });\n  visit('/cars');\n  click('.car-link');\n\n  andThen(() =\u0026gt; {\n    assert.equal(currentURL(), `/car/${car.id}/parts`);\n    assert.equal(find('.part').length, parts.length);\n  });\n});\n\nOur first breakage occurs because Mirage has no part factory.\n//mirage/factories/part.js\n\nimport Mirage from 'ember-cli-mirage';\n\nexport default Mirage.Factory.extend({\n  name(i) { return `Part ${i}`; }\n});\n\nNow QUnit yells because we have no links. Turn our list of cars into links, so that when we click on one, we can see that car's parts.\n\u0026lt;!-- templates/components/a-car.hbs --\u0026gt;\n{{#link-to 'car.parts' car class='car-link'}}\n  {{car.name}}\n{{/link-to}}\n\nQUnit shames us for not having a car.parts route.\n$ ember g route car/parts\n\nThe router should look like:\n//router.js\nimport Ember from 'ember';\nimport config from './config/environment';\n\nvar Router = Ember.Router.extend({\n  location: config.locationType\n});\n\nRouter.map(function() {\n  this.route('cars', function() {\n    this.route('new', {});\n  });\n\n  this.route('car', function(){\n    this.route('parts', {});\n  });\n});\n\nexport default Router;\n\nWe'll add a dynamic segment of id to the car path.\n//...\n  this.route('car', { path: '/car/:id'}, function(){\n    this.route('parts');\n  });\n//...\n});\n\nexport default Router;\n\nSince our route is nested, we need to specify the model for the parent route.\n$ ember g route car\n\n\nIn the car route, return the car specified by the id dynamic segment.\n//routes/car.js\nimport Ember from 'ember';\n\nexport default Ember.Route.extend({\n  model(params){\n    return this.store.find('car', params.id);\n  }\n});\n\nWe also have to create a Mirage route to GET a single car. At this point in the app, we have had our cars loaded from visiting the index, but a user could go straight to a car/:id url, so we need to handle that.\nJSON API requires relationship information to be stored in a 'relationships' object. Add it to your mirage config file.\n//mirage/config.js\nexport default function() {\n  //...\n\n  this.get('/cars/:id', (db, request) =\u0026gt; {\n    let car = db.cars.find(request.params.id);\n    let parts = db.parts.where({car_id: car.id});\n\n    let data = {\n      type: 'car',\n      id: request.params.id,\n      attributes: car,\n      relationships: {\n        parts:{\n          data:{}\n        }\n      }\n    }\n\n    data.relationships.parts.data = parts.map((attrs) =\u0026gt; {\n      return { type: 'parts', id: attrs.id, attributes: attrs };\n    });\n\n    return { data };\n  });\n\n}\n\n\nAdditionally, in our Mirage /cars route, we are only\nreturning the car information, not the associated parts.  What this means is, if the first page we visit is the /cars page, those cars will already be loaded in the store (with no knowledge of any associated parts).\nWhen we go to the cars/part page, the store won't fetch the model, because it's already in the store, so there will be no parts available to render.  We should load a cars parts in the cars/index route.\n//mirage/config.js\nexport default function() {\n  this.get('/cars', (db, request) =\u0026gt; {\n    let data = {};\n    data = db.cars.map((attrs) =\u0026gt; {\n\n      let car = {\n        type: 'cars',\n        id: attrs.id,\n        attributes: attrs ,\n        relationships: {\n          parts: {\n            data: {}\n          }\n        },\n      };\n\n      data.relationships.parts.data = db.parts\n        .where({car_id: attrs.id})\n        .map((attrs) =\u0026gt; {\n          return {\n            type: 'parts',\n            id: attrs.id,\n            attributes: attrs\n          };\n        });\n\n      return car;\n\n    });\n    return { data };\n  });\n//....\n\nWe also need the Mirage end-points for getting a part.\n//mirage/config.js\nexport default function() {\n//...\n  this.get('parts/:id', (db, request) =\u0026gt; {\n    let part = db.parts.find(request.params.id);\n\n    let data = {\n      type: 'parts',\n      id: request.params.id,\n      attributes: part,\n    };\n\n    return { data };\n  });\n//...\n\nNow we need a part model, and a factory.\n//models/part.js\nimport DS from 'ember-data';\n\nexport default DS.Model.extend({\n  name: DS.attr('string'),\n  car: DS.belongsTo('car')\n});\nimport Mirage from 'ember-cli-mirage';\n\nexport default Mirage.Factory.extend({\n  name(i) { return `Part ${i}`; }\n});\n\nAnd update our car model to show the association.\n//models/car.js\nimport DS from 'ember-data';\n\nexport default DS.Model.extend({\n  name: DS.attr('string'),\n  parts: DS.hasMany('part')\n});\n\nAnd our template:\n\n\u0026lt;!-- car/parts.hbs --\u0026gt;\nParts\n\u0026lt;ul\u0026gt;\n  {{model.name}}\n  {{#each model.parts as |part|}}\n    \u0026lt;li class='part'\u0026gt;\n      {{part.name}}\n    \u0026lt;/li\u0026gt;\n  {{/each}}\n\u0026lt;/ul\u0026gt;\n\nAnd now our test should be green.\n\nI'll leave converting the part into a component with an integration test as an exercise for you to complete. The steps are the same as they were for cars.\n\n\nAdding Parts\n\nOur last test will cover adding parts. At the bottom of your parts acceptance test:\n//tests/acceptance/parts.js\n\ntest('I can add a new part to a car', (assert) =\u0026gt; {\n  server.create('car');\n  visit('/cars');\n  click('.car-link');\n  click('.new-part');\n\n  fillIn('input[name=\"part-name\"]', \"My new part\");\n  click('button');\n  andThen(() =\u0026gt; {\n    assert.equal(find('.part').text().trim(), \"My new part\");\n  });\n});\n\nOur test tells us we don't have a '.new-part' link. in our template:\nParts\n\u0026lt;!-- car/parts.hbs --\u0026gt;\n\u0026lt;ul\u0026gt;\n  {{model.name}}\n  {{#each model.parts as |part|}}\n    \u0026lt;li class='part'\u0026gt;\n      {{part.name}}\n    \u0026lt;/li\u0026gt;\n  {{/each}}\n\u0026lt;/ul\u0026gt;\n\n{{#link-to 'car.new-part' model class='new-part'}}\n  Add new Part\n{{/link-to}}\n$ ember g route car/new-part\n\nNow we need a 'car.new-part' route.\n//router.js\nRouter.map(function() {\n  this.route('cars', function() {\n    this.route('new', {});\n  });\n\n  this.route('car', { path: '/car/:id' }, function(){\n    this.route('parts', {});\n    this.route('new-part', {});\n  });\n});\n\nAnd a template for our route to render.\n\u0026lt;!--templates/car/new-part--\u0026gt;\nNew Part\n\n\u0026lt;form {{action 'newPart' name on='submit'}}\u0026gt;\n  {{input name='part-name' value=name}}\n  \u0026lt;button\u0026gt; Create Part \u0026lt;/button\u0026gt;\n\u0026lt;/form\u0026gt;\n//routes/car/new-part.js\nimport Ember from 'ember';\n\nexport default Ember.Route.extend({\n  actions: {\n    newPart(name){\n      const car = this.modelFor('car');\n      const part = this.store.createRecord('part', { name, car });\n      part.save().then(() =\u0026gt; {\n        this.transitionTo('car.parts', car);\n      });\n    }\n  }\n});\n\nAnd a Mirage endpoint:\nthis.post('parts', (db, request) =\u0026gt; {\n  return JSON.parse(request.requestBody);\n});\n\nAnd we're done! This process will be even easier once Mirage supports JSON API, which is on its way.  You can view the source at https://github.com/jsncmgs1/mirage-tutorial.git.\n\n\n\nDemystifying Ember Async Testing\nJSON API\nEmber CLI Mirage\n\n","summary":"When developing a client side javascript app, you won’t always have an API available before you start. Even when you do, you probably don’t want to have your tests reliant on the the API end-points.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/237/mirage.jpg","date_published":"2015-10-19T09:00:00-04:00","data_modified":"2015-10-19T09:15:56-04:00","author":{"name":"Jason Cummings","url":"https://hashrocket.com/team/jason-cummings","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/93/jason-cummings.jpg"},"tags":["Javascript","Ember","Testing","Software"]},{"id":"https://hashrocket.com/blog/posts/ember-weekend-recap-episode-1-2","url":"https://hashrocket.com/blog/posts/ember-weekend-recap-episode-1-2","title":"Ember Weekend Recap: Episode 1 \u0026 2","content_html":"A little over a month ago 2 of my co-workers, Chase and Jon, started a podcast about [Ember JS](http://emberjs.com/) called [Ember Weekend](https://emberweekend.com/episodes). Here's a guide to the topics they approached on the 1st and 2nd episodes.\r\n \n\n\r\n## Episode 1 - [Our First Foray](https://emberweekend.com/episodes/our-first-foray)\r\n \r\n### EmberSherpa Workshop\r\n \r\n[Jon](https://twitter.com/rondale_sc) attended the online workshop about Ember 1.11 by [Ember Sherpa](http://embersherpa.com/) and was super excited about some of the changes.\r\nIf you overslept like [Chase](https://twitter.com/code0100fun), you can watch it on [Youtube](https://www.youtube.com/watch?v=8GMeMM0ukYM).\r\n \r\nThe official changelog can be found at the Ember JS blog: [http://emberjs.com/blog/2015/03/27/ember-1-11-0-released.html](http://emberjs.com/blog/2015/03/27/ember-1-11-0-released.html).\r\n \r\n### EmberJax Chat\r\n \r\nTo test some of the new features on Ember 1.11, Chase built a chat app. To see it in action visit [Ember Jax Chat](https://emberjax-chat.firebaseapp.com/).\r\n \r\nThe source code can be found on Github: [https://github.com/code0100fun/emberjax-chat](https://github.com/code0100fun/emberjax-chat).\r\n \r\n### Babel\r\n \r\n[Babel](https://babeljs.io/) is a transpiler from ECMAScript 6 (ES6) into ES5.\r\nTo learn more about ES6 visit [https://babeljs.io/docs/learn-es6](https://babeljs.io/docs/learn-es6/).\r\n \r\n### Ember Observer\r\n \r\nThe [Ember Observer](http://emberobserver.com/) is a tool to discover ember-cli addons. They categorize, review, and give scores to each project they list.\r\nMake sure you check the [details](http://emberobserver.com/about) about how they review and score addons before you choose one for your app.\r\n \r\n### Ember Resources\r\n \r\n[https://github.com/rondale-sc/introductory-ember-resources](https://github.com/rondale-sc/introductory-ember-resources)\r\n \r\nA recurring question when starting with a new technology is \"How do I get started?\".  Jon created a repository with some links to help beginners find great content. Know of a good link for beginners? Feel free to contribute with new items.\r\n \r\n### Growing Ember: One Tomster at a Time\r\n \r\nDuring [Ember Conf 2015](http://emberconf.com/), Jamie White talked about growing the Ember Community and mentioned how to create a custom Tomster. Find more information about it on the Ember JS site: [http://emberjs.com/meetup-assets/](http://emberjs.com/meetup-assets/)\r\n \r\nIf you're interested, the talk is available on [Youtube](https://www.youtube.com/watch?v=xsG0gDkvDPw).\r\n \r\n## Episode 2 - [The Weekend Strikes Back](https://emberweekend.com/episodes/the-weekend-strikes-back)\r\n \r\n### Ember JS Versioned Guides\r\n \r\nhttp://guides.emberjs.com/v1.11.0/\r\n \r\nNow it is possible to see the documentation for Ember by version. Super useful for those who are tied to an older version. \r\nAlso, the new examples use Ember CLI. \r\n \r\n### QUnit Custom Assertion\r\n \r\nhttps://gist.github.com/code0100fun/6f87155564ccf3724749\r\n \r\nCheck this gist on how to test custom assertions with QUnit. \r\n \r\n### Ember CLI Mirage\r\n \r\nThe [Ember CLI Mirage](https://github.com/samselikoff/ember-cli-mirage) is a library that help you to stub a backend service to develop, test and prototype an Ember CLI app. \r\n \r\n### Component with Optional Block\r\n \r\nhttps://gist.github.com/code0100fun/f5922b6f44eee2abc21d\r\n \r\nGist about how to test component with optional block. Have a better way to do this? Send it our way by adding a comment to this blog post or on the Gist. \r\n \r\n### Ember Try\r\n \r\nThe [Ember Try](https://github.com/kategengler/ember-try) is a library that allows you to test your addon against multiple Bower dependencies (ember version, ember-data). \r\n \r\n### Rubyist's Guide To Ember Dependencies\r\n \r\nhttp://reefpoints.dockyard.com/2015/03/24/rubyists-guide-to-ember-dependencies.html\r\n \r\nMichael Dupuis wrote a great blog post explaining the Ember JS dependencies for the Rubyist. Very useful content. \r\n \r\nThat's it for this edition! Make sure you keep up to date by following [@emberweekend](http://twitter.com/emberweekend) on Twitter and subscribing via [RSS feed](https://emberweekend.com/feed.xml) or [iTunes](https://itunes.apple.com/us/podcast/ember-weekend/id981719021).","content_text":"A little over a month ago 2 of my co-workers, Chase and Jon, started a podcast about Ember JS called Ember Weekend. Here's a guide to the topics they approached on the 1st and 2nd episodes.\nEpisode 1 - Our First Foray\nEmberSherpa Workshop\n\nJon attended the online workshop about Ember 1.11 by Ember Sherpa and was super excited about some of the changes.\nIf you overslept like Chase, you can watch it on Youtube.\n\nThe official changelog can be found at the Ember JS blog: http://emberjs.com/blog/2015/03/27/ember-1-11-0-released.html.\nEmberJax Chat\n\nTo test some of the new features on Ember 1.11, Chase built a chat app. To see it in action visit Ember Jax Chat.\n\nThe source code can be found on Github: https://github.com/code0100fun/emberjax-chat.\nBabel\n\nBabel is a transpiler from ECMAScript 6 (ES6) into ES5.\nTo learn more about ES6 visit https://babeljs.io/docs/learn-es6.\nEmber Observer\n\nThe Ember Observer is a tool to discover ember-cli addons. They categorize, review, and give scores to each project they list.\nMake sure you check the details about how they review and score addons before you choose one for your app.\nEmber Resources\n\nhttps://github.com/rondale-sc/introductory-ember-resources\n\nA recurring question when starting with a new technology is \"How do I get started?\".  Jon created a repository with some links to help beginners find great content. Know of a good link for beginners? Feel free to contribute with new items.\nGrowing Ember: One Tomster at a Time\n\nDuring Ember Conf 2015, Jamie White talked about growing the Ember Community and mentioned how to create a custom Tomster. Find more information about it on the Ember JS site: http://emberjs.com/meetup-assets/\n\nIf you're interested, the talk is available on Youtube.\nEpisode 2 - The Weekend Strikes Back\nEmber JS Versioned Guides\n\nhttp://guides.emberjs.com/v1.11.0/\n\nNow it is possible to see the documentation for Ember by version. Super useful for those who are tied to an older version. \nAlso, the new examples use Ember CLI. \nQUnit Custom Assertion\n\nhttps://gist.github.com/code0100fun/6f87155564ccf3724749\n\nCheck this gist on how to test custom assertions with QUnit. \nEmber CLI Mirage\n\nThe Ember CLI Mirage is a library that help you to stub a backend service to develop, test and prototype an Ember CLI app. \nComponent with Optional Block\n\nhttps://gist.github.com/code0100fun/f5922b6f44eee2abc21d\n\nGist about how to test component with optional block. Have a better way to do this? Send it our way by adding a comment to this blog post or on the Gist. \nEmber Try\n\nThe Ember Try is a library that allows you to test your addon against multiple Bower dependencies (ember version, ember-data). \nRubyist's Guide To Ember Dependencies\n\nhttp://reefpoints.dockyard.com/2015/03/24/rubyists-guide-to-ember-dependencies.html\n\nMichael Dupuis wrote a great blog post explaining the Ember JS dependencies for the Rubyist. Very useful content. \n\nThat's it for this edition! Make sure you keep up to date by following @emberweekend on Twitter and subscribing via RSS feed or iTunes.\n","summary":"A little over a month ago 2 of my co-workers, Chase and Jon, started a podcast about Ember JS called Ember Weekend. Here's a guide to the topics they approached on the 1st and 2nd episodes.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/223/ember_weekend_recap_header.jpg","date_published":"2015-05-07T09:00:00-04:00","data_modified":"2025-10-10T15:18:39-04:00","author":{"name":"Thais Camilo","url":"https://hashrocket.com/team/thais-camilo","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/46/thais-camilo.jpg"},"tags":["Ember","Javascript"]},{"id":"https://hashrocket.com/blog/posts/broccoli-the-build-tool-not-the-vegetable","url":"https://hashrocket.com/blog/posts/broccoli-the-build-tool-not-the-vegetable","title":"Broccoli: The Build Tool, Not the Vegetable","content_html":"Broccoli is the blazingly fast build tool used by Ember.js and Ember CLI. Though as we'll see it has uses in any JavaScript project and maybe beyond that.\n\n## In your Ember CLI app\r\nIf you have created an Ember CLI application you are probably using Broccoli very minimally. Your app is initialized with a `Brocfile.js` that looks something like this:\r\n\r\n```js\r\n// Brocfile.js\r\nvar EmberApp = require('ember-cli/lib/broccoli/ember-app');\r\nvar app = new EmberApp();\r\nmodule.exports = app.toTree();\r\n```\r\n\r\nTypically the only interaction you will have with the `Brocfile.js` of an Ember CLI app is when you need to include a vendored library installed with Bower.\r\n\r\n```js\r\n// Brocfile.js\r\nvar EmberApp = require('ember-cli/lib/broccoli/ember-app');\r\nvar app = new EmberApp();\r\napp.import(app.bowerDirectory + '/moment/moment.js'); // \u003c--- +\r\nmodule.exports = app.toTree();\r\n```\r\n\r\nThere are some more advanced use cases but it's likely that anything you do in your `Brocfile.js` should be moved to an Ember CLI addon. A great example of the **wrong way** to do this is the RSS feed generator currently being used by [Ember Weekend](https://emberweekend.com). If you take a look at the [`Brocfile.js`](https://github.com/emberjax/ember-weekend/blob/f54e26453671c499df49e29bcb708552b00d4a11/Brocfile.js#L27) for that repo,  you'll see a whole mess of things going on. This will eventually be moved out into an Ember CLI addon because it's functionality that can be tested and maintained outside of the main repository and would be useful in other applications.\r\n\r\nEven the `app.import(...)` statements can be moved to addons. You can see this in [ember-moment](https://github.com/stefanpenner/ember-moment/blob/28b33e03614daaf3b3be8a9990e62b27dc7ffac2/index.js). \r\n\r\n## In your Ember CLI addon\r\nAddons use Broccoli to a somewhat greater extent than apps. In the `setupPreprocessorRegistry` hook you add preprocessors to handle transpiling things like [CoffeeScript](https://github.com/kimroen/ember-cli-coffeescript/blob/f4d6f26915a4b8fc523d745d7d85497d8e138432/index.js#L32), [ECMAScript 2015](https://github.com/babel/ember-cli-babel/blob/5bde7459ef00811a13cf64c28660bd0f33f81a34/index.js#L10), or [HTMLBars](https://github.com/ember-cli/ember-cli-htmlbars/blob/f0d29f0a9b9aaa18d4ed45ebcd463141e2ca172a/ember-addon-main.js#L20). \r\n\r\n\r\nThe preprocessor object is basically just this:\r\n\r\n```js\r\n{ \r\n  name: '...',\r\n  ext: '...',\r\n  toTree: function(tree, inputPath, outputPath) {\r\n  \t// build output tree\r\n    return outputTree;\r\n  }\r\n}\r\n```\r\nThis object is simple enough to construct so some addons simply build it in place, such as [ember-cli-babel](https://github.com/babel/ember-cli-babel/blob/5bde7459ef00811a13cf64c28660bd0f33f81a34/index.js#L10). Others extract this into a separate module, as is the case with [ember-cli-coffeescript](https://github.com/kimroen/ember-cli-coffeescript/blob/f4d6f26915a4b8fc523d745d7d85497d8e138432/lib/coffee-preprocessor.js). In this case, the instance of `MyCustomPreprocessor` has the same properties and behavior as above:\r\n\r\n```js\r\nsetupPreprocessorRegistry: function(type, registry) {\r\n  var preprocessor = new MyCustomPreprocessor();\r\n  registry.add(type, preprocessor); // common types are 'js', 'css', and 'template'\r\n},\r\n```\r\nNow in the `toTree` function of the preprocessor we see where Broccoli comes into play. For clairity and to comply with the [single responsibility principle](http://en.wikipedia.org/wiki/Single_responsibility_principle), the building of the output tree should take place in another object. \r\n\r\n```js\r\ntoTree: function(tree, inputPath, outputPath, options) {\r\n  return new MyCutomTreeBuilder(tree);\r\n}\r\n```\r\n\r\nAgain this can live in the same repo like [ember-cli-htmlbars](https://github.com/ember-cli/ember-cli-htmlbars/blob/f0d29f0a9b9aaa18d4ed45ebcd463141e2ca172a/index.js) does, or can be extracted to a generic Broccoli library, as [broccoli-babel-transpiler](https://github.com/babel/broccoli-babel-transpiler/blob/3304a4c975d5a611f062a9fa528ff9943840e4c7/index.js) does.\r\n\r\n\r\nAll of the preprocessors I've mentioned build upon a simple base prototype called [broccoli-filter](https://github.com/broccolijs/broccoli-filter). As you can see, there is no implementation of `read` for this tree. You'll see why that is important when we discuss the tree API later. The `broccoli-filter` plugin handles much of the work for us, accepting simple configuration data and a `processString` callback to be executed for each file in the input tree.\r\n\r\n```js\r\nvar Filter = require('broccoli-filter');\r\nfunction MyCutomTreeBuilder(inputTree, options) {\r\n  this.inputTree = inputTree;\r\n}\r\nMyCutomTreeBuilder.prototype = Object.create(Filter.prototype);\r\nMyCutomTreeBuilder.prototype.extensions = ['js'];\r\nMyCutomTreeBuilder.prototype.targetExtension = 'js';\r\nMyCutomTreeBuilder.prototype.processString = function (string, relativePath) { ... };\r\n```\r\n\r\nWe could keep going down this rabbit hole, but I find it is easier to understand Broccoli from the bottom-up at this point. We will come back to visit `broccoli-filter` later.\r\n\r\n## Broccoli Everywhere\r\nIt's worth mentioning that many other libraries such as my Haml-like template parser, [hbars](https://github.com/code0100fun/hbars), use Broccoli to manage transpilation, linting, and building distributions entirely outside of the Ember ecosystem.\r\n\r\n## Broccoli Core\r\nThe Broccoli tool actually handles quite a few tasks. There are utilities that find/load the `Brocfile.js`, a file watcher, a server to issue live reload commands, and other related functionality. We will concentrate on just the `Builder`. But first...\r\n\r\n### What makes a tree a tree\r\nHere is an example of a minimal Broccoli tree according to the Broccoli Plugin API Specification:\r\n\r\n```js\r\nvar tree = {\r\n  read: function(readTree){\r\n    var tmp = quickTemp.makeOrRemake(this, 'tmpDestDir');\r\n    // or use promise-map-series\r\n    return readTree(subtree).then(function(dir){\r\n      // subtree has finished proccesing now\r\n      // read from subtree if needed and write to tmp files\r\n      return tmp;\r\n    });\r\n  },\r\n  cleanup: function(){\r\n    quickTemp.remove(this, 'tmpDestDir');\r\n  }\r\n};\r\n```\r\n**Note: `quickTemp` is from [node-quick-temp](https://github.com/joliss/node-quick-temp), and is used in various Broccoli plugins to generate temporary directories**\r\n\r\n As you can see there are only two functions, `read` and `cleanup`, defined on this object. This API allows for tremendous flexiblility. Something so simple can be easily implemented by plugin authors. The seemingly complicated `broccoli-filter` mentioned earlier is just an extension of these two functions.\r\n\r\n### Builder\r\n\r\nAt the core of Broccoli is the `Builder`. The builder is constructed with a path string or a tree:\r\n\r\n```js\r\nvar broccoli = require('broccoli');\r\n\r\nvar tree = broccoli.loadBrocfile();\r\nvar builder = new broccoli.Builder(tree);\r\n// or\r\nvar builder = new broccoli.Builder('someDir');\r\n```\r\n\r\n[Ember CLI](https://github.com/ember-cli/ember-cli/blob/126cf551d44244e968027250793b648a01fce065/lib/models/builder.js#L23) creates a builder just like this.\r\n\r\n### Build\r\n\r\nThe `build` command is called to kick off the process of building the output. Instead of blocking here while the output is built, a [promise](https://github.com/tildeio/rsvp.js/) to an output is returned. \r\n\r\n```js\r\nbuilder.build().then(function(output){\r\n  // finished building\r\n});\r\n```\r\n\r\nThis output is an object containing the output directory, the node representation of the tree and the time it took to build.\r\n\r\n```js\r\nvar output = {\r\n  directory: 'outputDirname'\r\n  graph: node,\r\n  totalTime: timeInMillis\r\n};\r\n```\r\n\r\nThe node is the top level tree and its associated metadata:\r\n\r\n```js\r\nvar node = {\r\n  tree: tree,\r\n  subtrees: [ subtreeNode ],\r\n  selfTime: timeInMillis,\r\n  totalTime: timeInMillis,\r\n  directory: 'outputDirname'\r\n};\r\n```\r\n\r\nLet's step through the process used to build this output.\r\n\r\n### Recurse\r\n\r\nThe builder simply calls `readAndReturnNodeFor` calls `read` on the given tree and returns a promise to the node for that tree to be resolved once it's done being built. This is an abbreviated version:\r\n\r\n```js\r\nfunction Builder (tree) {\r\n  this.tree = tree;\r\n}\r\n\r\nBuilder.prototype.build = function(willReadStringTree) {\r\n  ...\r\n  return RSVP.resolve()\r\n    .then(function() {\r\n      return readAndReturnNodeFor(tree);\r\n    })\r\n    .then(function(node) {\r\n      return { directory: node.directory, graph: node, totalTime: node.totalTime };\r\n    })\r\n    .catch(...);\r\n};\r\n```\r\n\r\nThe `readAndReturnNodeFor` does exactly what is says, calling `read` on the given tree.\r\n\r\n```js\r\nfunction readAndReturnNodeFor(tree){\r\n  ...\r\n  var node = {\r\n    tree: tree,\r\n    subtrees: [],\r\n    selfTime: 0,\r\n    totalTime: 0,\r\n    directory: null // \u003c-- populated once reolved\r\n  };\r\n  ...\r\n  return RSVP.resolve()\r\n    .then(function() {\r\n      return tree.read(readTree); // \u003c-- call `read` on input tree\r\n    }).then(function (treeDir) {\r\n      node.directory = treeDir;\r\n      return node;\r\n    });\r\n}\r\n```\r\n\r\nIf a tree consumes other trees as input it should call `readTree` for each inside the `read` function. The `readTree` function returns a promise that must be fulfilled before the next call to `readTree`. A convienient way to enure this is provided by the [promise-map-series](https://github.com/joliss/promise-map-series) library, also by [Jo Liss](https://github.com/joliss).\r\n\r\nInternally, `readTree` actually calls `readAndReturnNodeFor` recursively for all subtrees of the input tree. This of course calls `read` for their trees, and so on.\r\n\r\n```js\r\nfunction readTree(subtree) {\r\n  ...\r\n  return RSVP.resolve()\r\n    .then(function () {\r\n      return readAndReturnNodeFor(subtree) // \u003c-- recursive\r\n    })\r\n    .then(function(childNode) {\r\n      node.subtrees.push(childNode);\r\n      return childNode.directory;\r\n    })\r\n    .finally(...);\r\n}\r\n```\r\n\r\n### Cleanup\r\nOnce the build is complete you call `cleanup` on the builder to cascade the cleanup through the subtrees. Subtrees can sometimes just be a directory name, and in that case cleanup is not necessary.\r\n\r\n```js\r\nBuilder.prototype.cleanup = function () {\r\n  function cleanupTree(tree) {\r\n    if (typeof tree !== 'string') {\r\n      return tree.cleanup();\r\n    }\r\n  }\r\n\r\n  return mapSeries(this.allTreesRead, cleanupTree);\r\n};\r\n```\r\n\r\nThe `mapSeries` is from [promise-map-series](https://github.com/joliss/promise-map-series). It will call cleanup for each of the trees in `allTreesRead` sequentially, calling the next only after the promise from the last one has resolved. \r\n\r\n### Everything changes\r\n\r\nThe tree API has recently changed in Broccoli versions 0.14.x and 0.15.x, and you may have noticed some deprecation warnings. There was an [issue](https://github.com/broccolijs/broccoli/issues/204) discussed on Github that highlighted how `read` was nondescript.  The job of the function is to build the output of the tree and return the directory where the output is written. Jo Liss agreed and suggested that `rebuild` would be a more descriptive name.\r\n\r\nWhile fixing the minor naming issue, the larger issue of having to sequentially call `readTree` was fixed. With the new `rebuild` syntax, input trees are easier to manage. You simply add a property called `inputTree` or `inputTrees` to your object. Broccoli will handle calling `readTree` internally.\r\n\r\n```js\r\nvar tree = {\r\n  rebuild: function(){\r\n    var tmp = quickTemp.makeOrRemake(this, 'tmpDestDir');\r\n    return RSVP.resolve().then(function(){\r\n      // write to tmp files\r\n      return tmp;\r\n    });\r\n  },\r\n  cleanup: function(){\r\n    quickTemp.remove(this, 'tmpDestDir');\r\n  },\r\n  inputTrees: [ subtree ]\r\n};\r\n```\r\n\r\n## Conclusion\r\n\r\nThis is the information I wish I had when I was diving into Ember CLI addons for the first time. In fact, I originally put this information together as notes while deep diving into Broccoli's source and I'm glad I invested the time. So while I think broccoli is a delicious vegetable I think it makes an even better build tool!\r\n\r\nHope you found this helpful. Thanks for reading!\r\n\r\n\r\n\r\n\r\n#### Related\r\n\r\n* https://github.com/broccolijs/broccoli#plugin-api-specification\r\n* http://simonwade.me/intro-to-broccoli/\r\n* http://moduscreate.com/better-builds-begin-with-broccoli/","content_text":"Broccoli is the blazingly fast build tool used by Ember.js and Ember CLI. Though as we'll see it has uses in any JavaScript project and maybe beyond that.\nIn your Ember CLI app\n\nIf you have created an Ember CLI application you are probably using Broccoli very minimally. Your app is initialized with a Brocfile.js that looks something like this:\n// Brocfile.js\nvar EmberApp = require('ember-cli/lib/broccoli/ember-app');\nvar app = new EmberApp();\nmodule.exports = app.toTree();\n\nTypically the only interaction you will have with the Brocfile.js of an Ember CLI app is when you need to include a vendored library installed with Bower.\n// Brocfile.js\nvar EmberApp = require('ember-cli/lib/broccoli/ember-app');\nvar app = new EmberApp();\napp.import(app.bowerDirectory + '/moment/moment.js'); // \u0026lt;--- +\nmodule.exports = app.toTree();\n\nThere are some more advanced use cases but it's likely that anything you do in your Brocfile.js should be moved to an Ember CLI addon. A great example of the wrong way to do this is the RSS feed generator currently being used by Ember Weekend. If you take a look at the Brocfile.js for that repo,  you'll see a whole mess of things going on. This will eventually be moved out into an Ember CLI addon because it's functionality that can be tested and maintained outside of the main repository and would be useful in other applications.\n\nEven the app.import(...) statements can be moved to addons. You can see this in ember-moment. \nIn your Ember CLI addon\n\nAddons use Broccoli to a somewhat greater extent than apps. In the setupPreprocessorRegistry hook you add preprocessors to handle transpiling things like CoffeeScript, ECMAScript 2015, or HTMLBars. \n\nThe preprocessor object is basically just this:\n{ \n  name: '...',\n  ext: '...',\n  toTree: function(tree, inputPath, outputPath) {\n    // build output tree\n    return outputTree;\n  }\n}\n\nThis object is simple enough to construct so some addons simply build it in place, such as ember-cli-babel. Others extract this into a separate module, as is the case with ember-cli-coffeescript. In this case, the instance of MyCustomPreprocessor has the same properties and behavior as above:\nsetupPreprocessorRegistry: function(type, registry) {\n  var preprocessor = new MyCustomPreprocessor();\n  registry.add(type, preprocessor); // common types are 'js', 'css', and 'template'\n},\n\nNow in the toTree function of the preprocessor we see where Broccoli comes into play. For clairity and to comply with the single responsibility principle, the building of the output tree should take place in another object. \ntoTree: function(tree, inputPath, outputPath, options) {\n  return new MyCutomTreeBuilder(tree);\n}\n\nAgain this can live in the same repo like ember-cli-htmlbars does, or can be extracted to a generic Broccoli library, as broccoli-babel-transpiler does.\n\nAll of the preprocessors I've mentioned build upon a simple base prototype called broccoli-filter. As you can see, there is no implementation of read for this tree. You'll see why that is important when we discuss the tree API later. The broccoli-filter plugin handles much of the work for us, accepting simple configuration data and a processString callback to be executed for each file in the input tree.\nvar Filter = require('broccoli-filter');\nfunction MyCutomTreeBuilder(inputTree, options) {\n  this.inputTree = inputTree;\n}\nMyCutomTreeBuilder.prototype = Object.create(Filter.prototype);\nMyCutomTreeBuilder.prototype.extensions = ['js'];\nMyCutomTreeBuilder.prototype.targetExtension = 'js';\nMyCutomTreeBuilder.prototype.processString = function (string, relativePath) { ... };\n\nWe could keep going down this rabbit hole, but I find it is easier to understand Broccoli from the bottom-up at this point. We will come back to visit broccoli-filter later.\nBroccoli Everywhere\n\nIt's worth mentioning that many other libraries such as my Haml-like template parser, hbars, use Broccoli to manage transpilation, linting, and building distributions entirely outside of the Ember ecosystem.\nBroccoli Core\n\nThe Broccoli tool actually handles quite a few tasks. There are utilities that find/load the Brocfile.js, a file watcher, a server to issue live reload commands, and other related functionality. We will concentrate on just the Builder. But first...\nWhat makes a tree a tree\n\nHere is an example of a minimal Broccoli tree according to the Broccoli Plugin API Specification:\nvar tree = {\n  read: function(readTree){\n    var tmp = quickTemp.makeOrRemake(this, 'tmpDestDir');\n    // or use promise-map-series\n    return readTree(subtree).then(function(dir){\n      // subtree has finished proccesing now\n      // read from subtree if needed and write to tmp files\n      return tmp;\n    });\n  },\n  cleanup: function(){\n    quickTemp.remove(this, 'tmpDestDir');\n  }\n};\n\nNote: quickTemp is from node-quick-temp, and is used in various Broccoli plugins to generate temporary directories\n\nAs you can see there are only two functions, read and cleanup, defined on this object. This API allows for tremendous flexiblility. Something so simple can be easily implemented by plugin authors. The seemingly complicated broccoli-filter mentioned earlier is just an extension of these two functions.\nBuilder\n\nAt the core of Broccoli is the Builder. The builder is constructed with a path string or a tree:\nvar broccoli = require('broccoli');\n\nvar tree = broccoli.loadBrocfile();\nvar builder = new broccoli.Builder(tree);\n// or\nvar builder = new broccoli.Builder('someDir');\n\nEmber CLI creates a builder just like this.\nBuild\n\nThe build command is called to kick off the process of building the output. Instead of blocking here while the output is built, a promise to an output is returned. \nbuilder.build().then(function(output){\n  // finished building\n});\n\nThis output is an object containing the output directory, the node representation of the tree and the time it took to build.\nvar output = {\n  directory: 'outputDirname'\n  graph: node,\n  totalTime: timeInMillis\n};\n\nThe node is the top level tree and its associated metadata:\nvar node = {\n  tree: tree,\n  subtrees: [ subtreeNode ],\n  selfTime: timeInMillis,\n  totalTime: timeInMillis,\n  directory: 'outputDirname'\n};\n\nLet's step through the process used to build this output.\nRecurse\n\nThe builder simply calls readAndReturnNodeFor calls read on the given tree and returns a promise to the node for that tree to be resolved once it's done being built. This is an abbreviated version:\nfunction Builder (tree) {\n  this.tree = tree;\n}\n\nBuilder.prototype.build = function(willReadStringTree) {\n  ...\n  return RSVP.resolve()\n    .then(function() {\n      return readAndReturnNodeFor(tree);\n    })\n    .then(function(node) {\n      return { directory: node.directory, graph: node, totalTime: node.totalTime };\n    })\n    .catch(...);\n};\n\nThe readAndReturnNodeFor does exactly what is says, calling read on the given tree.\nfunction readAndReturnNodeFor(tree){\n  ...\n  var node = {\n    tree: tree,\n    subtrees: [],\n    selfTime: 0,\n    totalTime: 0,\n    directory: null // \u0026lt;-- populated once reolved\n  };\n  ...\n  return RSVP.resolve()\n    .then(function() {\n      return tree.read(readTree); // \u0026lt;-- call `read` on input tree\n    }).then(function (treeDir) {\n      node.directory = treeDir;\n      return node;\n    });\n}\n\nIf a tree consumes other trees as input it should call readTree for each inside the read function. The readTree function returns a promise that must be fulfilled before the next call to readTree. A convienient way to enure this is provided by the promise-map-series library, also by Jo Liss.\n\nInternally, readTree actually calls readAndReturnNodeFor recursively for all subtrees of the input tree. This of course calls read for their trees, and so on.\nfunction readTree(subtree) {\n  ...\n  return RSVP.resolve()\n    .then(function () {\n      return readAndReturnNodeFor(subtree) // \u0026lt;-- recursive\n    })\n    .then(function(childNode) {\n      node.subtrees.push(childNode);\n      return childNode.directory;\n    })\n    .finally(...);\n}\nCleanup\n\nOnce the build is complete you call cleanup on the builder to cascade the cleanup through the subtrees. Subtrees can sometimes just be a directory name, and in that case cleanup is not necessary.\nBuilder.prototype.cleanup = function () {\n  function cleanupTree(tree) {\n    if (typeof tree !== 'string') {\n      return tree.cleanup();\n    }\n  }\n\n  return mapSeries(this.allTreesRead, cleanupTree);\n};\n\nThe mapSeries is from promise-map-series. It will call cleanup for each of the trees in allTreesRead sequentially, calling the next only after the promise from the last one has resolved. \nEverything changes\n\nThe tree API has recently changed in Broccoli versions 0.14.x and 0.15.x, and you may have noticed some deprecation warnings. There was an issue discussed on Github that highlighted how read was nondescript.  The job of the function is to build the output of the tree and return the directory where the output is written. Jo Liss agreed and suggested that rebuild would be a more descriptive name.\n\nWhile fixing the minor naming issue, the larger issue of having to sequentially call readTree was fixed. With the new rebuild syntax, input trees are easier to manage. You simply add a property called inputTree or inputTrees to your object. Broccoli will handle calling readTree internally.\nvar tree = {\n  rebuild: function(){\n    var tmp = quickTemp.makeOrRemake(this, 'tmpDestDir');\n    return RSVP.resolve().then(function(){\n      // write to tmp files\n      return tmp;\n    });\n  },\n  cleanup: function(){\n    quickTemp.remove(this, 'tmpDestDir');\n  },\n  inputTrees: [ subtree ]\n};\nConclusion\n\nThis is the information I wish I had when I was diving into Ember CLI addons for the first time. In fact, I originally put this information together as notes while deep diving into Broccoli's source and I'm glad I invested the time. So while I think broccoli is a delicious vegetable I think it makes an even better build tool!\n\nHope you found this helpful. Thanks for reading!\nRelated\n\n\nhttps://github.com/broccolijs/broccoli#plugin-api-specification\nhttp://simonwade.me/intro-to-broccoli/\nhttp://moduscreate.com/better-builds-begin-with-broccoli/\n\n","summary":"Broccoli is the blazingly fast build tool used by Ember.js and Ember CLI. Though as we'll see it has uses in any JavaScript project and maybe beyond that.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/220/5461147989_9641bd9e83_o.jpg","date_published":"2015-04-08T12:02:41-04:00","data_modified":"2025-10-10T15:18:53-04:00","author":{"name":"Chase McCarthy","url":"https://hashrocket.com/team/chase-mccarthy","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/82/chase-mccarthy.jpg"},"tags":["Javascript","Ember"]},{"id":"https://hashrocket.com/blog/posts/a-compendium-of-hooks-in-embercli","url":"https://hashrocket.com/blog/posts/a-compendium-of-hooks-in-embercli","title":"A Compendium of Hooks in EmberCLI","content_html":"Below you'll find information relevant to creating addons in EmberCLI.  Addons are a powerful abstraction in EmberCLI.  These addons help enable the extension of Ember applications built with the framework.  EmberCLI has several generators that make creating addons simple, but knowing where to put your addon specific customizations can be difficult.  This post will attempt to list all known addon hooks as of EmberCLI version `0.1.4`.\n\nTable of Contents:\r\n\r\n1. [config](#config)\r\n- [bluprintsPath](#blueprintspath)\r\n- [includedCommands](#includedcommands)\r\n- [serverMiddleware](#servermiddleware)\r\n- [postBuild](#postbuild)\r\n- [preBuild](#prebuild)\r\n- [included](#included)\r\n- [postProcess](#postprocess) \r\n- [treeFor](#treefor)\r\n  1. [treeForApp](#treefor-cont)\r\n  - [treeForStyles](#treefor-cont)\r\n  - [treeForTemplates](#treefor-cont)\r\n  - [treeForAddon](#treefor-cont)\r\n  - [treeForVendor](#treefor-cont)\r\n  - [treeForTestSupport](#treefor-cont)\r\n  - [treeForPublic](#treefor-cont)\r\n\r\nFor each hook we'll cover the following (if applicable):\r\n\r\n- Received arguments\r\n- Source\r\n- Default implementation\r\n- Uses\r\n- Examples\r\n\r\n\u003csmall\u003eCompendium is largely based of a talk by [@rwjblue](https://github.com/rwjblue) which can be found [here](https://www.youtube.com/watch?v=e1l07N0ukzY\u0026feature=youtu.be\u0026t=1h40m53s)\u003c/small\u003e\r\n\r\n\u003ca name='config'\u003e\u003c/a\u003e\r\n## Config\r\n\r\nAugments the applications configuration settings.  Object returned from this hook is merged with the application's configuration object.  Application's configuration always take precedence.\r\n\r\n**Received arguments:**\r\n\r\n  - env - name of current environment (ie \"developement\")\r\n  - baseConfig - Initial application config\r\n\r\n**Source:** [lib/models/addon.js:312](https://github.com/stefanpenner/ember-cli/blob/v0.1.4/lib/models/addon.js#L312)\r\n\r\n**Default implementation:**\r\n\r\n```js\r\nAddon.prototype.config = function (env, baseConfig) {\r\n  var configPath = path.join(this.root, 'config', 'environment.js');\r\n\r\n  if (fs.existsSync(configPath)) {\r\n    var configGenerator = require(configPath);\r\n\r\n    return configGenerator(env, baseConfig);\r\n  }\r\n};\r\n```\r\n\r\n**Uses:**\r\n\r\n- Modifying configuration options (see list of defaults [here](https://github.com/ember-cli/ember-cli/blob/v0.1.4/lib/broccoli/ember-app.js#L83))\r\n  - For example\r\n    - `minifyJS`\r\n    - `storeConfigInMeta`\r\n    - `es3Safe`\r\n    - et, al\r\n\r\n**Examples:**\r\n\r\n- Setting `storeConfigInMeta` to false in [ember-cli-rails-addon](https://github.com/rondale-sc/ember-cli-rails-addon/blob/v0.0.4/index.js#L7)\r\n\r\n\u003ca name='blueprintspath'\u003e\u003c/a\u003e\r\n## blueprintsPath\r\n\r\nTells the application where your blueprints exist.\r\n\r\n**Received arguments:** None\r\n\r\n**Source:** [lib/models/addon.js:304](https://github.com/stefanpenner/ember-cli/blob/v0.1.4/lib/models/addon.js#L304)\r\n\r\n**Default implementation:**\r\n\r\n```js\r\nAddon.prototype.blueprintsPath = function() {\r\n  var blueprintPath = path.join(this.root, 'blueprints');\r\n\r\n  if (fs.existsSync(blueprintPath)) {\r\n    return blueprintPath;\r\n  }\r\n};\r\n```\r\n\r\n**Uses:**\r\n\r\n- Let application know where blueprints exists.\r\n\r\n**Examples:**\r\n\r\n- [ember-cli-coffeescript](https://github.com/kimroen/ember-cli-coffeescript/blob/v0.6.0/index.js#L29)\r\n\r\n\u003ca name='includedcommands'\u003e\u003c/a\u003e\r\n## includedCommands\r\n\r\nAllows the specification of custom addon commands.  Expects you to return an object whose key is the name of the command and value is the command instance.\r\n\r\n**Received arguments:** None\r\n\r\n**Source:** [lib/models/project.js:234](https://github.com/stefanpenner/ember-cli/blob/v0.1.4/lib/models/project.js#L234)\r\n\r\n**Default implementation:** None\r\n\r\n**Uses:**\r\n\r\n- Include custom commands into consuming application\r\n\r\n**Examples:**\r\n\r\n- [ember-cli-cordova](https://github.com/poetic/ember-cli-cordova/blob/v0.0.14/index.js#L19)\r\n\r\n```js\r\n  // https://github.com/rwjblue/ember-cli-divshot/blob/v0.1.6/index.js\r\n  includedCommands: function() {\r\n    return {\r\n      'divshot': require('./lib/commands/divshot')\r\n    }\r\n  }\r\n```\r\n\r\n\u003ca name='servermiddleware'\u003e\u003c/a\u003e\r\n## serverMiddleware\r\n\r\nDesigned to manipulate requests in development mode.\r\n\r\n**Received arguments:**\r\n  - options (eg express_instance, project, watcher, environment)\r\n\r\n**Source:** [lib/tasks/server/express-server.js:63](https://github.com/stefanpenner/ember-cli/blob/v0.1.4/lib/tasks/server/express-server.js#L63)\r\n\r\n**Default implementation:** None\r\n\r\n**Uses:**\r\n\r\n- Tacking on headers to each request\r\n- Modifying the request object\r\n\r\n*Note:* that this should only be used in development, and if you need the same behavior in production you'll need to configure your server.\r\n\r\n\r\n**Examples:**\r\n\r\n- [ember-cli-content-security-policy](https://github.com/rwjblue/ember-cli-content-security-policy/blob/v0.3.0/index.js#L25)\r\n\r\n- [history-support-addon](https://github.com/stefanpenner/ember-cli/blob/master/lib/tasks/server/middleware/history-support/index.js#L13)\r\n\r\n\u003ca name='postbuild'\u003e\u003c/a\u003e\r\n## postBuild\r\n\r\nGives access to the result of the tree, and the location of the output.\r\n\r\n**Received arguments:**\r\n\r\n- Result object from broccoli build\r\n  - `result.directory` - final output path\r\n\r\n**Source:** [lib/models/builder.js:111](https://github.com/stefanpenner/ember-cli/blob/v0.1.4/lib/models/builder.js#L111)\r\n\r\n**Default implementation:** None\r\n\r\n**Uses:**\r\n\r\n- Slow tree listing\r\n- May be used to manipulate your project after build has happened\r\n- Opportunity to symlink or copy files elsewhere.\r\n\r\n**Examples:**\r\n\r\n- [ember-cli-rails-addon](https://github.com/rondale-sc/ember-cli-rails-addon/blob/master/index.js#L14)\r\n\t- In this case we are using this in tandem with a rails middleware to remove a lock file.  This allows our ruby gem to block incoming requests until after the build happens reliably.\r\n\t\r\n\u003ca name='prebuild'\u003e\u003c/a\u003e\r\n## preBuild\r\n\r\nHook called before build takes place.\r\n\r\n**Received arguments:**\r\n\r\n**Source:** [lib/models/builder.js:114](https://github.com/rwjblue/ember-cli/blob/pre-build-duh/lib/models/builder.js#L114)\r\n\r\n**Default implementation:** None\r\n\r\n**Uses:**\r\n\r\n**Examples:**\r\n\r\n- [ember-cli-rails-addon](https://github.com/rondale-sc/ember-cli-rails-addon/blob/master/index.js#L14)\r\n  - In this case we are using this in tandem with a rails middleware to create a lock file.\r\n  *[See postBuild]*\r\n\r\n\r\n\u003ca name='included'\u003e\u003c/a\u003e\r\n## included\r\n\r\nUsually used to import assets into the application.\r\n\r\n**Received arguments:**\r\n\r\n- `EmberApp` instance [see ember-app.js](https://github.com/stefanpenner/ember-cli/blob/v0.1.4/lib/broccoli/ember-app.js)\r\n\r\n**Source:** [lib/broccoi/ember-app.js:216](https://github.com/stefanpenner/ember-cli/blob/v0.1.4/lib/broccoli/ember-app.js#L216)\r\n\r\n**Default implementation:** None\r\n\r\n**Uses:**\r\n\r\n- including vendor files\r\n- setting configuration options\r\n\r\n*Note:* Any options set in the consuming application will override the addon.\r\n\r\n**Examples:**\r\n\r\n```js\r\n// https://github.com/yapplabs/ember-colpick/blob/master/index.js\r\nincluded: function colpick_included(app) {\r\n  this._super.included(app);\r\n  \r\n  var colpickPath = path.join(app.bowerDirectory, 'colpick');\r\n  \r\n  this.app.import(path.join(colpickPath, 'js',  'colpick.js'));\r\n  this.app.import(path.join(colpickPath, 'css', 'colpick.css'));\r\n}\r\n```\r\n\r\n- [ember-cli-rails-addon](https://github.com/rondale-sc/ember-cli-rails-addon/blob/master/index.js#L6)\r\n\r\n\u003ca name='postprocess'\u003e\u003c/a\u003e\r\n## postProcess\r\n\r\n**Received arguments:**\r\n\r\n- post processing type (eg all)\r\n- receives tree after build\r\n\r\n**Source:** [lib/broccoli/ember-app.js:251](https://github.com/stefanpenner/ember-cli/blob/v0.1.4/lib/broccoli/ember-app.js#L251)\r\n\r\n**Default implementation:** None\r\n\r\n**Uses:**\r\n\r\n- fingerprint assets\r\n- running processes after build but before toTree\r\n\r\n**Examples:**\r\n\r\n- [broccoli-asset-rev](https://github.com/rickharrison/broccoli-asset-rev/blob/c82c3580855554a31f7d6600b866aecf69cdaa6d/index.js#L29)\r\n\r\n\u003ca name='treefor'\u003e\u003c/a\u003e\r\n## treeFor\r\n\r\nReturn value is merged with application tree of same type\r\n\r\n**Received arguments:**\r\n\r\n- returns given type of tree (eg app, vendor, bower)\r\n\r\n**Source:** [lib/broccoli/ember-app.js:240](https://github.com/ember-cli/ember-cli/blob/v0.1.4/lib/broccoli/ember-app.js#L240)\r\n\r\n**Default implementation:**\r\n\r\n```js\r\nAddon.prototype.treeFor = function treeFor(name) {\r\n  this._requireBuildPackages();\r\n\r\n  var tree;\r\n  var trees = [];\r\n\r\n  if (tree = this._treeFor(name)) {\r\n    trees.push(tree);\r\n  }\r\n\r\n  if (this.isDevelopingAddon() \u0026\u0026 this.app.hinting \u0026\u0026 name === 'app') {\r\n    trees.push(this.jshintAddonTree());\r\n  }\r\n\r\n  return this.mergeTrees(trees.filter(Boolean));\r\n};\r\n```\r\n\r\n**Uses:**\r\n\r\n- manipulating trees at build time\r\n\r\n**Examples:**\r\n\r\n\u003ca name='treefor-cont'\u003e\u003c/a\u003e\r\n# treeFor (cont...)\r\n\r\nInstead of overriding `treeFor` and acting only if the tree you receive matches the one you need EmberCLI has custom hooks for the following Broccoli trees\r\n\r\n- treeForApp\r\n- treeForStyles\r\n- treeForTemplates\r\n- treeForAddon\r\n- treeForVendor\r\n- treeForTestSupport\r\n- treeForPublic\r\n\r\nSee more [here](https://github.com/ember-cli/ember-cli/blob/b12e0023dc653316f68aa58b9bb14ade3037e9e6/lib/models/addon.js#L38)","content_text":"Below you'll find information relevant to creating addons in EmberCLI.  Addons are a powerful abstraction in EmberCLI.  These addons help enable the extension of Ember applications built with the framework.  EmberCLI has several generators that make creating addons simple, but knowing where to put your addon specific customizations can be difficult.  This post will attempt to list all known addon hooks as of EmberCLI version 0.1.4.\n\nTable of Contents:\n\n\nconfig\nbluprintsPath\nincludedCommands\nserverMiddleware\npostBuild\npreBuild\nincluded\npostProcess \ntreeFor\n\n\ntreeForApp\ntreeForStyles\ntreeForTemplates\ntreeForAddon\ntreeForVendor\ntreeForTestSupport\ntreeForPublic\n\n\n\nFor each hook we'll cover the following (if applicable):\n\n\nReceived arguments\nSource\nDefault implementation\nUses\nExamples\n\n\nCompendium is largely based of a talk by @rwjblue which can be found here\n\n\nConfig\n\nAugments the applications configuration settings.  Object returned from this hook is merged with the application's configuration object.  Application's configuration always take precedence.\n\nReceived arguments:\n\n\nenv - name of current environment (ie \"developement\")\nbaseConfig - Initial application config\n\n\nSource: lib/models/addon.js:312\n\nDefault implementation:\nAddon.prototype.config = function (env, baseConfig) {\n  var configPath = path.join(this.root, 'config', 'environment.js');\n\n  if (fs.existsSync(configPath)) {\n    var configGenerator = require(configPath);\n\n    return configGenerator(env, baseConfig);\n  }\n};\n\nUses:\n\n\nModifying configuration options (see list of defaults here)\n\n\nFor example\nminifyJS\nstoreConfigInMeta\nes3Safe\net, al\n\n\n\nExamples:\n\n\nSetting storeConfigInMeta to false in ember-cli-rails-addon\n\n\n\nblueprintsPath\n\nTells the application where your blueprints exist.\n\nReceived arguments: None\n\nSource: lib/models/addon.js:304\n\nDefault implementation:\nAddon.prototype.blueprintsPath = function() {\n  var blueprintPath = path.join(this.root, 'blueprints');\n\n  if (fs.existsSync(blueprintPath)) {\n    return blueprintPath;\n  }\n};\n\nUses:\n\n\nLet application know where blueprints exists.\n\n\nExamples:\n\n\nember-cli-coffeescript\n\n\n\nincludedCommands\n\nAllows the specification of custom addon commands.  Expects you to return an object whose key is the name of the command and value is the command instance.\n\nReceived arguments: None\n\nSource: lib/models/project.js:234\n\nDefault implementation: None\n\nUses:\n\n\nInclude custom commands into consuming application\n\n\nExamples:\n\n\nember-cli-cordova\n\n  // https://github.com/rwjblue/ember-cli-divshot/blob/v0.1.6/index.js\n  includedCommands: function() {\n    return {\n      'divshot': require('./lib/commands/divshot')\n    }\n  }\n\n\nserverMiddleware\n\nDesigned to manipulate requests in development mode.\n\nReceived arguments:\n  - options (eg express_instance, project, watcher, environment)\n\nSource: lib/tasks/server/express-server.js:63\n\nDefault implementation: None\n\nUses:\n\n\nTacking on headers to each request\nModifying the request object\n\n\nNote: that this should only be used in development, and if you need the same behavior in production you'll need to configure your server.\n\nExamples:\n\n\nember-cli-content-security-policy\nhistory-support-addon\n\n\n\npostBuild\n\nGives access to the result of the tree, and the location of the output.\n\nReceived arguments:\n\n\nResult object from broccoli build\n\n\nresult.directory - final output path\n\n\n\nSource: lib/models/builder.js:111\n\nDefault implementation: None\n\nUses:\n\n\nSlow tree listing\nMay be used to manipulate your project after build has happened\nOpportunity to symlink or copy files elsewhere.\n\n\nExamples:\n\n\nember-cli-rails-addon\n\n\nIn this case we are using this in tandem with a rails middleware to remove a lock file.  This allows our ruby gem to block incoming requests until after the build happens reliably.\n\n\n\n\npreBuild\n\nHook called before build takes place.\n\nReceived arguments:\n\nSource: lib/models/builder.js:114\n\nDefault implementation: None\n\nUses:\n\nExamples:\n\n\nember-cli-rails-addon\n\n\nIn this case we are using this in tandem with a rails middleware to create a lock file.\n[See postBuild]\n\n\n\n\nincluded\n\nUsually used to import assets into the application.\n\nReceived arguments:\n\n\nEmberApp instance see ember-app.js\n\n\nSource: lib/broccoi/ember-app.js:216\n\nDefault implementation: None\n\nUses:\n\n\nincluding vendor files\nsetting configuration options\n\n\nNote: Any options set in the consuming application will override the addon.\n\nExamples:\n// https://github.com/yapplabs/ember-colpick/blob/master/index.js\nincluded: function colpick_included(app) {\n  this._super.included(app);\n\n  var colpickPath = path.join(app.bowerDirectory, 'colpick');\n\n  this.app.import(path.join(colpickPath, 'js',  'colpick.js'));\n  this.app.import(path.join(colpickPath, 'css', 'colpick.css'));\n}\n\n\nember-cli-rails-addon\n\n\n\npostProcess\n\nReceived arguments:\n\n\npost processing type (eg all)\nreceives tree after build\n\n\nSource: lib/broccoli/ember-app.js:251\n\nDefault implementation: None\n\nUses:\n\n\nfingerprint assets\nrunning processes after build but before toTree\n\n\nExamples:\n\n\nbroccoli-asset-rev\n\n\n\ntreeFor\n\nReturn value is merged with application tree of same type\n\nReceived arguments:\n\n\nreturns given type of tree (eg app, vendor, bower)\n\n\nSource: lib/broccoli/ember-app.js:240\n\nDefault implementation:\nAddon.prototype.treeFor = function treeFor(name) {\n  this._requireBuildPackages();\n\n  var tree;\n  var trees = [];\n\n  if (tree = this._treeFor(name)) {\n    trees.push(tree);\n  }\n\n  if (this.isDevelopingAddon() \u0026amp;\u0026amp; this.app.hinting \u0026amp;\u0026amp; name === 'app') {\n    trees.push(this.jshintAddonTree());\n  }\n\n  return this.mergeTrees(trees.filter(Boolean));\n};\n\nUses:\n\n\nmanipulating trees at build time\n\n\nExamples:\n\n\ntreeFor (cont...)\n\nInstead of overriding treeFor and acting only if the tree you receive matches the one you need EmberCLI has custom hooks for the following Broccoli trees\n\n\ntreeForApp\ntreeForStyles\ntreeForTemplates\ntreeForAddon\ntreeForVendor\ntreeForTestSupport\ntreeForPublic\n\n\nSee more here\n","summary":"Below you'll find information relevant to creating addons in EmberCLI.  Addons are a powerful abstraction in EmberCLI.  These addons help enable the extension of Ember applications built with the framework.  EmberCLI has several generators that make creating addons simple, but knowing where to put your addon specific customizations can be difficult.  This post will attempt to list all known addon hooks as of EmberCLI version 0.1.4.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/211/6713554905_51817878f7_o.jpg","date_published":"2014-12-22T09:00:00-05:00","data_modified":"2025-10-10T15:19:20-04:00","author":{"name":"Jonathan Jackson","url":"https://hashrocket.com/team/jonathan-jackson","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/76/jonathan-jackson.jpg"},"tags":["Javascript","Ember"]},{"id":"https://hashrocket.com/blog/posts/rich-ui-prototyping-with-stagehand","url":"https://hashrocket.com/blog/posts/rich-ui-prototyping-with-stagehand","title":"Rich UI Prototyping with Stagehand","content_html":"I'm a huge proponent of increasing designer/developer communication and easing the handoff between static and implemented markup – and Stagehand is my latest attempt to bridge the gap.\n\n## Prototype UI without writing Javascript.\r\n\r\nStagehand is a jQuery plugin that allows non-developer-types to simulate the states of a page simply by adding a few data attributes. For example, if a designer or frontend dev needs to slice a search page into HTML/CSS, they can easily slice the [blank slate](http://gettingreal.37signals.com/ch09_The_Blank_Slate.php), search results, and 'no results' messaging all in one view. Then, using the Stagehand toolbar, anyone visiting the static markup of the Search page can flip through the various states:\r\n\r\n```haml\r\n%section(data-stage='search' data-scene='blank slate')\r\n  %h1 Search for something!\r\n\r\n%section(data-stage='search' data-scene='results'\r\n  // cool search result listing\r\n\r\n%section(data-stage='search' data-scene='no results')\r\n  %h1 No results for this query\r\n  = link_to \"try searching for something else\", \"#\"\r\n```\r\n\r\nThat's just the tip of the iceberg. In the month since I released Stagehand, it's been used for toolbars, nav dropdowns, dynamic forms, and more. It's a new way to think about mocking interfaces – one that's resulted in saved time and increased markup quality for our projects.\r\n\r\nA solution like Stagehand was a long time coming. We've tried a number of different solutions over the years in efforts to figure out where to draw the line between simulating \u0026 implementing Javascript interactions:\r\n\r\n## In which we stub out AJAX calls.\r\n\r\nBack in the day, slicing AJAX features just involved setting the code up to call a dummy URL, which would then be swapped out for a real call in the real app view. We'd find ourselves with broken static markup as the Javascript was implemented for the real app, but we treated that as a necessary tradeoff for how convenient the URL-swapping method was.\r\n\r\nTo make matters worse, as client-side interfaces became more complex and frameworks (Backbone, Ember, etc.) became more common, we'd find unexpected layout bugs that were either deep within the implemented app or just plain hard to reproduce.\r\n\r\n## In which we double down on Javascript files.\r\n\r\nWe've also tried maintaining a separate Javascript file just for the static markup, full of quick \u0026 easy jQuery toggles and animations. But as projects grew in complexity, the dummy JS would grow as well, resulting a few hundred lines of essentially throwaway code – plus, we ran the risk of making Javascript implementation decisions prior to the implementation of the framework that'd be tying everything together. Unhealthy!\r\n\r\n## Stagehand: decoupled and happy.\r\n\r\nEnter Stagehand. Now there can be a bare, bare minimum of JavaScript to go with our static markup, and interface states are simulated with Stagehand data attributes. Broken JavaScript is a thing of the past, and no throwaway JavaScript is written.\r\n\r\nHonestly, it was hard for me to let go after years of the Hashrocket design \u0026 frontend team being responsible for core of the Javascript interactions along with the HAML/SASS. But thanks to Stagehand, I find myself focusing on designing \u0026 slicing every possible interface state, because it's so easy to write them all at once. It's helped me catch layout issues sooner and reduces the amount of assumptions the developers need to make when implementing views.\r\n\r\nAnd I'm actively avoiding writing JavaScript, leaving 100% of the implementation to our capable team of developers, who've quickly and capably embraced JS \u0026 Coffeescript alongside Ruby, Elixir, Go and all the other stuff we're writing around here. As the nature of applications has changed, we must adapt – and it's my hope that Stagehand is the next step that we've been looking for.\r\n\r\nThat's a bit of how Stagehand has changed our process for the better – maybe it can do the same for you. Check out [the Stagehand homepage](http://camerond.github.io/stagehand/) for documentation \u0026 more examples – and then go forth and slice!","content_text":"I'm a huge proponent of increasing designer/developer communication and easing the handoff between static and implemented markup – and Stagehand is my latest attempt to bridge the gap.\nPrototype UI without writing Javascript.\n\nStagehand is a jQuery plugin that allows non-developer-types to simulate the states of a page simply by adding a few data attributes. For example, if a designer or frontend dev needs to slice a search page into HTML/CSS, they can easily slice the blank slate, search results, and 'no results' messaging all in one view. Then, using the Stagehand toolbar, anyone visiting the static markup of the Search page can flip through the various states:\n%section(data-stage='search' data-scene='blank slate')\n  %h1 Search for something!\n\n%section(data-stage='search' data-scene='results'\n  // cool search result listing\n\n%section(data-stage='search' data-scene='no results')\n  %h1 No results for this query\n  = link_to \"try searching for something else\", \"#\"\n\nThat's just the tip of the iceberg. In the month since I released Stagehand, it's been used for toolbars, nav dropdowns, dynamic forms, and more. It's a new way to think about mocking interfaces – one that's resulted in saved time and increased markup quality for our projects.\n\nA solution like Stagehand was a long time coming. We've tried a number of different solutions over the years in efforts to figure out where to draw the line between simulating \u0026amp; implementing Javascript interactions:\nIn which we stub out AJAX calls.\n\nBack in the day, slicing AJAX features just involved setting the code up to call a dummy URL, which would then be swapped out for a real call in the real app view. We'd find ourselves with broken static markup as the Javascript was implemented for the real app, but we treated that as a necessary tradeoff for how convenient the URL-swapping method was.\n\nTo make matters worse, as client-side interfaces became more complex and frameworks (Backbone, Ember, etc.) became more common, we'd find unexpected layout bugs that were either deep within the implemented app or just plain hard to reproduce.\nIn which we double down on Javascript files.\n\nWe've also tried maintaining a separate Javascript file just for the static markup, full of quick \u0026amp; easy jQuery toggles and animations. But as projects grew in complexity, the dummy JS would grow as well, resulting a few hundred lines of essentially throwaway code – plus, we ran the risk of making Javascript implementation decisions prior to the implementation of the framework that'd be tying everything together. Unhealthy!\nStagehand: decoupled and happy.\n\nEnter Stagehand. Now there can be a bare, bare minimum of JavaScript to go with our static markup, and interface states are simulated with Stagehand data attributes. Broken JavaScript is a thing of the past, and no throwaway JavaScript is written.\n\nHonestly, it was hard for me to let go after years of the Hashrocket design \u0026amp; frontend team being responsible for core of the Javascript interactions along with the HAML/SASS. But thanks to Stagehand, I find myself focusing on designing \u0026amp; slicing every possible interface state, because it's so easy to write them all at once. It's helped me catch layout issues sooner and reduces the amount of assumptions the developers need to make when implementing views.\n\nAnd I'm actively avoiding writing JavaScript, leaving 100% of the implementation to our capable team of developers, who've quickly and capably embraced JS \u0026amp; Coffeescript alongside Ruby, Elixir, Go and all the other stuff we're writing around here. As the nature of applications has changed, we must adapt – and it's my hope that Stagehand is the next step that we've been looking for.\n\nThat's a bit of how Stagehand has changed our process for the better – maybe it can do the same for you. Check out the Stagehand homepage for documentation \u0026amp; more examples – and then go forth and slice!\n","summary":"I'm a huge proponent of increasing designer/developer communication and easing the handoff between static and implemented markup – and Stagehand is my latest attempt to bridge the gap.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/159/stage.jpg","date_published":"2014-01-13T09:00:00-05:00","data_modified":"2014-01-08T10:35:30-05:00","author":{"name":"Cameron Daigle","url":"https://hashrocket.com/team/cameron-daigle","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/3/cameron-daigle.jpg"},"tags":["Javascript","UI","Design"]},{"id":"https://hashrocket.com/blog/posts/setting-up-an-ember-app-with-a-rails-backend","url":"https://hashrocket.com/blog/posts/setting-up-an-ember-app-with-a-rails-backend","title":"Setting up an Ember App with a Rails Backend","content_html":"**Update: June 26, 2014**\r\n\r\nThis tutorial is now out of date, and you should instead look at [Vic Ramon's Ember Tutorial](http://ember.vicramon.com) for a tutorial introducing Ember with Rails. \r\n\r\n**Update: November 8th, 2013**\r\n\r\nSome of the instructions below are out of date, particularly the code in the store.js file. Please look at the [Ember Data Rails Example](https://github.com/vicramon/ember-data-rails-example) that I have posted to Github for correct, updated code. This app can be viewed [live on Heroku](http://ember-data-rails-example.herokuapp.com).\r\n\r\n-------------------\r\n\r\nToday I'm going to show you how to setup an Ember app with a Rails backend. The process is relatively straightforward, but . there are some gotchas that I'd like to help you avoid. \r\n\r\nThe app will use Haml, CoffeeScript, [Emblem](http://emblemjs.com), and Ember Data. In this tutorial I'll be starting a sample app called Launch Bay. Launch Bay would essentially be an ultra-lightweight version of Pivotal Tracker. In this first post I'm just going to get my app setup to receive data.\n\n## Basic App Setup\r\n\r\nLet's hit the ground running with a new app\r\n\r\n````bash\r\nrails new launchbay  -d postgresql\r\n\r\n````\r\n\r\nSetup your database and rvm gemset, and make sure to include these in your Gemfile:\r\n\r\n````ruby\r\ngem 'coffee-rails', '~\u003e 4.0.0'\r\ngem 'ember-rails'\r\ngem 'ember-source'\r\ngem 'emblem-rails'\r\ngem 'haml-rails'\r\n````\r\n\r\nOpen your configs and add the following:\r\n\r\n````ruby\r\n# environments/production.rb\r\nconfig.ember.variant = :production\r\n````\r\n\r\n````ruby\r\n# environments/development.rb\r\nconfig.ember.variant = :development\r\n````\r\n\r\n````ruby\r\n# environments/test.rb\r\nconfig.ember.variant = :development\r\n````\r\n\r\n## Backend Setup\r\n\r\nOk, now we've got the basics we need to get our Rails backend up and running. Our app is just going to show a list of stories. For the sake of simplicity these won't be scoped to a particular user or project.\r\n\r\nLet's create a stories table. \r\n\r\n````ruby\r\nrails g migration create_stories name:string body:text\r\nrake db:migrate\r\n````\r\n\r\nWe'll also need a story model, controller, and serializer. \r\n\r\n````ruby\r\n# app/models/story.rb\r\n\r\nclass Story \u003c ActiveRecord::Base\r\nend\r\n```\r\n\r\n````ruby\r\n# app/serializers/story_serializer.rb\r\n\r\nclass StorySerializer \u003c ActiveModel::Serializer\r\n  attributes :name, :body\r\nend\r\n````\r\n\r\nI'm going to create a versioned api for the controller just in case I want to change things later:\r\n\r\n````ruby\r\n# config/routes.rb\r\n\r\nnamespace :api do\r\n  namespace :v1 do\r\n      resources :stories, only: :index\r\n  end\r\nend\r\n````\r\n\r\nThis controller is going to accept json so that we can interact with our Ember app.\r\n\r\n````ruby\r\n # app/controllers/api/v1/stories_controller.rb\r\n\r\nclass Api::V1::StoriesController \u003c ApplicationController\r\n  respond_to :json\r\n\r\n  def index\r\n    respond_with Story.all\r\n  end\r\n\r\n  private\r\n\r\n  def story_params\r\n    params.require(:story).permit(:name, :body)\r\n  end\r\nend\r\n````\r\n\r\nTo see if this is all setup properly spin up your server and open a rails console. First let's seed the database:\r\n\r\n````bash\r\n$ rails console\r\n$ Story.create(name: 'User views a list of stories', body: 'Given I am a user \u003cbr /\u003e When I visit the stories index \u003cbr /\u003e Then I should see a list of stories')\r\n````\r\n\r\nNow hit the api controller and you should get your one story back as json: `http://localhost:3000/api/v1/stories.json`\r\n\r\n## Providing an Outlet for Ember\r\n\r\nWe need to have a place for our Ember app to actually show up, so let's do that now. I am going to create a generic home controller for now.\r\n\r\n````ruby\r\n# config/routes.rb\r\n\r\nroot to: 'home#index'\r\n````\r\n\r\n````ruby\r\n# app/controllers/home_controller.rb\r\n\r\nclass HomeController \u003c ApplicationController\r\nend\r\n````\r\n\r\nNow give Ember an outlet in the view:\r\n\r\n````haml\r\n# app/views/home/index.html.haml\r\n\r\n%script{ type: 'text/x-handlebars' }\r\n  {{ outlet }}\r\n````\r\n\r\nWe also need a Rails layout:\r\n\r\n````ruby\r\n# app/views/application.html.haml\r\n\r\n!!!\r\n%html(lang=\"en-US\")\r\n  %head\r\n    %title Launch Bay\r\n    = stylesheet_link_tag \"application\"\r\n    = javascript_include_tag \"application\"\r\n    = csrf_meta_tags\r\n  %body\r\n    = yield\r\n```\r\n\r\n## Setting up Ember Internals\r\n\r\nFirst, run the generator provided by ember-rails:\r\n\r\n````bash\r\nrails g ember:bootstrap -g --javascript-engine coffee -n App\r\n````\r\n\r\nNow restart your Rails server and hit `localhost:3000`. You should see a blank page. Open your developer console and you should see output like this:\r\n\r\n````\r\nDEBUG: ------------------------------- \r\nDEBUG: Ember.VERSION : 1.0.0\r\nDEBUG: Handlebars.VERSION : 1.0.0 \r\nDEBUG: jQuery.VERSION : 1.10.2 \r\nDEBUG: ------------------------------- \r\n````\r\n\r\nFor more advanced debugging, check out the [Ember Inspector for Google Chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)\r\n\r\n## Setting Up Ember Data\r\n\r\nWe'll be using Ember Data to handle client-server communication. \r\n\r\nThe Ember-rails generator created a store.js file. Open it and remove everything, and just add the following:\r\n\r\n````javascript\r\n# app/javascripts/store.js\r\n\r\nDS.RESTAdapter.reopen\r\n  namespace: 'api/v1'\r\n````\r\n\r\n## Ember Models\r\n\r\nNow to create the actual story model:\r\n\r\n````coffeescript\r\n# app/assets/javascripts/models/story.js.coffee\r\n\r\nApp.Story = DS.Model.extend\r\n  name: DS.attr('string')\r\n  body: DS.attr('string')\r\n````\r\n\r\n## Ember Routes\r\n\r\n`app/assets/javascripts/router.js` contains our top-level routes. \r\n\r\nOpen that file, change it to Coffeescript, then add this:\r\n\r\n````coffeescript\r\n# app/assets/javascripts/router.js.coffee\r\n\r\nApp.Router.map ()-\u003e\r\n  @resource 'stories'\r\n````\r\n\r\nNote that `resource` is singular, in contrast to Rails.\r\n\r\n## Ember Template\r\n\r\nCreate the following file:\r\n\r\n````\r\n# app/assets/javascripts/templates/stories.js.emblem\r\n\r\n| Hello world.\r\n````\r\n\r\nSave that, now head over to `http://localhost:3000/#/stories`. You should see your message on the page. \r\n\r\nThere a few things to note. \r\n\r\n1. Use a pipe to tell emblem that \"Hello\" is text and not an html tag. \r\n\r\n2. We created a file named `stories` in the templates folder. This works right now because the stories route has no subroutes. If you added a subroute to the stories resource, then this template would need to be moved to templates/stories/index.emblem.js.\r\n\r\n3.  We didn't need to create a stories controller or stories route to make this work. Ember will create those in memory for us if we don't explicitly create them. \r\n\r\n## Pulling in real data\r\n\r\nOk, we've got text showing up on the page, but we need to actually get data out of our database.To do that we need to bind data to the route, and we do that by creating a stories route. \r\n\r\n````coffeescript\r\n# app/assets/javascripts/routes/stories.js.coffee\r\n\r\nApp.StoriesRoute = Ember.Route.extend\r\n  model: -\u003e\r\n    @get('store').findAll('story')\r\n````\r\n\r\nOpen the template back up and add the following:\r\n\r\n````\r\n# app/assets/javascripts/templates/stories.js.emblem\r\n\r\nh1 Story Listing\r\n\r\n= each story in controller\r\n  h2= story.name\r\n  | {{{story.body}}} \r\n````\r\n\r\nOpen up `http://localhost:3000/#/stories` and you should see the story you put in the database. \r\n\r\nI'm doing `{{{story.body}}}` to prevent the html line breaks from being escaped.\r\n\r\nNotice that I am referring to the array of stories in the template as `controller`. That's because the data is bound do the Stories controller (which is still in memory). \r\n\r\n## Wrap Up\r\n\r\nThat's it for app setup with Rails, Ember, and Ember Data. If you were to continue with this project the next step would be to get project models setup, then nest stories under projects in the Ember router. I hope this was helpful. ","content_text":"Update: June 26, 2014\n\nThis tutorial is now out of date, and you should instead look at Vic Ramon's Ember Tutorial for a tutorial introducing Ember with Rails. \n\nUpdate: November 8th, 2013\n\nSome of the instructions below are out of date, particularly the code in the store.js file. Please look at the Ember Data Rails Example that I have posted to Github for correct, updated code. This app can be viewed live on Heroku.\n\n\n\nToday I'm going to show you how to setup an Ember app with a Rails backend. The process is relatively straightforward, but . there are some gotchas that I'd like to help you avoid. \n\nThe app will use Haml, CoffeeScript, Emblem, and Ember Data. In this tutorial I'll be starting a sample app called Launch Bay. Launch Bay would essentially be an ultra-lightweight version of Pivotal Tracker. In this first post I'm just going to get my app setup to receive data.\nBasic App Setup\n\nLet's hit the ground running with a new app\nrails new launchbay  -d postgresql\n\n\nSetup your database and rvm gemset, and make sure to include these in your Gemfile:\ngem 'coffee-rails', '~\u0026gt; 4.0.0'\ngem 'ember-rails'\ngem 'ember-source'\ngem 'emblem-rails'\ngem 'haml-rails'\n\nOpen your configs and add the following:\n# environments/production.rb\nconfig.ember.variant = :production\n# environments/development.rb\nconfig.ember.variant = :development\n# environments/test.rb\nconfig.ember.variant = :development\nBackend Setup\n\nOk, now we've got the basics we need to get our Rails backend up and running. Our app is just going to show a list of stories. For the sake of simplicity these won't be scoped to a particular user or project.\n\nLet's create a stories table. \nrails g migration create_stories name:string body:text\nrake db:migrate\n\nWe'll also need a story model, controller, and serializer. \n# app/models/story.rb\n\nclass Story \u0026lt; ActiveRecord::Base\nend\n```\n\n````ruby\n# app/serializers/story_serializer.rb\n\nclass StorySerializer \u0026lt; ActiveModel::Serializer\n  attributes :name, :body\nend\n\nI'm going to create a versioned api for the controller just in case I want to change things later:\n# config/routes.rb\n\nnamespace :api do\n  namespace :v1 do\n      resources :stories, only: :index\n  end\nend\n\nThis controller is going to accept json so that we can interact with our Ember app.\n # app/controllers/api/v1/stories_controller.rb\n\nclass Api::V1::StoriesController \u0026lt; ApplicationController\n  respond_to :json\n\n  def index\n    respond_with Story.all\n  end\n\n  private\n\n  def story_params\n    params.require(:story).permit(:name, :body)\n  end\nend\n\nTo see if this is all setup properly spin up your server and open a rails console. First let's seed the database:\n$ rails console\n$ Story.create(name: 'User views a list of stories', body: 'Given I am a user \u0026lt;br /\u0026gt; When I visit the stories index \u0026lt;br /\u0026gt; Then I should see a list of stories')\n\nNow hit the api controller and you should get your one story back as json: http://localhost:3000/api/v1/stories.json\nProviding an Outlet for Ember\n\nWe need to have a place for our Ember app to actually show up, so let's do that now. I am going to create a generic home controller for now.\n# config/routes.rb\n\nroot to: 'home#index'\n# app/controllers/home_controller.rb\n\nclass HomeController \u0026lt; ApplicationController\nend\n\nNow give Ember an outlet in the view:\n# app/views/home/index.html.haml\n\n%script{ type: 'text/x-handlebars' }\n  {{ outlet }}\n\nWe also need a Rails layout:\n# app/views/application.html.haml\n\n!!!\n%html(lang=\"en-US\")\n  %head\n    %title Launch Bay\n    = stylesheet_link_tag \"application\"\n    = javascript_include_tag \"application\"\n    = csrf_meta_tags\n  %body\n    = yield\n```\n\n## Setting up Ember Internals\n\nFirst, run the generator provided by ember-rails:\n\n````bash\nrails g ember:bootstrap -g --javascript-engine coffee -n App\n\nNow restart your Rails server and hit localhost:3000. You should see a blank page. Open your developer console and you should see output like this:\nDEBUG: ------------------------------- \nDEBUG: Ember.VERSION : 1.0.0\nDEBUG: Handlebars.VERSION : 1.0.0 \nDEBUG: jQuery.VERSION : 1.10.2 \nDEBUG: ------------------------------- \n\nFor more advanced debugging, check out the Ember Inspector for Google Chrome\nSetting Up Ember Data\n\nWe'll be using Ember Data to handle client-server communication. \n\nThe Ember-rails generator created a store.js file. Open it and remove everything, and just add the following:\n# app/javascripts/store.js\n\nDS.RESTAdapter.reopen\n  namespace: 'api/v1'\nEmber Models\n\nNow to create the actual story model:\n# app/assets/javascripts/models/story.js.coffee\n\nApp.Story = DS.Model.extend\n  name: DS.attr('string')\n  body: DS.attr('string')\nEmber Routes\n\napp/assets/javascripts/router.js contains our top-level routes. \n\nOpen that file, change it to Coffeescript, then add this:\n# app/assets/javascripts/router.js.coffee\n\nApp.Router.map ()-\u0026gt;\n  @resource 'stories'\n\nNote that resource is singular, in contrast to Rails.\nEmber Template\n\nCreate the following file:\n# app/assets/javascripts/templates/stories.js.emblem\n\n| Hello world.\n\nSave that, now head over to http://localhost:3000/#/stories. You should see your message on the page. \n\nThere a few things to note. \n\n\nUse a pipe to tell emblem that \"Hello\" is text and not an html tag. \nWe created a file named stories in the templates folder. This works right now because the stories route has no subroutes. If you added a subroute to the stories resource, then this template would need to be moved to templates/stories/index.emblem.js.\nWe didn't need to create a stories controller or stories route to make this work. Ember will create those in memory for us if we don't explicitly create them. \n\nPulling in real data\n\nOk, we've got text showing up on the page, but we need to actually get data out of our database.To do that we need to bind data to the route, and we do that by creating a stories route. \n# app/assets/javascripts/routes/stories.js.coffee\n\nApp.StoriesRoute = Ember.Route.extend\n  model: -\u0026gt;\n    @get('store').findAll('story')\n\nOpen the template back up and add the following:\n# app/assets/javascripts/templates/stories.js.emblem\n\nh1 Story Listing\n\n= each story in controller\n  h2= story.name\n  | {{{story.body}}} \n\nOpen up http://localhost:3000/#/stories and you should see the story you put in the database. \n\nI'm doing {{{story.body}}} to prevent the html line breaks from being escaped.\n\nNotice that I am referring to the array of stories in the template as controller. That's because the data is bound do the Stories controller (which is still in memory). \nWrap Up\n\nThat's it for app setup with Rails, Ember, and Ember Data. If you were to continue with this project the next step would be to get project models setup, then nest stories under projects in the Ember router. I hope this was helpful. \n","summary":"Update: June 26, 2014\n\nThis tutorial is now out of date, and you should instead look at Vic Ramon's Ember Tutorial for a tutorial introducing Ember with Rails. \n\nUpdate: November 8th, 2013\n\nSome of the instructions below are out of date, particularly the code in the store.js file. Please look at the Ember Data Rails Example that I have posted to Github for correct, updated code. This app can be viewed live on Heroku.\n\n\n\nToday I'm going to show you how to setup an Ember app with a Rails backend. The process is relatively straightforward, but . there are some gotchas that I'd like to help you avoid. \n\nThe app will use Haml, CoffeeScript, Emblem, and Ember Data. In this tutorial I'll be starting a sample app called Launch Bay. Launch Bay would essentially be an ultra-lightweight version of Pivotal Tracker. In this first post I'm just going to get my app setup to receive data.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/139/ember-rails-backend.png","date_published":"2013-09-15T20:00:00-04:00","data_modified":"2025-10-10T15:40:25-04:00","author":{"name":"Vic Ramon","url":"https://hashrocket.com/team/vic-ramon","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/77/vic-ramon.jpg"},"tags":["Ruby","Ruby on Rails","Javascript","Ember","Development"]},{"id":"https://hashrocket.com/blog/posts/anatomy-of-the-new-hashrocket-frontend-design","url":"https://hashrocket.com/blog/posts/anatomy-of-the-new-hashrocket-frontend-design","title":"Anatomy of the new Hashrocket: Frontend Design","content_html":"As you may have noticed, we launched a new version of the Hashrocket site recently. This new design is a ground-up redesign \u0026 rewrite of the frontend – much of the backend was rewritten as well, but I'm a design guy, so if you want a backend breakdown, you'll have to buy Polito an ice cream cake or something. Here's some cool stuff about the frontend.\n\n## Liquidity\r\n\r\nThe Hashrocket site is designed to look good on a continuous spectrum from 1200px wide down to 320px. We don't target specific devices or widths – responsive sites are about making sure a site looks good at any size.\r\n\r\nBase styles are designed for the widest viewport, and additional styles override those as the site gets narrower. We've found it's much more straightforward to design first for more complex wide layouts and then remove attributes (columns, for example) as the page becomes narrower than vice versa.\r\n\r\nYou can learn more about our patterns for responsive design in [this previous post of mine](http://hashrocket.com/blog/posts/using-sass-to-be-responsive-and-retina-ready), but the tl;dr of it all is that you should design wide, override narrow. \r\n\r\n## Retina Images\r\n\r\nOnly certain images on the new Hashrocket site are doubled up for retina: the header logo, for one, because I wanted to make sure it was sharp and not being resized by the browser. So the logo maintains its size no matter the width of the device, and the spacing around it tightens up as the window gets narrower.\r\n\r\nThe open source icons on the [Community page](http://hashrocket.com/community) and contact forms are doubled for the same reason – and the social icons in our footer \u0026 people page are simply [SymbolSet](http://symbolset.com) characters.\r\n\r\nAll other images on the site are not doubled up for retina. For example, each illustration on [the Process page](http://hashrocket.com/process) is in a column set to a width of 25% (actually 23.5%, thanks to additional margin) – so even though the images themselves are 360x190, even at that page's widest point (1200px), they're only rendered at 282x149. We found that was enough breathing room for the images to look nice in retina as well as all browser sizes.\r\n\r\nThe same sort of thing applies to the slideshows on the client detail pages and homepage. The homepage features a slideshow where the images are actually 1200x600 despite being rendered at 862x431 at their largest non-retina size – a compromise to allow for decent file sizes without depending on a second set of slideshow images loaded through a Javascript call or media query.\r\n\r\nI think this will happen more and more as retina becomes the standard: it will be more important for images to have exact proportions rather than exact pixel sizes, because pixel accuracy won't be as much of a concern.\r\n\r\n## Fun with `background-size`\r\n\r\nThe header backgrounds on our site (for example, the astronauts on [the team page](https://hashrocket.com/team)) are all being displayed smaller than native, and thanks to `background-size`, they always fit the content area properly.\r\n\r\nThis took a few tries to get right. The height of that header area is dependent upon the content, and the width is obviously variable all the way down to mobile, so multiple size-specific background images would be a pain (and result in jarring transitions as you resize your browser window) – and fixed-pixel background sizes would be inconvenient to maintain.\r\n\r\nThe solution was actually far simpler than I initially realized. The header section just has a background image with a `background-size` of `auto 90%` – that is, `auto` width, `90%` of height. So as the page gets narrower, the background image gets smaller, and if the text gets longer, the background image grows to fit. Piece of cake, actually – it just took some getting used to to think of a background image as such a fluid element.\r\n\r\n## The Cycle2 Plugin\r\n\r\nOur slideshows are courtesy of the indomitable [Cycle2](http://www.malsup.com/jquery/cycle2/) plugin, combined with the [Carousel](http://www.malsup.com/jquery/cycle2/download/) transition plugin. The original Cycle plugin (contributed to by our own Shane Riley) was solid and used often on our projects, but Cycle2 adds the ability to handle varying sizes, and does so very easily.\r\n\r\nHere's how that works on our homepage. First, we have a couple of divs enclosing a set of images:\r\n\r\n```haml\r\n.slides\r\n  .pano\r\n    - (1..12).to_a.shuffle.each do |i|\r\n      = image_tag \"img_home_#{i}.jpg\"\r\n```\r\n\r\nWe call the cycle plugin. There's a little more going on here (events, a little cuteness to center the first slide), but this is the main initialization:\r\n\r\n```javascript\r\nthis.$el.cycle({\r\n  fx: \"carousel\",\r\n  paused: true,\r\n  carouselVisible: 3,\r\n  carouselFluid: true,\r\n  swipe: true\r\n});\r\n```\r\n\r\nHere's the trick. The carousel plugin has a `carouselVisible` parameter that controls how many slides are shown at once. This is how it calculates the height of the carousel – we don't set that ourselves. However, we don't want 3 whole slides visible – we just want the edges of the left \u0026 right slides to be visible. So, we do this:\r\n\r\n```sass\r\n.slides\r\n  overflow: hidden\r\n  .pano\r\n    margin: 0 -40%\r\n```\r\n\r\nThe negative margin on the `.pano` accomplishes what we want. The Cycle2 plugin is still sizing for 3 slides, but we're just letting those bleed off of the edges.\r\n\r\nAdditionally, at a certain width, we don't want to show the edges of the left \u0026 right slides anymore. So, using the media query mixin detailed in [my aforementioned post about that](http://hashrocket.com/blog/posts/using-sass-to-be-responsive-and-retina-ready):\r\n\r\n```sass\r\n+max-width\r\n  .slides\r\n    .pano\r\n    margin: 0 -100%\r\n```\r\n\r\nVoila! Clean and nice.\r\n\r\n## Etc.\r\n\r\nSo there you have it. It's really exciting to have launched something that I consider to be such a huge step forward from our old site. There are other interesting bits \u0026 pieces out there – I'll leave the implementation of the faux-chart on our homepage as an exercise for the reader – but those were some of the main tricks \u0026 techniques we used. Thanks for reading.","content_text":"As you may have noticed, we launched a new version of the Hashrocket site recently. This new design is a ground-up redesign \u0026amp; rewrite of the frontend – much of the backend was rewritten as well, but I'm a design guy, so if you want a backend breakdown, you'll have to buy Polito an ice cream cake or something. Here's some cool stuff about the frontend.\nLiquidity\n\nThe Hashrocket site is designed to look good on a continuous spectrum from 1200px wide down to 320px. We don't target specific devices or widths – responsive sites are about making sure a site looks good at any size.\n\nBase styles are designed for the widest viewport, and additional styles override those as the site gets narrower. We've found it's much more straightforward to design first for more complex wide layouts and then remove attributes (columns, for example) as the page becomes narrower than vice versa.\n\nYou can learn more about our patterns for responsive design in this previous post of mine, but the tl;dr of it all is that you should design wide, override narrow. \nRetina Images\n\nOnly certain images on the new Hashrocket site are doubled up for retina: the header logo, for one, because I wanted to make sure it was sharp and not being resized by the browser. So the logo maintains its size no matter the width of the device, and the spacing around it tightens up as the window gets narrower.\n\nThe open source icons on the Community page and contact forms are doubled for the same reason – and the social icons in our footer \u0026amp; people page are simply SymbolSet characters.\n\nAll other images on the site are not doubled up for retina. For example, each illustration on the Process page is in a column set to a width of 25% (actually 23.5%, thanks to additional margin) – so even though the images themselves are 360x190, even at that page's widest point (1200px), they're only rendered at 282x149. We found that was enough breathing room for the images to look nice in retina as well as all browser sizes.\n\nThe same sort of thing applies to the slideshows on the client detail pages and homepage. The homepage features a slideshow where the images are actually 1200x600 despite being rendered at 862x431 at their largest non-retina size – a compromise to allow for decent file sizes without depending on a second set of slideshow images loaded through a Javascript call or media query.\n\nI think this will happen more and more as retina becomes the standard: it will be more important for images to have exact proportions rather than exact pixel sizes, because pixel accuracy won't be as much of a concern.\nFun with background-size\n\nThe header backgrounds on our site (for example, the astronauts on the team page) are all being displayed smaller than native, and thanks to background-size, they always fit the content area properly.\n\nThis took a few tries to get right. The height of that header area is dependent upon the content, and the width is obviously variable all the way down to mobile, so multiple size-specific background images would be a pain (and result in jarring transitions as you resize your browser window) – and fixed-pixel background sizes would be inconvenient to maintain.\n\nThe solution was actually far simpler than I initially realized. The header section just has a background image with a background-size of auto 90% – that is, auto width, 90% of height. So as the page gets narrower, the background image gets smaller, and if the text gets longer, the background image grows to fit. Piece of cake, actually – it just took some getting used to to think of a background image as such a fluid element.\nThe Cycle2 Plugin\n\nOur slideshows are courtesy of the indomitable Cycle2 plugin, combined with the Carousel transition plugin. The original Cycle plugin (contributed to by our own Shane Riley) was solid and used often on our projects, but Cycle2 adds the ability to handle varying sizes, and does so very easily.\n\nHere's how that works on our homepage. First, we have a couple of divs enclosing a set of images:\n.slides\n  .pano\n    - (1..12).to_a.shuffle.each do |i|\n      = image_tag \"img_home_#{i}.jpg\"\n\nWe call the cycle plugin. There's a little more going on here (events, a little cuteness to center the first slide), but this is the main initialization:\nthis.$el.cycle({\n  fx: \"carousel\",\n  paused: true,\n  carouselVisible: 3,\n  carouselFluid: true,\n  swipe: true\n});\n\nHere's the trick. The carousel plugin has a carouselVisible parameter that controls how many slides are shown at once. This is how it calculates the height of the carousel – we don't set that ourselves. However, we don't want 3 whole slides visible – we just want the edges of the left \u0026amp; right slides to be visible. So, we do this:\n.slides\n  overflow: hidden\n  .pano\n    margin: 0 -40%\n\nThe negative margin on the .pano accomplishes what we want. The Cycle2 plugin is still sizing for 3 slides, but we're just letting those bleed off of the edges.\n\nAdditionally, at a certain width, we don't want to show the edges of the left \u0026amp; right slides anymore. So, using the media query mixin detailed in my aforementioned post about that:\n+max-width\n  .slides\n    .pano\n    margin: 0 -100%\n\nVoila! Clean and nice.\nEtc.\n\nSo there you have it. It's really exciting to have launched something that I consider to be such a huge step forward from our old site. There are other interesting bits \u0026amp; pieces out there – I'll leave the implementation of the faux-chart on our homepage as an exercise for the reader – but those were some of the main tricks \u0026amp; techniques we used. Thanks for reading.\n","summary":"As you may have noticed, we launched a new version of the Hashrocket site recently. This new design is a ground-up redesign \u0026amp; rewrite of the frontend – much of the backend was rewritten as well, but I'm a design guy, so if you want a backend breakdown, you'll have to buy Polito an ice cream cake or something. Here's some cool stuff about the frontend.\n","image":"https://dkj231ikyz7c1.cloudfront.net/uploads/blog/post/image/133/anatomy-of-the-new-Hashrocket.png","date_published":"2013-08-16T05:30:00-04:00","data_modified":"2016-08-25T12:03:00-04:00","author":{"name":"Cameron Daigle","url":"https://hashrocket.com/team/cameron-daigle","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/3/cameron-daigle.jpg"},"tags":["Javascript","CSS","Design"]},{"id":"https://hashrocket.com/blog/posts/ember-routing-the-when-and-why-of-nesting","url":"https://hashrocket.com/blog/posts/ember-routing-the-when-and-why-of-nesting","title":"Ember Routing: The When and Why of Nesting","content_html":"Understanding the proper use of the Router in an Ember.js app is a fundamental core concept of the framework. The docs tell us that the router translates a URL into a series of _nested_ templates and almost all available tutorials show how to nest routes as part of an explanation of how the Router works. This is great when you want a UI where a list of items are present at the same time a single item is shown. This leaves many beginners struggling, however, as they try and replace the entire contents of a page with another route's template. Today we'll explore two ways to tackle this problem.\n\n## Getting started\r\n\r\nFirst, let's start off with a perfunctory application template.\r\n\r\n```html\r\n\u003cscript type=\"text/x-handlebars\"\u003e\r\n  \u003ch1\u003eMy Ember App\u003c/h1\u003e\r\n\u003c/script\u003e\r\n```\r\n\r\n```js\r\nvar App = Ember.Application.create();\r\n```\r\n\r\nJS Bin example: http://jsbin.com/alewof/2/edit\r\n\r\nOur application is instantiated with one line to Ember.Application.create(); When Ember encounters a Handlebars template without a data-template-name attribute, it will use it by default as the template for the index route and consider it's data-template-name as 'application' if an 'application' template is otherwise not found. Thus Ember will create an app and render this template with little code. It can be helpful to consider this template the application layout.\r\n\r\n## Adding a Products Route, Model and Template\r\n\r\n```html\r\n\u003cscript type=\"text/x-handlebars\"\u003e\r\n  \u003ch1\u003eMy Ember App\u003c/h1\u003e\r\n  {{ outlet }}\r\n\u003c/script\u003e\r\n\r\n\u003cscript type=\"text/x-handlebars\" data-template-name=\"products\"\u003e\r\n  \u003ch2\u003eProducts\u003c/h2\u003e\r\n  {{#each controller}}\r\n    {{ title }}\r\n  {{/each}}\r\n\u003c/script\u003e\r\n```\r\n\r\n```js\r\nvar App = Ember.Application.create();\r\n\r\nApp.Router.map(function() {\r\n  this.resource('products');\r\n});\r\n\r\nApp.Store = DS.Store.extend({\r\n  revision: 13,\r\n  adapter: 'DS.FixtureAdapter'\r\n});\r\n\r\nApp.Product = DS.Model.extend({\r\n  title: DS.attr('string')\r\n});\r\n\r\nApp.Product.FIXTURES = [\r\n  { id: 1, title: 'Rube Goldberg Breakfast-o-Matic' }\r\n];\r\n\r\nApp.ProductsRoute = Ember.Route.extend({\r\n  model: function() {\r\n    return App.Product.find();\r\n  }\r\n});\r\n```\r\n\r\nThis is quite a bit of code at once but if you're acquainted with Ember basics it should look familiar. We've defined an {{ outlet }} in our application template where new templates will render. We've added a 'products' template that iterates the products available in the controller and lists their titles. We define 'products' as a route within App.Router and in our ProductsRoute we set the model property on the route to return all products. The rest of this code is our Product model, fixtures, and establishing our App.Store with ember-data. Additionally, we can automatically redirect to the 'products' route from the root by defining an IndexRoute.\r\n\r\n```js\r\nApp.IndexRoute = Ember.Route.extend({\r\n  redirect: function() {\r\n    this.transitionTo('products');\r\n  }\r\n});\r\n```\r\n\r\nNow when we load our app the products route is activated and we should see the products template rendered.\r\n\r\nJS Bin example: http://jsbin.com/alewof/3/edit\r\n\r\n## Nesting Routes\r\n\r\nAs most tutorials available show, we can nest our routes as follows:\r\n\r\n```js\r\nApp.Router.map(function() {\r\n  this.resource('products', function() {\r\n    this.resource('product', { 'path' : '/:product_id' });\r\n  });\r\n});\r\n```\r\n\r\nThis will look very familiar to Rails developers, and in my opinion this is part of the rub. It seems very natural to nest this resource because the URLs implied match the notion of REST that most Rails developers recognize.\r\n\r\nFrom here we can add links to individual products in our products template:\r\n\r\n```erb\r\n\u003cscript type=\"text/x-handlebars\" data-template-name=\"products\"\u003e\r\n  \u003ch2\u003eProducts\u003c/h2\u003e\r\n  {{#each controller}}\r\n    {{#linkTo 'product' this}}{{ title }}{{/linkTo}}\r\n  {{/each}}\r\n\u003c/script\u003e\r\n```\r\n\r\nAnd correspondingly we can add a minimal product template like so:\r\n\r\n```erb\r\n\u003cscript type=\"text/x-handlebars\" data-template-name=\"product\"\u003e\r\n  \u003ch2\u003eProduct: {{ title }}\u003c/h2\u003e\r\n\u003c/script\u003e\r\n```\r\n\r\nFor the sake of demonstration, if we want our templates to nest just as our routes do we can add an {{ outlet }} to the bottom of our products template. Then when we click the link in the products template the product template will render inside of it.\r\n\r\n```erb\r\n\u003cscript type=\"text/x-handlebars\" data-template-name=\"products\"\u003e\r\n  \u003ch2\u003eProducts\u003c/h2\u003e\r\n  {{#each controller}}\r\n    {{#linkTo 'product' this}}{{ title }}{{/linkTo}}\r\n  {{/each}}\r\n  {{ outlet }}\r\n\u003c/script\u003e\r\n```\r\n\r\nJS Bin example: http://jsbin.com/alewof/4/edit\r\n\r\nWe don't want to do this today, though. Let's look at some other Ember methods available to render templates.\r\n\r\n## Replacing page content with renderTemplate\r\n\r\nWe can define a renderTemplate method inside of an Ember Route object to handle \r\nthe specifics of which template the route will render and where:\r\n\r\n```js\r\nApp.ProductRoute = Ember.Route.extend({\r\n  renderTemplate: function() {\r\n      this.render('product', { into: 'application' });\r\n  }\r\n});\r\n```\r\n\r\nJS Bin example: http://jsbin.com/alewof/5/edit\r\n\r\nOur new function is calling out to render the product template inside of 'application'. Remember that our Handlebars template without the data-type-name attribute is by default treated as 'application'. Now our product template is replacing the products template. Note that this happens even if you still have an added {{ outlet }} to the bottom of the products template.\r\n\r\nThis seems like an innocuous and easy change to get the behavior we want, but we very subtly departed from the 'Ember Way' without realizing it. First, however, we've introduced a bug.\r\n\r\n## Fixing the empty products template bug\r\n\r\nIf you are on the products page and click the product link all works as expected. However, if you press the back button on your browser, (you can replicate this in the JS Bin example by pressing backspace or delete) you end up with an empty area where you expect the products template to render. Before the explanation, let's look at the code to fix this:\r\n\r\n```js\r\nApp.ProductsIndexRoute = App.ProductsRoute = Ember.Route.extend({\r\n  renderTemplate: function() {\r\n    this.render('products', { into: 'application' });\r\n  },   \r\n  model: function() {\r\n    return App.Product.find();\r\n  }\r\n});\r\n```\r\n\r\nJS Bin example: http://jsbin.com/alewof/6/edit\r\n\r\nThis should hopefully trigger some alarms that you've strayed from the path. Now in our ProductsRoute we need to add almost the same code to ensure the template is rendered where we want. Additionally and even worse is that we need to define a ProductsIndexRoute which we previously didn't even care about. Why is this?\r\n\r\nThe reason for this behavior illuminates a fundamental difference in the way a Rails developer approaching Ember must look at nested routes. In Ember routes are very much tied to how templates will render on the page and not just constrained to URL construction and interpretation.\r\n\r\nIn Ember when you designate that a route is nested, you are essentially confirming that the child route and the parent route will render at the same time, on the same page. This is a default behavior of nested routes. Remember that we're talking about routes and not necessarily URLs.\r\n\r\nIn the case above we have defined our routes in a nested fashion, and then strong-armed Ember away from its defaults by forcing the product template to render over its parent. When we click the back button on our browser, Ember expects that we are simply returning to a route which it believes has already rendered. Thus as far as your Ember app is concerned, the content you expect to see should already be there.\r\n\r\nTo make matters even more complex, when you supply a function as an argument to a resource in your route (nesting them), an index route is created in memory and its template will be rendered _after_ the products route. Therefore when we go back from the product route Ember considered the products index the former route, and _then_ the products route. For this reason if they are all to render into the application template they must all implement the same renderTemplate code.\r\n\r\nWhether this seems complex or not, rendering over the parent with renderTemplate is going against the grain. Let's look at something a little more idiomatic.\r\n\r\n## The Ember Way\r\n\r\nTo correct this error, we need to go back to where it began, in the Router.\r\n\r\n```js\r\nApp.Router.map(function() {\r\n  this.resource('products');\r\n  this.resource('product', { 'path' : 'products/:product_id' });\r\n});\r\n```\r\n\r\nThis may seem weird at first because we expect product to be nested under products from a URL routing standpoint, but here they are declared flatly beside each other. We can maintain the URL structure we expect by passing the appropriate value in for our 'path'.\r\n\r\nWe can also now remove the renderTemplate method from our ProductsRoute:\r\n\r\n```js\r\nApp.ProductsRoute = Ember.Route.extend({ \r\n  model: function() {\r\n    return App.Product.find();\r\n  }\r\n});\r\n```\r\n\r\nWe've also removed the declaration of the ProductsIndexRoute from this code as well. Another object that's ripe for the chopping block is the ProductRoute. Once you remove the renderTemplate code from the ProductRoute you aren't left with anything expect for declaring the ProductRoute's existence which Ember will take care of for you by default.\r\n\r\nThis is significantly more straightforward, and feels more in line with Ember idioms.\r\n\r\nJS Bin example: http://jsbin.com/alewof/7/edit\r\n\r\n## Routing revisited\r\n\r\nEmber's Router largely represents the state of your application in a way that is both more firmly established and enforced than any other Javascript MVC framework that I have used. This is a good thing, but it requires a little adjustment in your thinking if you're coming from other frameworks.\r\n\r\nPlan your routes according to your UI. If a route replaces another, it should should be represented at the same level in your router. If a route is to render on the same page as another route within the {{ outlet }} in its template, then you should nest that route in your Router.\r\n\r\nEmber is doing a lot for you. If something feels tricky, take a step back and make sure you're respecting the defaults.","content_text":"Understanding the proper use of the Router in an Ember.js app is a fundamental core concept of the framework. The docs tell us that the router translates a URL into a series of nested templates and almost all available tutorials show how to nest routes as part of an explanation of how the Router works. This is great when you want a UI where a list of items are present at the same time a single item is shown. This leaves many beginners struggling, however, as they try and replace the entire contents of a page with another route's template. Today we'll explore two ways to tackle this problem.\nGetting started\n\nFirst, let's start off with a perfunctory application template.\n\u0026lt;script type=\"text/x-handlebars\"\u0026gt;\n  \u0026lt;h1\u0026gt;My Ember App\u0026lt;/h1\u0026gt;\n\u0026lt;/script\u0026gt;\nvar App = Ember.Application.create();\n\nJS Bin example: http://jsbin.com/alewof/2/edit\n\nOur application is instantiated with one line to Ember.Application.create(); When Ember encounters a Handlebars template without a data-template-name attribute, it will use it by default as the template for the index route and consider it's data-template-name as 'application' if an 'application' template is otherwise not found. Thus Ember will create an app and render this template with little code. It can be helpful to consider this template the application layout.\nAdding a Products Route, Model and Template\n\u0026lt;script type=\"text/x-handlebars\"\u0026gt;\n  \u0026lt;h1\u0026gt;My Ember App\u0026lt;/h1\u0026gt;\n  {{ outlet }}\n\u0026lt;/script\u0026gt;\n\n\u0026lt;script type=\"text/x-handlebars\" data-template-name=\"products\"\u0026gt;\n  \u0026lt;h2\u0026gt;Products\u0026lt;/h2\u0026gt;\n  {{#each controller}}\n    {{ title }}\n  {{/each}}\n\u0026lt;/script\u0026gt;\nvar App = Ember.Application.create();\n\nApp.Router.map(function() {\n  this.resource('products');\n});\n\nApp.Store = DS.Store.extend({\n  revision: 13,\n  adapter: 'DS.FixtureAdapter'\n});\n\nApp.Product = DS.Model.extend({\n  title: DS.attr('string')\n});\n\nApp.Product.FIXTURES = [\n  { id: 1, title: 'Rube Goldberg Breakfast-o-Matic' }\n];\n\nApp.ProductsRoute = Ember.Route.extend({\n  model: function() {\n    return App.Product.find();\n  }\n});\n\nThis is quite a bit of code at once but if you're acquainted with Ember basics it should look familiar. We've defined an {{ outlet }} in our application template where new templates will render. We've added a 'products' template that iterates the products available in the controller and lists their titles. We define 'products' as a route within App.Router and in our ProductsRoute we set the model property on the route to return all products. The rest of this code is our Product model, fixtures, and establishing our App.Store with ember-data. Additionally, we can automatically redirect to the 'products' route from the root by defining an IndexRoute.\nApp.IndexRoute = Ember.Route.extend({\n  redirect: function() {\n    this.transitionTo('products');\n  }\n});\n\nNow when we load our app the products route is activated and we should see the products template rendered.\n\nJS Bin example: http://jsbin.com/alewof/3/edit\nNesting Routes\n\nAs most tutorials available show, we can nest our routes as follows:\nApp.Router.map(function() {\n  this.resource('products', function() {\n    this.resource('product', { 'path' : '/:product_id' });\n  });\n});\n\nThis will look very familiar to Rails developers, and in my opinion this is part of the rub. It seems very natural to nest this resource because the URLs implied match the notion of REST that most Rails developers recognize.\n\nFrom here we can add links to individual products in our products template:\n\u0026lt;script type=\"text/x-handlebars\" data-template-name=\"products\"\u0026gt;\n  \u0026lt;h2\u0026gt;Products\u0026lt;/h2\u0026gt;\n  {{#each controller}}\n    {{#linkTo 'product' this}}{{ title }}{{/linkTo}}\n  {{/each}}\n\u0026lt;/script\u0026gt;\n\nAnd correspondingly we can add a minimal product template like so:\n\u0026lt;script type=\"text/x-handlebars\" data-template-name=\"product\"\u0026gt;\n  \u0026lt;h2\u0026gt;Product: {{ title }}\u0026lt;/h2\u0026gt;\n\u0026lt;/script\u0026gt;\n\nFor the sake of demonstration, if we want our templates to nest just as our routes do we can add an {{ outlet }} to the bottom of our products template. Then when we click the link in the products template the product template will render inside of it.\n\u0026lt;script type=\"text/x-handlebars\" data-template-name=\"products\"\u0026gt;\n  \u0026lt;h2\u0026gt;Products\u0026lt;/h2\u0026gt;\n  {{#each controller}}\n    {{#linkTo 'product' this}}{{ title }}{{/linkTo}}\n  {{/each}}\n  {{ outlet }}\n\u0026lt;/script\u0026gt;\n\nJS Bin example: http://jsbin.com/alewof/4/edit\n\nWe don't want to do this today, though. Let's look at some other Ember methods available to render templates.\nReplacing page content with renderTemplate\n\nWe can define a renderTemplate method inside of an Ember Route object to handle \nthe specifics of which template the route will render and where:\nApp.ProductRoute = Ember.Route.extend({\n  renderTemplate: function() {\n      this.render('product', { into: 'application' });\n  }\n});\n\nJS Bin example: http://jsbin.com/alewof/5/edit\n\nOur new function is calling out to render the product template inside of 'application'. Remember that our Handlebars template without the data-type-name attribute is by default treated as 'application'. Now our product template is replacing the products template. Note that this happens even if you still have an added {{ outlet }} to the bottom of the products template.\n\nThis seems like an innocuous and easy change to get the behavior we want, but we very subtly departed from the 'Ember Way' without realizing it. First, however, we've introduced a bug.\nFixing the empty products template bug\n\nIf you are on the products page and click the product link all works as expected. However, if you press the back button on your browser, (you can replicate this in the JS Bin example by pressing backspace or delete) you end up with an empty area where you expect the products template to render. Before the explanation, let's look at the code to fix this:\nApp.ProductsIndexRoute = App.ProductsRoute = Ember.Route.extend({\n  renderTemplate: function() {\n    this.render('products', { into: 'application' });\n  },   \n  model: function() {\n    return App.Product.find();\n  }\n});\n\nJS Bin example: http://jsbin.com/alewof/6/edit\n\nThis should hopefully trigger some alarms that you've strayed from the path. Now in our ProductsRoute we need to add almost the same code to ensure the template is rendered where we want. Additionally and even worse is that we need to define a ProductsIndexRoute which we previously didn't even care about. Why is this?\n\nThe reason for this behavior illuminates a fundamental difference in the way a Rails developer approaching Ember must look at nested routes. In Ember routes are very much tied to how templates will render on the page and not just constrained to URL construction and interpretation.\n\nIn Ember when you designate that a route is nested, you are essentially confirming that the child route and the parent route will render at the same time, on the same page. This is a default behavior of nested routes. Remember that we're talking about routes and not necessarily URLs.\n\nIn the case above we have defined our routes in a nested fashion, and then strong-armed Ember away from its defaults by forcing the product template to render over its parent. When we click the back button on our browser, Ember expects that we are simply returning to a route which it believes has already rendered. Thus as far as your Ember app is concerned, the content you expect to see should already be there.\n\nTo make matters even more complex, when you supply a function as an argument to a resource in your route (nesting them), an index route is created in memory and its template will be rendered after the products route. Therefore when we go back from the product route Ember considered the products index the former route, and then the products route. For this reason if they are all to render into the application template they must all implement the same renderTemplate code.\n\nWhether this seems complex or not, rendering over the parent with renderTemplate is going against the grain. Let's look at something a little more idiomatic.\nThe Ember Way\n\nTo correct this error, we need to go back to where it began, in the Router.\nApp.Router.map(function() {\n  this.resource('products');\n  this.resource('product', { 'path' : 'products/:product_id' });\n});\n\nThis may seem weird at first because we expect product to be nested under products from a URL routing standpoint, but here they are declared flatly beside each other. We can maintain the URL structure we expect by passing the appropriate value in for our 'path'.\n\nWe can also now remove the renderTemplate method from our ProductsRoute:\nApp.ProductsRoute = Ember.Route.extend({ \n  model: function() {\n    return App.Product.find();\n  }\n});\n\nWe've also removed the declaration of the ProductsIndexRoute from this code as well. Another object that's ripe for the chopping block is the ProductRoute. Once you remove the renderTemplate code from the ProductRoute you aren't left with anything expect for declaring the ProductRoute's existence which Ember will take care of for you by default.\n\nThis is significantly more straightforward, and feels more in line with Ember idioms.\n\nJS Bin example: http://jsbin.com/alewof/7/edit\nRouting revisited\n\nEmber's Router largely represents the state of your application in a way that is both more firmly established and enforced than any other Javascript MVC framework that I have used. This is a good thing, but it requires a little adjustment in your thinking if you're coming from other frameworks.\n\nPlan your routes according to your UI. If a route replaces another, it should should be represented at the same level in your router. If a route is to render on the same page as another route within the {{ outlet }} in its template, then you should nest that route in your Router.\n\nEmber is doing a lot for you. If something feels tricky, take a step back and make sure you're respecting the defaults.\n","summary":"Understanding the proper use of the Router in an Ember.js app is a fundamental core concept of the framework. The docs tell us that the router translates a URL into a series of nested templates and almost all available tutorials show how to nest routes as part of an explanation of how the Router works. This is great when you want a UI where a list of items are present at the same time a single item is shown. This leaves many beginners struggling, however, as they try and replace the entire contents of a page with another route's template. Today we'll explore two ways to tackle this problem.\n","image":"blog/bg_default_article_.png","date_published":"2013-07-29T20:00:00-04:00","data_modified":"2013-11-26T10:41:27-05:00","author":{"name":"Andy Borsz","url":"https://hashrocket.com/team/andy-borsz","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/42/andy-borsz.jpg"},"tags":["Javascript","Development"]},{"id":"https://hashrocket.com/blog/posts/two-coffeescript-oddities","url":"https://hashrocket.com/blog/posts/two-coffeescript-oddities","title":"Two Coffeescript Oddities","content_html":"Here are a couple of interesting situations I encountered a while back while writing some Coffeescript. I was writing [the QUnit test suite for jQuery.minical](https://github.com/camerond/jquery-minical/blob/master/views/coffeescript/suite.coffee) as an exploratory exercise to become more familiar with the language, and I ran across a couple of unexpected behaviors – not bugs, just ways Coffeescript's more minimal syntax can trip you up in ways that aren't immediately apparent if you're coming from the world of vanilla Javascript.\n\n## $.map() and Coffeescript Aren't Friends\r\n\r\nOne particular test in this plugin required that I verify the contents of the days of the week (it's a calendar plugin), which are contained in the `\u003cth\u003e` elements of the calendar (a table). This is essentially what we want to verify:\r\n\r\n```javascript\r\n$(\".calendar th\").text() == \"SunMonTueWedThuFriSat\"\r\n```\r\n\r\nWell, that's fine, but kind of ugly. Just because`.text()` mashes everything together doesn't mean I should just compare against a mashed-up string. So, I thought a nice touch would be to compare the elements in an array, like this:\r\n\r\n```javascript\r\n$(\".calendar th\").getTextArray() == [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"]\r\n```\r\n\r\nNow, `$.getTextArray()` is the function I wrote for this, and it's kind of awkward in Coffeescript. The Javascript implementation is easy: use `$.map()` to create a jQuery object, and use `$.get()` to extract the native array. As the jQuery docs say: [\"As the return value is a jQuery object, which contains an array, it's very common to call .get() on the result to work with a basic array.\"](http://api.jquery.com/map/)\r\n\r\n```javascript\r\n$.fn.getTextArray = function() {\r\n  return $(this).map(function() {\r\n    return $(this).text();\r\n  }).get();\r\n}\r\n```\r\n\r\nHowever, as far as I can tell, that output is undeniably awkward to execute in Coffeescript. This should work, right?\r\n\r\n```coffeescript\r\n$.fn.getTextArray = -\u003e\r\n  $(@).map -\u003e $(@).text().get()\r\n```\r\n\r\nNope, that will call `.get()` within `.map()`:\r\n\r\n```javascript\r\n$(this).map(function() {\r\n  return $(this).text().get();\r\n});\r\n```\r\nYour options are to add parentheses, which is the most verbose javascript-like option ...\r\n\r\n```coffeescript\r\n$(\"li\").map( -\u003e\r\n    $(@).text()\r\n).get()\r\n```\r\n\r\n... outdent, which feels super-awkward (especially if you don't indent the third line at all, which evaluates accurately but looks confusing) ...\r\n\r\n```coffeescript\r\n$(\"li\").map -\u003e\r\n    $(@).text()\r\n  .get()\r\n```\r\n... or use an extra set of parentheses (which actually outputs an extra set into the Javascript as well, but works fine):\r\n\r\n```coffeescript\r\n($(\"li\").map -\u003e $(@).text()).get()\r\n```\r\n\r\nThis isn't a deal breaker by any means, but if you're used to vanilla Javascript, you'll run into these issues where jQuery expects to operate in ways that require more parentheses than Coffeescript usually needs, and your code will fail in unpredictable ways.\r\n\r\n_Update: I stand humbled by Tomas Carnecky's comment below with another (superior) option: `$(\"li\").map(-\u003e $(@).text()).get()` does the job. Just goes to show how many suboptimal ways there are to refactor Javascript into Coffeescript._\r\n\r\n## Don't Under-Parenthesize, if \"Parenthesize\" Is Even A Word \r\n\r\nHere's a more complex example. I wanted to put together an array of every day I expected to be in a particular calendar month view. This would require handwriting an array or running a bunch of loops in Javascript, but Coffeescript's comprehensions and array operations allows us to slam together the whole array in one line.\r\n\r\n```coffeescript\r\ndays = ((day + \"\") for day in [].concat([25..30],[1..31],[1..5]))\r\n```\r\n\r\nWoohoo! I love that to death. Coffeescript is occasionally great for reducing distracting code around something minor like constructing an arbitrary array. However, note the enclosing parentheses around the whole thing. If you remove those:\r\n\r\n```coffeescript\r\ndays = (day + \"\") for day in [].concat([25..30],[1..31],[1..5])\r\n```\r\n\r\nCRAZY THINGS happen, [the results of which I will leave as an exercise for the reader](http://bit.ly/XP7H98).\r\n\r\nThe bottom line here is that Coffeescript's minimal syntax will sometimes sneak up on you when you're not carefully keeping track of how it's evaluating your code. It's pretty easy to drop one too many sets of parentheses in your zeal to write concisely.\r\n\r\nThis sort of extreme code breakage in the CS to JS handoff is the riskiest thing about using Coffeescript, as it can be difficult to bug fix, but if you've used Coffeescript at all, you're well aware of that, and hopefully this article has better prepared you for those situations. Happy Coffeescripting!","content_text":"Here are a couple of interesting situations I encountered a while back while writing some Coffeescript. I was writing the QUnit test suite for jQuery.minical as an exploratory exercise to become more familiar with the language, and I ran across a couple of unexpected behaviors – not bugs, just ways Coffeescript's more minimal syntax can trip you up in ways that aren't immediately apparent if you're coming from the world of vanilla Javascript.\n$.map() and Coffeescript Aren't Friends\n\nOne particular test in this plugin required that I verify the contents of the days of the week (it's a calendar plugin), which are contained in the \u0026lt;th\u0026gt; elements of the calendar (a table). This is essentially what we want to verify:\n$(\".calendar th\").text() == \"SunMonTueWedThuFriSat\"\n\nWell, that's fine, but kind of ugly. Just because.text() mashes everything together doesn't mean I should just compare against a mashed-up string. So, I thought a nice touch would be to compare the elements in an array, like this:\n$(\".calendar th\").getTextArray() == [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"]\n\nNow, $.getTextArray() is the function I wrote for this, and it's kind of awkward in Coffeescript. The Javascript implementation is easy: use $.map() to create a jQuery object, and use $.get() to extract the native array. As the jQuery docs say: \"As the return value is a jQuery object, which contains an array, it's very common to call .get() on the result to work with a basic array.\"\n$.fn.getTextArray = function() {\n  return $(this).map(function() {\n    return $(this).text();\n  }).get();\n}\n\nHowever, as far as I can tell, that output is undeniably awkward to execute in Coffeescript. This should work, right?\n$.fn.getTextArray = -\u0026gt;\n  $(@).map -\u0026gt; $(@).text().get()\n\nNope, that will call .get() within .map():\n$(this).map(function() {\n  return $(this).text().get();\n});\n\nYour options are to add parentheses, which is the most verbose javascript-like option ...\n$(\"li\").map( -\u0026gt;\n    $(@).text()\n).get()\n\n... outdent, which feels super-awkward (especially if you don't indent the third line at all, which evaluates accurately but looks confusing) ...\n$(\"li\").map -\u0026gt;\n    $(@).text()\n  .get()\n\n... or use an extra set of parentheses (which actually outputs an extra set into the Javascript as well, but works fine):\n($(\"li\").map -\u0026gt; $(@).text()).get()\n\nThis isn't a deal breaker by any means, but if you're used to vanilla Javascript, you'll run into these issues where jQuery expects to operate in ways that require more parentheses than Coffeescript usually needs, and your code will fail in unpredictable ways.\n\nUpdate: I stand humbled by Tomas Carnecky's comment below with another (superior) option: $(\"li\").map(-\u0026gt; $(@).text()).get() does the job. Just goes to show how many suboptimal ways there are to refactor Javascript into Coffeescript.\nDon't Under-Parenthesize, if \"Parenthesize\" Is Even A Word\n\nHere's a more complex example. I wanted to put together an array of every day I expected to be in a particular calendar month view. This would require handwriting an array or running a bunch of loops in Javascript, but Coffeescript's comprehensions and array operations allows us to slam together the whole array in one line.\ndays = ((day + \"\") for day in [].concat([25..30],[1..31],[1..5]))\n\nWoohoo! I love that to death. Coffeescript is occasionally great for reducing distracting code around something minor like constructing an arbitrary array. However, note the enclosing parentheses around the whole thing. If you remove those:\ndays = (day + \"\") for day in [].concat([25..30],[1..31],[1..5])\n\nCRAZY THINGS happen, the results of which I will leave as an exercise for the reader.\n\nThe bottom line here is that Coffeescript's minimal syntax will sometimes sneak up on you when you're not carefully keeping track of how it's evaluating your code. It's pretty easy to drop one too many sets of parentheses in your zeal to write concisely.\n\nThis sort of extreme code breakage in the CS to JS handoff is the riskiest thing about using Coffeescript, as it can be difficult to bug fix, but if you've used Coffeescript at all, you're well aware of that, and hopefully this article has better prepared you for those situations. Happy Coffeescripting!\n","summary":"Here are a couple of interesting situations I encountered a while back while writing some Coffeescript. I was writing the QUnit test suite for jQuery.minical as an exploratory exercise to become more familiar with the language, and I ran across a couple of unexpected behaviors – not bugs, just ways Coffeescript's more minimal syntax can trip you up in ways that aren't immediately apparent if you're coming from the world of vanilla Javascript.\n","image":"blog/bg_default_article_.png","date_published":"2013-02-07T04:00:00-05:00","data_modified":"2013-11-26T10:41:27-05:00","author":{"name":"Cameron Daigle","url":"https://hashrocket.com/team/cameron-daigle","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/3/cameron-daigle.jpg"},"tags":["Javascript","Development"]},{"id":"https://hashrocket.com/blog/posts/using-tiled-and-canvas-to-render-game-screens","url":"https://hashrocket.com/blog/posts/using-tiled-and-canvas-to-render-game-screens","title":"Using Tiled and Canvas to Render Game Screens","content_html":"A while back, I came across a great application called Tiled that can be used for creating layered scenes using a sprite map, and thought it would be a great way to quickly create scenes in Canvas-based games.\n\nThe idea behind this block of code is creating a way to go from Tiled data to the same scene rendered in Canvas, like [this one](http://shaneriley.com/demos/canvas/tiled_renderer/) for use in top-down view Javascript games.\r\n\r\nTo start with, let's grab a nice-looking sprite sheet from our friends at [Open Game Art](http://opengameart.org). We'll use a nice Zelda [overworld-like sprite sheet](http://shaneriley.com/demos/canvas/tiled_renderer/images/mountain_landscape_23.png). You can check out the [Tiled tutorial](https://github.com/bjorn/tiled/wiki) for how to do the basics. We'll start by creating a new tileset. From the map menu, select new tileset. Find the sprite sheet we grabbed from Open Game Art, give it a name, and leave the default 32 x 32 tile size. You should see the tileset in the bottom right. This gives us the sprite stamps we need to create our scene.\r\n\r\nNext, we'll make the layers we'll use to create our scene. Click the add layer icon below the layers pane or choose add tile layer from the layer menu. For this example, I created three layers: forest, mountain, and ground. The ground layer contains any grass or pathways, forest contains trees, tall grass, and small stones. Mountain contains the larger rock formations, including the cave entrance.\r\n\r\nOnce we've got the scene drawn, we can export the data necessary to render it to a canvas in JSON format. From the file menu, choose export as. From here, we can start writing the code necessary to generate the scene in canvas. We'll start by creating a scene object to hold our layers and methods for rendering them.\r\n\r\n```javascript\r\nvar scene = {\r\n  layers: [],\r\n  renderLayer: function(layer) { },\r\n  renderLayers: function(layers) { },\r\n  loadTileset: function(json) { },\r\n  load: function(name) { }\r\n}\r\n```\r\n\r\nLet's create a method to load the scene using jQuery's Ajax method. We'll assume the path will be to the maps directory, and we'll create an argument to pass in the scene name. When we're done loading the JSON data, we'll call the loadTileset method to preload our sprite sheet.\r\n\r\n```javascript\r\n  load: function(name) {\r\n    return $.ajax({\r\n      url: \"/maps/\" + name + \".json\",\r\n      type: \"JSON\"\r\n    }).done($.proxy(this.loadTileset, this));\r\n  }\r\n```\r\n\r\nNote that we are using jQuery's proxy method to assign the context that the loadTileset method is being run with. We'll receive the same arguments passed to loadTileset as we normally would with the done callback, but `this` will refer to the scene object.\r\n\r\nWhy did we use the deferred .done() method rather than the success method in the Ajax object? This allows us to add multiple callbacks on the same Ajax request and manage the callbacks if necessary. Since the load method is returning the deferred object created by jQuery, we can attach any scene-specific callbacks to the returned object when we tell the scene to load.\r\n\r\nThe loadTileset method is easy enough. Create the tileset image, store a reference to it, then attach an onload event to render the scene's layers.\r\n\r\n```javascript\r\n  loadTileset: function(json) {\r\n    this.data = json;\r\n    this.tileset = $(\"\u003cimg /\u003e\", { src: json.tilesets[0].image })[0]\r\n    this.tileset.onload = $.proxy(this.renderLayers, this);\r\n  }\r\n```\r\n\r\nWe assign the JSON data to the data property of our scene object, create an img DOM element and store it as scene.tileset, then set our proxied renderLayers method as our onload callback. This means our next method of interest will be renderLayers.\r\n\r\n```javascript\r\n  renderLayers: function(layers) {\r\n    layers = $.isArray(layers) ? layers : this.data.layers;\r\n    layers.forEach(this.renderLayer);\r\n  }\r\n```\r\n\r\nI've set up renderLayers to allow an optional argument to specify which layers we should be rendering. This allows us to call the method later and render either a subset of the layers of that scene or a mixture of layers from this scene and others.\r\n\r\nFinally on to the bulk of the work, the renderLayer method. Let's start by making sure the layer we've been passed can and should be rendered. If so, we'll set up a scratch canvas to render to for a slight performance improvement. This method assumes you've created a canvas rendering context and assigned it to variable `c`, like `var c = $(\"canvas\")[0].getContext(\"2d\");`\r\n\r\n```javascript\r\n  if (layer.type !== \"tilelayer\" || !layer.opacity) { return; }\r\n  var s = c.canvas.cloneNode(),\r\n        size = scene.data.tilewidth;\r\n  s = s.getContext(\"2d\");\r\n```\r\n\r\nNext we'll check to see if we've previously rendered the layers and stored them as images. If we haven't, we'll start rendering the tiles for that layer to the canvas.\r\n\r\n```javascript\r\n  if (scene.layers.length \u003c scene.data.layers.length) {\r\n    layer.data.forEach(function(tile_idx, i) {\r\n      if (!tile_idx) { return; }\r\n      var img_x, img_y, s_x, s_y,\r\n            tile = scene.data.tilesets[0];\r\n      tile_idx--;\r\n      img_x = (tile_idx % (tile.imagewidth / size)) * size;\r\n      img_y = ~~(tile_idx / (tile.imagewidth / size)) * size;\r\n      s_x = (i % layer.width) * size;\r\n      s_y = ~~(i / layer.width) * size;\r\n      s.drawImage(scene.tileset, img_x, img_y, size, size,\r\n                          s_x, s_y, size, size);\r\n    });\r\n    scene.layers.push(s.canvas.toDataURL());\r\n    c.drawImage(s.canvas, 0, 0);\r\n  }\r\n```\r\n\r\nOur forEach method received the tile ID from the JSON layer object and an iteration index. If the tile ID is 0, there is no sprite to be rendered in that location, and we won't need to proceed further. If there is a tile ID, we'll start into the drawing method by setting our x and y coordinates for the sprite. To properly calculate this based on the tile ID, we'll need to use a 0-based index and as such we'll decrement the tile ID before performing our calculations.\r\n\r\nThe x and y coordinates of the sprite within the sprite sheet are calculated by thinking of the canvas as a two-dimensional matrix that we write to from left to right, top to bottom. The x value, therefore, is derived from the modulus of the tile ID and the sprite sheet's width divided by the width of the sprite, multiplied by the width of the sprite. As an example, if we were told to use sprite 4 for the current block in the scene matrix, our sprite sheet's width is 512 and our sprite width is 32, we'd have an equation that looks like this:\r\n\r\n```javascript\r\n  img_x = (3 % (512 / 32)) * 32;\r\n```\r\n\r\nWe do the same for the y value. The tile ID is divided by the result of the sprite sheet width divided by the size. This gives us the row we're grabbing the sprite from. Multiply that by the sprite size and you get the starting y value of the sprite. To ensure we don't use a floating point number and receive a blurred version of our sprite, we use the double bitwise not operator to drop the decimal places. Depending on your browsers, you may have a slight performance improvement by using the left bitwise shift, `img_y = (tile_idx / (tile.imagewidth / size)) \u003c\u003c 0 * size`, but I've found that more developers know what you're doing with the double not. If both are confusing, pass it in to Math.floor instead.\r\n\r\nThe sprite's x and y position are now calculated in a similar fashion to the sprite sheet's x and y position. Finally, we get to rendering the portion of the sprite sheet to the canvas. We use the tileset image as the source, set the starting x and y position on the image, the width and height we're taking from that point, then use that cut of the image to draw from s_x, s_y at the same sprite size.\r\n\r\nOnce we've rendered each of the sprites to the canvas for that layer, we push a base-64 encoded version of the canvas in PNG format to the layers array on our scene object for quick redrawing. Using our scratch canvas as an image, we draw it to the game canvas starting at point 0, 0.\r\n\r\nIf our layers are already rendered to PNGs, we simply draw them to the canvas.\r\n\r\n```javascript\r\n  else {\r\n    scene.layers.forEach(function(src) {\r\n      var i = $(\"\u003cimg /\u003e\", { src: src })[0];\r\n      c.drawImage(i, 0, 0);\r\n    });\r\n  }\r\n```\r\n\r\nAnd that's it! You should now see the same scene you created in Tiled rendered to the canvas, like [my example](http://shaneriley.com/demos/canvas/tiled_renderer/) from before. In my example, I'm using a 640 x 480 canvas to make it more like the screen size of the 16-bit era games. You can now use this backdrop for whatever manner of game you're making. I recommend keeping this as a separate canvas layered underneath the game canvas via CSS or exporting the contents of the canvas-rendered scene to a PNG and using that to redraw your scene. This will give you a tremendous performance boost allowing you to do more with your game without experiencing frame rate issues.\r\n\r\nThere's more that can be done to this to make it more universal, such as turning it into a prototype or giving it a new method to create separate instances (for now I use $.extend) and adding flipped sprite output. Adding options like sprite sheet path, map data path, and scene overrides (modify opacity, sprite dimensions, etc.) will make it easier to control the output. If time and desire permit, I'll be adding these features in later and making a formal repository for it. For now, you can grab the code we've reviewed from [this Gist](https://gist.github.com/4078905).","content_text":"A while back, I came across a great application called Tiled that can be used for creating layered scenes using a sprite map, and thought it would be a great way to quickly create scenes in Canvas-based games.\n\nThe idea behind this block of code is creating a way to go from Tiled data to the same scene rendered in Canvas, like this one for use in top-down view Javascript games.\n\nTo start with, let's grab a nice-looking sprite sheet from our friends at Open Game Art. We'll use a nice Zelda overworld-like sprite sheet. You can check out the Tiled tutorial for how to do the basics. We'll start by creating a new tileset. From the map menu, select new tileset. Find the sprite sheet we grabbed from Open Game Art, give it a name, and leave the default 32 x 32 tile size. You should see the tileset in the bottom right. This gives us the sprite stamps we need to create our scene.\n\nNext, we'll make the layers we'll use to create our scene. Click the add layer icon below the layers pane or choose add tile layer from the layer menu. For this example, I created three layers: forest, mountain, and ground. The ground layer contains any grass or pathways, forest contains trees, tall grass, and small stones. Mountain contains the larger rock formations, including the cave entrance.\n\nOnce we've got the scene drawn, we can export the data necessary to render it to a canvas in JSON format. From the file menu, choose export as. From here, we can start writing the code necessary to generate the scene in canvas. We'll start by creating a scene object to hold our layers and methods for rendering them.\nvar scene = {\n  layers: [],\n  renderLayer: function(layer) { },\n  renderLayers: function(layers) { },\n  loadTileset: function(json) { },\n  load: function(name) { }\n}\n\nLet's create a method to load the scene using jQuery's Ajax method. We'll assume the path will be to the maps directory, and we'll create an argument to pass in the scene name. When we're done loading the JSON data, we'll call the loadTileset method to preload our sprite sheet.\n  load: function(name) {\n    return $.ajax({\n      url: \"/maps/\" + name + \".json\",\n      type: \"JSON\"\n    }).done($.proxy(this.loadTileset, this));\n  }\n\nNote that we are using jQuery's proxy method to assign the context that the loadTileset method is being run with. We'll receive the same arguments passed to loadTileset as we normally would with the done callback, but this will refer to the scene object.\n\nWhy did we use the deferred .done() method rather than the success method in the Ajax object? This allows us to add multiple callbacks on the same Ajax request and manage the callbacks if necessary. Since the load method is returning the deferred object created by jQuery, we can attach any scene-specific callbacks to the returned object when we tell the scene to load.\n\nThe loadTileset method is easy enough. Create the tileset image, store a reference to it, then attach an onload event to render the scene's layers.\n  loadTileset: function(json) {\n    this.data = json;\n    this.tileset = $(\"\u0026lt;img /\u0026gt;\", { src: json.tilesets[0].image })[0]\n    this.tileset.onload = $.proxy(this.renderLayers, this);\n  }\n\nWe assign the JSON data to the data property of our scene object, create an img DOM element and store it as scene.tileset, then set our proxied renderLayers method as our onload callback. This means our next method of interest will be renderLayers.\n  renderLayers: function(layers) {\n    layers = $.isArray(layers) ? layers : this.data.layers;\n    layers.forEach(this.renderLayer);\n  }\n\nI've set up renderLayers to allow an optional argument to specify which layers we should be rendering. This allows us to call the method later and render either a subset of the layers of that scene or a mixture of layers from this scene and others.\n\nFinally on to the bulk of the work, the renderLayer method. Let's start by making sure the layer we've been passed can and should be rendered. If so, we'll set up a scratch canvas to render to for a slight performance improvement. This method assumes you've created a canvas rendering context and assigned it to variable c, like var c = $(\"canvas\")[0].getContext(\"2d\");\n  if (layer.type !== \"tilelayer\" || !layer.opacity) { return; }\n  var s = c.canvas.cloneNode(),\n        size = scene.data.tilewidth;\n  s = s.getContext(\"2d\");\n\nNext we'll check to see if we've previously rendered the layers and stored them as images. If we haven't, we'll start rendering the tiles for that layer to the canvas.\n  if (scene.layers.length \u0026lt; scene.data.layers.length) {\n    layer.data.forEach(function(tile_idx, i) {\n      if (!tile_idx) { return; }\n      var img_x, img_y, s_x, s_y,\n            tile = scene.data.tilesets[0];\n      tile_idx--;\n      img_x = (tile_idx % (tile.imagewidth / size)) * size;\n      img_y = ~~(tile_idx / (tile.imagewidth / size)) * size;\n      s_x = (i % layer.width) * size;\n      s_y = ~~(i / layer.width) * size;\n      s.drawImage(scene.tileset, img_x, img_y, size, size,\n                          s_x, s_y, size, size);\n    });\n    scene.layers.push(s.canvas.toDataURL());\n    c.drawImage(s.canvas, 0, 0);\n  }\n\nOur forEach method received the tile ID from the JSON layer object and an iteration index. If the tile ID is 0, there is no sprite to be rendered in that location, and we won't need to proceed further. If there is a tile ID, we'll start into the drawing method by setting our x and y coordinates for the sprite. To properly calculate this based on the tile ID, we'll need to use a 0-based index and as such we'll decrement the tile ID before performing our calculations.\n\nThe x and y coordinates of the sprite within the sprite sheet are calculated by thinking of the canvas as a two-dimensional matrix that we write to from left to right, top to bottom. The x value, therefore, is derived from the modulus of the tile ID and the sprite sheet's width divided by the width of the sprite, multiplied by the width of the sprite. As an example, if we were told to use sprite 4 for the current block in the scene matrix, our sprite sheet's width is 512 and our sprite width is 32, we'd have an equation that looks like this:\n  img_x = (3 % (512 / 32)) * 32;\n\nWe do the same for the y value. The tile ID is divided by the result of the sprite sheet width divided by the size. This gives us the row we're grabbing the sprite from. Multiply that by the sprite size and you get the starting y value of the sprite. To ensure we don't use a floating point number and receive a blurred version of our sprite, we use the double bitwise not operator to drop the decimal places. Depending on your browsers, you may have a slight performance improvement by using the left bitwise shift, img_y = (tile_idx / (tile.imagewidth / size)) \u0026lt;\u0026lt; 0 * size, but I've found that more developers know what you're doing with the double not. If both are confusing, pass it in to Math.floor instead.\n\nThe sprite's x and y position are now calculated in a similar fashion to the sprite sheet's x and y position. Finally, we get to rendering the portion of the sprite sheet to the canvas. We use the tileset image as the source, set the starting x and y position on the image, the width and height we're taking from that point, then use that cut of the image to draw from s_x, s_y at the same sprite size.\n\nOnce we've rendered each of the sprites to the canvas for that layer, we push a base-64 encoded version of the canvas in PNG format to the layers array on our scene object for quick redrawing. Using our scratch canvas as an image, we draw it to the game canvas starting at point 0, 0.\n\nIf our layers are already rendered to PNGs, we simply draw them to the canvas.\n  else {\n    scene.layers.forEach(function(src) {\n      var i = $(\"\u0026lt;img /\u0026gt;\", { src: src })[0];\n      c.drawImage(i, 0, 0);\n    });\n  }\n\nAnd that's it! You should now see the same scene you created in Tiled rendered to the canvas, like my example from before. In my example, I'm using a 640 x 480 canvas to make it more like the screen size of the 16-bit era games. You can now use this backdrop for whatever manner of game you're making. I recommend keeping this as a separate canvas layered underneath the game canvas via CSS or exporting the contents of the canvas-rendered scene to a PNG and using that to redraw your scene. This will give you a tremendous performance boost allowing you to do more with your game without experiencing frame rate issues.\n\nThere's more that can be done to this to make it more universal, such as turning it into a prototype or giving it a new method to create separate instances (for now I use $.extend) and adding flipped sprite output. Adding options like sprite sheet path, map data path, and scene overrides (modify opacity, sprite dimensions, etc.) will make it easier to control the output. If time and desire permit, I'll be adding these features in later and making a formal repository for it. For now, you can grab the code we've reviewed from this Gist.\n","summary":"A while back, I came across a great application called Tiled that can be used for creating layered scenes using a sprite map, and thought it would be a great way to quickly create scenes in Canvas-based games.\n","image":"blog/bg_default_article_.png","date_published":"2012-11-15T19:00:00-05:00","data_modified":"2013-11-26T10:41:31-05:00","author":{"name":"Shane Riley","url":"https://hashrocket.com/team/shane-riley","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/20/shane-riley.jpg"},"tags":["Design","Javascript","Development"]},{"id":"https://hashrocket.com/blog/posts/javascript-rails-google-maps","url":"https://hashrocket.com/blog/posts/javascript-rails-google-maps","title":"Javascript, Rails, Google Maps!","content_html":"For anyone familiar with a certain little backpack wearing explorer, the layout of this post will seem eerily reminiscent of an episode. Not only because of her favorite tool, the map, but also because we are following her three step method: JavaScript, Rails, Goo-gle-maps! (I can hear her now)\n\nLately at Hashrocket we have had a number of projects heavily dependent on the [Google Maps API](https://developers.google.com/maps/documentation/javascript/) so it seemed like a great time to discuss one of these implementations, as well as some of the key points when integrating with that API. However, before we can begin we need to \"map\" out our journey. First we will geolocate our position using the Google Map API and javascript. Then we will use those results to query our rails services API for additional markers in the area. Finally we will return to JavaScript and add the markers and events to our map. So, come on vámonos!\r\n\r\nEvery journey starts with the first step, and our first step is to include Google's JavaScript API URI path in our page heading.\r\n\r\n```javascript\r\n= javascript_include_tag \"http://maps.googleapis.com/maps/api/js?sensor=false\"\r\n```\r\n\r\nThis will give us all the Google-y goodness needed for our adventure.\r\n\r\nJavaScript \u0026 Google Geocoding API - the First Stop\r\n------------------------------------------------------\r\n\r\nThe [Google Geocoding API](https://developers.google.com/maps/documentation/geocoding/) is provided as part of the Google Maps API in order to retrieve geolocation data. The API provides access to the geocoder object through HTTP requests. The service can geolocate points by both a standard or a reverse lookup with either an address or coordinates. Once the Map API URI is included as a JavaScript tag, accessing the Geocoder object is simple but we need to look at the results set that is returned.\r\n\r\n```javascript\r\nvar geocoder = new google.maps.Geocoder();\r\ngeocoder.geocode({address: 'Jax Beach, FL'}, function(results, status) {\r\n    // Do something with the results\r\n});\r\n```\r\n\r\nThe API delivers back some valuable data in the form of a JSON object (XML is also an option) that can be easily parsed to provide some essential information. One side effect is address sanitization so the address [Jax Beach, FL](https://maps.googleapis.com/maps/api/geocode/json?address=Jax+Beach,+FL\u0026sensor=false) returns Jacksonville Beach, FL.\r\n\r\nIn addition to having a clean address the response includes the geometry details like bounds, location point and suggested viewport bounds. Both the bounds and viewport are points in the Northeast and Southwest quadrants and define the rectangle that our supplied address appears within. The API provides a couple methods to get to those boundary points: ```getNorthEast()``` and ```getSouthWest()```. The two methods will extract the bounding box points so that we can use them in out ajax request.\r\n\r\n```javascript\r\nvar geocoder = new google.maps.Geocoder();\r\ngeocoder.geocode({address: 'Jax Beach, FL'}, function(results, status) {\r\n    var bounds = results[0].geometry.bounds,\r\n        center = results[0].geometry.location;\r\n    if (bounds) {\r\n        var ne = bounds.getNorthEast(),\r\n            sw = bounds.getSouthWest(),\r\n            data = { sw: [sw.lat(), sw.lng()], ne: [ne.lat(), ne.lng()]};\r\n\r\n            // ajax call to rails service API\r\n    }\r\n});\r\n```\r\n\r\nThe Rails API - The Other Side\r\n------------------------------\r\n\r\nNow that we have arrived on the other side of the tracks, we can discuss a couple of gems used to ease the implementation of our Rails API. Because querying the data comes before delivering it, we should start with the [geokit-rails3](https://github.com/jlecour/geokit-rails3) gem. This is a port of the geokit gem and provides a common interface for geolocation applications. It also allows us to make ActiveRecord models mappable simply by adding acts_as_mappable to our model. To query the database based on our supplied bounds, we use the geo_scope method. This is the gem's underlying method for scoped searches and we can chain methods to refine our result set like any other ActiveRecord query.\r\n\r\n```ruby\r\ndef geo_search(sw, ne)\r\n    self.geo_scope(bounds: [sw, ne]).where(active: true)\r\nend\r\n```\r\n\r\nOnce we have retrieved the data from our database we can use the [jBuilder](https://github.com/rails/jbuilder) gem to easily generate the JSON views. The setup is simple for our scenario and we can abstract some of the formatting to an application helper. Here is the format we will use in our result set:\r\n\r\n```ruby\r\njson.results do |json|\r\n    json.array!(searchables) do |json, result|\r\n        json.id result.id\r\n        json.type result_type(result)\r\n        json.name result.name\r\n        json.lat result.latitude\r\n        json.lng result.longitude\r\n        json.url url_for(result)\r\n    end\r\nend\r\n```\r\n\r\nWhen you are testing the Rails service API with rspec, remember to add a call to render_views in your spec. Otherwise, the tests will not return the JSON views in the response.\r\n\r\n```ruby\r\ndescribe SearchablesController do\r\n    render_views\r\n\r\n    # add Fabricated records for testing\r\n\r\n    context \"search spots\" do\r\n        it \"returns our spot\" do\r\n            get :index, { format: :json, sw: sw, ne: ne }\r\n            data = JSON.parse(response.body)\r\n            data['results'].count.should eq(1)\r\n            data['results'][0]['name'].should eq( 'Jacksonville Beach Pier' )\r\n        end\r\n    end\r\nend\r\n```\r\n\r\nPutting Our Map Together\r\n------------------------\r\n\r\nNow that we have our results, we need to add them to a map object. To do that we need to define a map and give it a few options. Note the map center point is being set through the options. This can also be set after the map is drawn using the ```setCenter()``` method. (For the full list of options check out the Google Maps API [Map Options](https://developers.google.com/maps/documentation/javascript/reference#MapOptions))\r\n\r\n```javascript\r\nvar opts = {\r\n    zoom: 10,\r\n    max_zoom: 16,\r\n    scrollwheel: false,\r\n    center: new google.maps.LatLng(center.lat(), center.lng()),\r\n    mapTypeId: google.maps.MapTypeId.ROADMAP,\r\n    MapTypeControlOptions: {\r\n        MapTypeIds: [google.maps.MapTypeId.ROADMAP]\r\n    }\r\n};\r\n\r\nvar map = new google.maps.Map($('#map'), opts);\r\n```\r\n\r\nAt this point we need to iterate through our results set and add markers for each point to the map. The Google API also provides a Marker object to define a marker and these can be customized to level your application's needs, including the addition of custom images.\r\n\r\n```javascript\r\nvar marker = new google.maps.Marker({\r\n    id: result.id,\r\n    title: result.name,\r\n    position: new google.maps.LatLng(result.lat, result.lng),\r\n    cursor: 'pointer',\r\n    flat: false,\r\n    icon: new google.maps.MarkerImage('/assets/our_custom_icon.png',\r\n                                        new google.maps.Size(32, 35),\r\n                                        new google.maps.Point(0, 0),\r\n                                        new google.maps.Point(32 / 2, 35),\r\n                                        new google.maps.Size(32, 35))\r\n   })\r\n);\r\n```\r\n\r\nAnd there you have it! A map that has all our results that fall within a boundary. What more could you want? Oh yeah, when you move the map or zoom in/out the bounds change but our results don't. For that we need to use the Google Map API events, most notably the \"idle\" event. This event is fired when the map display goes idle. Although there are specific events for panning and zoom, we have found that the \"idle\" event works best for this scenario.\r\n\r\n```javascript\r\ngoogle.maps.event.addListener(map, 'idle', function() {\r\n    // call your geolocation method\r\n});\r\n```\r\n\r\nOur journey is now complete. We have a Google map that displays all the stored results that fall within a bounding box. The code here has been modified to fit neatly into a single post so its purpose is to be an example of Google Maps integration. We would suggest defining your map object using object literal notation so that the map functionality can be encapsulated and functions can be reused.\r\n\r\nSwiper, go swiping and have your own adventure with JavaScript, Rails, Goo-gle maps! ","content_text":"For anyone familiar with a certain little backpack wearing explorer, the layout of this post will seem eerily reminiscent of an episode. Not only because of her favorite tool, the map, but also because we are following her three step method: JavaScript, Rails, Goo-gle-maps! (I can hear her now)\n\nLately at Hashrocket we have had a number of projects heavily dependent on the Google Maps API so it seemed like a great time to discuss one of these implementations, as well as some of the key points when integrating with that API. However, before we can begin we need to \"map\" out our journey. First we will geolocate our position using the Google Map API and javascript. Then we will use those results to query our rails services API for additional markers in the area. Finally we will return to JavaScript and add the markers and events to our map. So, come on vámonos!\n\nEvery journey starts with the first step, and our first step is to include Google's JavaScript API URI path in our page heading.\n= javascript_include_tag \"http://maps.googleapis.com/maps/api/js?sensor=false\"\n\nThis will give us all the Google-y goodness needed for our adventure.\nJavaScript \u0026amp; Google Geocoding API - the First Stop\n\nThe Google Geocoding API is provided as part of the Google Maps API in order to retrieve geolocation data. The API provides access to the geocoder object through HTTP requests. The service can geolocate points by both a standard or a reverse lookup with either an address or coordinates. Once the Map API URI is included as a JavaScript tag, accessing the Geocoder object is simple but we need to look at the results set that is returned.\nvar geocoder = new google.maps.Geocoder();\ngeocoder.geocode({address: 'Jax Beach, FL'}, function(results, status) {\n    // Do something with the results\n});\n\nThe API delivers back some valuable data in the form of a JSON object (XML is also an option) that can be easily parsed to provide some essential information. One side effect is address sanitization so the address Jax Beach, FL returns Jacksonville Beach, FL.\n\nIn addition to having a clean address the response includes the geometry details like bounds, location point and suggested viewport bounds. Both the bounds and viewport are points in the Northeast and Southwest quadrants and define the rectangle that our supplied address appears within. The API provides a couple methods to get to those boundary points: getNorthEast() and getSouthWest(). The two methods will extract the bounding box points so that we can use them in out ajax request.\nvar geocoder = new google.maps.Geocoder();\ngeocoder.geocode({address: 'Jax Beach, FL'}, function(results, status) {\n    var bounds = results[0].geometry.bounds,\n        center = results[0].geometry.location;\n    if (bounds) {\n        var ne = bounds.getNorthEast(),\n            sw = bounds.getSouthWest(),\n            data = { sw: [sw.lat(), sw.lng()], ne: [ne.lat(), ne.lng()]};\n\n            // ajax call to rails service API\n    }\n});\nThe Rails API - The Other Side\n\nNow that we have arrived on the other side of the tracks, we can discuss a couple of gems used to ease the implementation of our Rails API. Because querying the data comes before delivering it, we should start with the geokit-rails3 gem. This is a port of the geokit gem and provides a common interface for geolocation applications. It also allows us to make ActiveRecord models mappable simply by adding acts_as_mappable to our model. To query the database based on our supplied bounds, we use the geo_scope method. This is the gem's underlying method for scoped searches and we can chain methods to refine our result set like any other ActiveRecord query.\ndef geo_search(sw, ne)\n    self.geo_scope(bounds: [sw, ne]).where(active: true)\nend\n\nOnce we have retrieved the data from our database we can use the jBuilder gem to easily generate the JSON views. The setup is simple for our scenario and we can abstract some of the formatting to an application helper. Here is the format we will use in our result set:\njson.results do |json|\n    json.array!(searchables) do |json, result|\n        json.id result.id\n        json.type result_type(result)\n        json.name result.name\n        json.lat result.latitude\n        json.lng result.longitude\n        json.url url_for(result)\n    end\nend\n\nWhen you are testing the Rails service API with rspec, remember to add a call to render_views in your spec. Otherwise, the tests will not return the JSON views in the response.\ndescribe SearchablesController do\n    render_views\n\n    # add Fabricated records for testing\n\n    context \"search spots\" do\n        it \"returns our spot\" do\n            get :index, { format: :json, sw: sw, ne: ne }\n            data = JSON.parse(response.body)\n            data['results'].count.should eq(1)\n            data['results'][0]['name'].should eq( 'Jacksonville Beach Pier' )\n        end\n    end\nend\nPutting Our Map Together\n\nNow that we have our results, we need to add them to a map object. To do that we need to define a map and give it a few options. Note the map center point is being set through the options. This can also be set after the map is drawn using the setCenter() method. (For the full list of options check out the Google Maps API Map Options)\nvar opts = {\n    zoom: 10,\n    max_zoom: 16,\n    scrollwheel: false,\n    center: new google.maps.LatLng(center.lat(), center.lng()),\n    mapTypeId: google.maps.MapTypeId.ROADMAP,\n    MapTypeControlOptions: {\n        MapTypeIds: [google.maps.MapTypeId.ROADMAP]\n    }\n};\n\nvar map = new google.maps.Map($('#map'), opts);\n\nAt this point we need to iterate through our results set and add markers for each point to the map. The Google API also provides a Marker object to define a marker and these can be customized to level your application's needs, including the addition of custom images.\nvar marker = new google.maps.Marker({\n    id: result.id,\n    title: result.name,\n    position: new google.maps.LatLng(result.lat, result.lng),\n    cursor: 'pointer',\n    flat: false,\n    icon: new google.maps.MarkerImage('/assets/our_custom_icon.png',\n                                        new google.maps.Size(32, 35),\n                                        new google.maps.Point(0, 0),\n                                        new google.maps.Point(32 / 2, 35),\n                                        new google.maps.Size(32, 35))\n   })\n);\n\nAnd there you have it! A map that has all our results that fall within a boundary. What more could you want? Oh yeah, when you move the map or zoom in/out the bounds change but our results don't. For that we need to use the Google Map API events, most notably the \"idle\" event. This event is fired when the map display goes idle. Although there are specific events for panning and zoom, we have found that the \"idle\" event works best for this scenario.\ngoogle.maps.event.addListener(map, 'idle', function() {\n    // call your geolocation method\n});\n\nOur journey is now complete. We have a Google map that displays all the stored results that fall within a bounding box. The code here has been modified to fit neatly into a single post so its purpose is to be an example of Google Maps integration. We would suggest defining your map object using object literal notation so that the map functionality can be encapsulated and functions can be reused.\n\nSwiper, go swiping and have your own adventure with JavaScript, Rails, Goo-gle maps! \n","summary":"For anyone familiar with a certain little backpack wearing explorer, the layout of this post will seem eerily reminiscent of an episode. Not only because of her favorite tool, the map, but also because we are following her three step method: JavaScript, Rails, Goo-gle-maps! (I can hear her now)\n","image":"blog/bg_default_article_.png","date_published":"2012-08-29T20:00:00-04:00","data_modified":"2025-10-10T15:37:25-04:00","author":{"name":"Johnny Winn","url":"https://hashrocket.com/team/johnny-winn","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/39/johnny-winn.jpg"},"tags":["Ruby","Ruby on Rails","Javascript","Development"]},{"id":"https://hashrocket.com/blog/posts/three-jquery-plugins-we-wrote-and-one-we-love","url":"https://hashrocket.com/blog/posts/three-jquery-plugins-we-wrote-and-one-we-love","title":"Three jQuery Plugins We Wrote (And One We Love) ","content_html":"\r\nHere in Hashrocket Front-End Coding Land, we like our plugins lightweight, manageable and practical. Inevitably, this has resulted in some custom plugins that tend to find their way onto pretty much every project in one form or another. Here are four of those plugins, in no particular order. Hopefully one or more of these will help make your life easier as well.\r\n\n\n\r\n## Dummy Image\r\n\r\nThis is [Shane's](http://blog.hashrocket.com/user/shane-riley) newest creation – a lightweight solution to an issue that's been plaguing us for a while. We tried [dummyimage.com](http://dummyimage.com/), but it would occasionally bog down. In a moment of desperation, I even switched over to [placekitten.com](http://placekitten.com/) for a while, and that - while undeniably adorable - also had performance issues.\r\n\r\nOne of our guys wrote a Ruby gem to generate placeholder images, but one day Shane realized that this problem could be solved in a platform-independent manner using Canvas, and dummy_image.js was born.\r\n\r\n[Check out the Dummy Image site here](http://shaneriley.com/dummy_image) and throw it into your next project. It's a piece of cake to use and has become a standard part of every new project here.\r\n\r\n## jQuery.modal\r\n\r\nWe work on a lot of management UI, and inevitably it'll make sense to show something in a modal (lightbox, popup, whatever you want to call it). After messing with an assortment of prebuilt plugins, Shane rolled our own a while back, and it's been evolving from project to project.\r\n\r\nThe latest version has been ported to CoffeeScript and weighs in at a trim 3kb when minified. [Check it out on Github](https://github.com/shaneriley/modal) – while other plugins may have more options \u0026 features, we love this one because it generates minimal markup, is easily customizable, and does exactly what we need it to do.\r\n\r\n## Chosen\r\n\r\nChosen is a select-box enhancer written \u0026 maintained by the good people at [Harvest](http://www.getharvest.com/). It takes a standard dropdown and replaces it with a prettier one, complete with a search field and very nice multiple-select capability.\r\n\r\nWe run across scaling issues with dropdowns fairly often in our projects, and Chosen's autofill UI has become our standard fallback with any situation where a select box has to handle more than a handful of options. Although it's not particularly easily restylable, the design is tasteful enough that a restyle isn't really needed; it fits in nicely with standard browser controls.\r\n\r\n[Here's the homepage for Chosen](http://harvesthq.github.com/chosen/) – check it out, you'll undoubtedly have a use for it.\r\n\r\n## jQuery.minical\r\n\r\nAh, the infamous date picker. Every once in a while, a bit of UI will require the user to choose a date, and that's where the fun begins. Back before Minical, we'd wrangle together some jQuery UI codeball, plop in the [jQuery UI Datepicker](http://jqueryui.com/demos/datepicker/), and hope for the best.\r\n\r\nI finally couldn't take it anymore, realized that a good datepicker plugin was well within my capabilities as a designer-who-also-does-frontend-coding, and Minical was born.\r\n\r\n[I have a little site for it here](http://minical.camerondaigle.com/) with more info and examples. I recently rewrote it from the ground up in CoffeeScript, which resulted in a good learning experience and a vastly improved codebase. It's easy to customize and super-lightweight; I've been adding features as we've needed them. If you just need a calendar and don't need all of the extra features that jQuery UI provides, Minical just might be for you.","content_text":"Here in Hashrocket Front-End Coding Land, we like our plugins lightweight, manageable and practical. Inevitably, this has resulted in some custom plugins that tend to find their way onto pretty much every project in one form or another. Here are four of those plugins, in no particular order. Hopefully one or more of these will help make your life easier as well.\nDummy Image\n\nThis is Shane's newest creation – a lightweight solution to an issue that's been plaguing us for a while. We tried dummyimage.com, but it would occasionally bog down. In a moment of desperation, I even switched over to placekitten.com for a while, and that - while undeniably adorable - also had performance issues.\n\nOne of our guys wrote a Ruby gem to generate placeholder images, but one day Shane realized that this problem could be solved in a platform-independent manner using Canvas, and dummy_image.js was born.\n\nCheck out the Dummy Image site here and throw it into your next project. It's a piece of cake to use and has become a standard part of every new project here.\njQuery.modal\n\nWe work on a lot of management UI, and inevitably it'll make sense to show something in a modal (lightbox, popup, whatever you want to call it). After messing with an assortment of prebuilt plugins, Shane rolled our own a while back, and it's been evolving from project to project.\n\nThe latest version has been ported to CoffeeScript and weighs in at a trim 3kb when minified. Check it out on Github – while other plugins may have more options \u0026amp; features, we love this one because it generates minimal markup, is easily customizable, and does exactly what we need it to do.\nChosen\n\nChosen is a select-box enhancer written \u0026amp; maintained by the good people at Harvest. It takes a standard dropdown and replaces it with a prettier one, complete with a search field and very nice multiple-select capability.\n\nWe run across scaling issues with dropdowns fairly often in our projects, and Chosen's autofill UI has become our standard fallback with any situation where a select box has to handle more than a handful of options. Although it's not particularly easily restylable, the design is tasteful enough that a restyle isn't really needed; it fits in nicely with standard browser controls.\n\nHere's the homepage for Chosen – check it out, you'll undoubtedly have a use for it.\njQuery.minical\n\nAh, the infamous date picker. Every once in a while, a bit of UI will require the user to choose a date, and that's where the fun begins. Back before Minical, we'd wrangle together some jQuery UI codeball, plop in the jQuery UI Datepicker, and hope for the best.\n\nI finally couldn't take it anymore, realized that a good datepicker plugin was well within my capabilities as a designer-who-also-does-frontend-coding, and Minical was born.\n\nI have a little site for it here with more info and examples. I recently rewrote it from the ground up in CoffeeScript, which resulted in a good learning experience and a vastly improved codebase. It's easy to customize and super-lightweight; I've been adding features as we've needed them. If you just need a calendar and don't need all of the extra features that jQuery UI provides, Minical just might be for you.\n","summary":"Here in Hashrocket Front-End Coding Land, we like our plugins lightweight, manageable and practical. Inevitably, this has resulted in some custom plugins that tend to find their way onto pretty much every project in one form or another. Here are four of those plugins, in no particular order. Hopefully one or more of these will help make your life easier as well.\n","image":"blog/bg_default_article_.png","date_published":"2012-08-15T05:00:00-04:00","data_modified":"2013-11-26T10:41:31-05:00","author":{"name":"Cameron Daigle","url":"https://hashrocket.com/team/cameron-daigle","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/3/cameron-daigle.jpg"},"tags":["Javascript","Design"]},{"id":"https://hashrocket.com/blog/posts/jquery-san-francisco","url":"https://hashrocket.com/blog/posts/jquery-san-francisco","title":"Conference Wrap-Up: jQuery San Francisco","content_html":"\r\n\r\nAfter many years of watching jQuery conference dates come and go without attending, this year I finally got to go, and as a speaker to boot.\n\n\r\nAt jQuery conferences, you'd expect to meet all the big names in jQuery development, from [Ben Alman](http://twitter.com/cowboy) to [Jörn Zaefferer](http://twitter.com/bassistance), and San Francisco did not let me down in this regard. Many of the speakers were people whose work I've followed for years, so it was nice to finally meet the people behind the innovations. By far, the most memorable person I met was [Adam Sontag](http://twitter.com/ajpiano), a character and a half and someone who was made for conferences like this one.\r\n\r\nNearly every talk felt targeted towards an intermediate to expert level audience, which I greatly appreciated but could see how that might have put some potential attendees off. Given the Bocoup training session that was held just before, I would have figured there would be one or two basic-level talks to start off day one. The event was single-track with a breakout room for lightning talks to whomever wanted to present, which I hope is a pattern more and more conferences adopt. I would have been a bit upset if I didn't get to see both the [deferreds](https://github.com/alexmcpherson/jquery-talk/blob/master/jquery2012Defs.key) and [game dev](http://prezi.com/ggsq_slgcwq5/game-dev-on-the-ipad/) talks if it were a multi-track conference.\r\n\r\nThe highlight of the conference, however, was the very first announcement that jQuery 2 would drop all support for IE8 and below. I couldn't tweet that information fast enough. We've never really had issues with supporting IE8 at Hashrocket, but I know that dropping support leaves a lot of room for more features while keeping the code footprint the same. I can't wait to see what new goodies are introduced once this takes place. It was also mentioned that a new custom builder would be released for jQuery similar to what is currently on the jQuery UI site. I don't know how fine-grained it will be, but it will be great to pare down the library to a core subset for my quick demos and leave methods like $.ajax out.\r\n\r\nFor the evening of the first day, a semi-impromptu (scheduled during the speaker dinner the night before) hackathon was organized just after the drinkup in the first floor of the conference center. Over 60 people were hacking away at coding all of the newly designed jQuery websites, from the main site to the jQuery Foundation site. I chose to lead a group to code the jQuery Foundation site, jquery.org. Despite some initial hurdles, we were able to get a good start, and I was left so motivated to continue to help that I coded the remaining pages assigned to my group while waiting in the airport the morning after the conference. I'd love to show you the new designs, but there's still a lot of work to be done. However if you're curious enough, you can probably see the pages somewhere in my [GitHub](http://github.com/shaneriley) account ;)\r\n\r\nOverall, I think it was well worth the trip just for the conversations I had with other jQuery team members. I'd love to at least attend some of the other 12(!) jQuery conferences that are to be planned throughout the next year around the world, if not speak at them. Hopefully my next one will be the Toronto date! It's been a long time since I've been to Canada.\r\n","content_text":"After many years of watching jQuery conference dates come and go without attending, this year I finally got to go, and as a speaker to boot.\n\nAt jQuery conferences, you'd expect to meet all the big names in jQuery development, from Ben Alman to Jörn Zaefferer, and San Francisco did not let me down in this regard. Many of the speakers were people whose work I've followed for years, so it was nice to finally meet the people behind the innovations. By far, the most memorable person I met was Adam Sontag, a character and a half and someone who was made for conferences like this one.\n\nNearly every talk felt targeted towards an intermediate to expert level audience, which I greatly appreciated but could see how that might have put some potential attendees off. Given the Bocoup training session that was held just before, I would have figured there would be one or two basic-level talks to start off day one. The event was single-track with a breakout room for lightning talks to whomever wanted to present, which I hope is a pattern more and more conferences adopt. I would have been a bit upset if I didn't get to see both the deferreds and game dev talks if it were a multi-track conference.\n\nThe highlight of the conference, however, was the very first announcement that jQuery 2 would drop all support for IE8 and below. I couldn't tweet that information fast enough. We've never really had issues with supporting IE8 at Hashrocket, but I know that dropping support leaves a lot of room for more features while keeping the code footprint the same. I can't wait to see what new goodies are introduced once this takes place. It was also mentioned that a new custom builder would be released for jQuery similar to what is currently on the jQuery UI site. I don't know how fine-grained it will be, but it will be great to pare down the library to a core subset for my quick demos and leave methods like $.ajax out.\n\nFor the evening of the first day, a semi-impromptu (scheduled during the speaker dinner the night before) hackathon was organized just after the drinkup in the first floor of the conference center. Over 60 people were hacking away at coding all of the newly designed jQuery websites, from the main site to the jQuery Foundation site. I chose to lead a group to code the jQuery Foundation site, jquery.org. Despite some initial hurdles, we were able to get a good start, and I was left so motivated to continue to help that I coded the remaining pages assigned to my group while waiting in the airport the morning after the conference. I'd love to show you the new designs, but there's still a lot of work to be done. However if you're curious enough, you can probably see the pages somewhere in my GitHub account ;)\n\nOverall, I think it was well worth the trip just for the conversations I had with other jQuery team members. I'd love to at least attend some of the other 12(!) jQuery conferences that are to be planned throughout the next year around the world, if not speak at them. Hopefully my next one will be the Toronto date! It's been a long time since I've been to Canada.\n","summary":"After many years of watching jQuery conference dates come and go without attending, this year I finally got to go, and as a speaker to boot.\n","image":"blog/bg_default_article_.png","date_published":"2012-07-11T20:00:00-04:00","data_modified":"2013-11-26T10:41:31-05:00","author":{"name":"Shane Riley","url":"https://hashrocket.com/team/shane-riley","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/20/shane-riley.jpg"},"tags":["Javascript","Trip Reports","Community"]},{"id":"https://hashrocket.com/blog/posts/dabbling-with-backbone-js","url":"https://hashrocket.com/blog/posts/dabbling-with-backbone-js","title":"Dabbling with Backbone.js","content_html":"Here at [Hashrocket](http://www.hashrocket.com), every Friday afternoon is reserved for contributing to open source or learning new skills. Last Friday I decided to use this time to teach myself [Backbone.js](http://backbonejs.org/), with the goal of building a toy app with it in a few hours.\n\nThe idea is a simple \"leaderboard\" to rank [Rocketeers](http://hashrocket.com/people/team) by the number of \"hugs\" they received on Twitter - to \"hug\" a rocketeer, you  just add the hashtag \"#hugarocketeer\" on a tweet mentioning a rocketeer's Twitter handle. (You can see the implemented app [here](http://hugarocketeer.com).) The main reason for this idea is that I could lean on Twitter APIs to provide data so I can focus on just the front end with Backbone. I have never done a Backbone app before, and I didn't feel like starting from scratch. I grabbed the Backbone implementation of [ToDo.js](http://backbonejs.org/examples/todos/index.html) to serve as a starting point as well as to learn from the code base.\r\n\r\nFirst I mocked up the UI with plain HTML and CSS, just to have an idea what the end product will be. The ToDo.js serves as a great base here with all the supporting files and boilerplates set up, so I only had to change the \"body\" part of the index.html, and modified the css to make it look right.\r\n\r\n```html\r\n\u003cbody\u003e\r\n  \u003cdiv id=\"hug_a_rocketeer\"\u003e\r\n    \u003cul id=\"rocketeers\"\u003e\r\n      \u003cli\u003e\r\n\t\u003cdiv id='pic_and_name'\u003e\r\n\t  \u003cimg src='http://dummyimage.com/50x50/574b57/bcbfeb.png'/\u003e\r\n\t  \u003cspan\u003e Marian Phalen \u003c/span\u003e\r\n\t\u003c/div\u003e\r\n\t\u003cul id=\"props\"\u003e\r\n\t  \u003cli\u003e 3 Hugs \u003c/li\u003e\r\n\t\u003c/ul\u003e\r\n      \u003c/li\u003e\r\n      \u003cli\u003e\r\n\t\u003cdiv id='pic_and_name'\u003e\r\n\t  \u003cimg src='http://dummyimage.com/50x50/574b57/bcbfeb.png'/\u003e\r\n\t  \u003cspan\u003e Micah Cooper \u003c/span\u003e\r\n\t\u003c/div\u003e\r\n\t\u003cul id=\"props\"\u003e\r\n\t  \u003cli\u003e 4 Hugs \u003c/li\u003e\r\n\t\u003c/ul\u003e\r\n      \u003c/li\u003e\r\n    \u003c/ul\u003e\r\n  \u003c/div\u003e\r\n\u003c/body\u003e\r\n```\r\n\r\nExtracting Underscore template from the markup: \r\n\r\n```html\r\n\u003cbody\u003e\r\n  \u003cdiv id=\"hug_a_rocketeer\"\u003e\r\n    \u003ch1\u003e Hug a Rocketeer! \u003c/h1\u003e\r\n    \u003cspan\u003e Add #hugarocketeer to your tweet mentiong your favorite rocketeer, and watch them climb up on this board. :)\r\n    \u003cul id=\"rocketeers\"\u003e\r\n    \u003c/ul\u003e\r\n  \u003c/div\u003e\r\n\r\n  \u003cscript type=\"text/template\" id=\"person-template\"\u003e\r\n    \u003cdiv id='pic_and_name'\u003e\r\n      \u003cimg src=\"\u003c%= img_url %\u003e\"/\u003e\r\n      \u003cspan\u003e \u003c%= name %\u003e \u003c/span\u003e\r\n    \u003c/div\u003e\r\n    \u003cul id=\"hugs\"\u003e\r\n      \u003cli\u003e \u003c%= hugs %\u003e hugs \u003c/li\u003e\r\n    \u003c/ul\u003e\r\n  \u003c/script\u003e\r\n\u003c/body\u003e\r\n```\r\nOn to the javascript file: ToDo.js has a nice and small Backbone implementation and with helpful comments - I learned the fundamentals of Backbone just by reading the code. For our purpose, I kept the code structure and removed all its logic, and put in our own Backbone model, collection and view. \r\n\r\n```javascript\r\n$(function(){\r\n  var Person = Backbone.Model.extend({\r\n    defaults: {\r\n      hugs: 0\r\n    }\r\n  });\r\n\r\n  function retrieveTwitterInfo(element) { \r\n    {\r\n      img_url: 'https://api.twitter.com/1/users/profile_image?screen_name=' + element + '\u0026size=bigger'\r\n    }\r\n  };\r\n\r\n  var rocketeerTwitterHandles = ['marianphalen', 'mrmicahcooper', 'knwang'];\r\n  var rocketeers = rocketeerTwitterHandles.map(retrieveTwitterInfo);\r\n\r\n\r\n  var People = Backbone.Collection.extend({\r\n    model: Person\r\n  });\r\n\r\n  var PersonView = Backbone.View.extend({\r\n    tagName:  \"li\",\r\n\r\n    template: _.template($('#person-template').html()),\r\n\r\n    initialize: function() {\r\n      _.bindAll(this, 'render');\r\n      this.model.bind('change', this.render);\r\n\r\n    },\r\n    render: function() {\r\n      $(this.el).html(this.template(this.model.toJSON()));\r\n      return this;\r\n    }\r\n  });\r\n\r\n  var AppView = Backbone.View.extend({\r\n\r\n    el: $(\"hug_a_rocketeer\"),\r\n\r\n    initialize: function() {\r\n    },\r\n    render: function(){\r\n      var self = this;\r\n      _(this.collection.models).each(function(item){\r\n\tself.appendItem(item);\r\n      }, this);\r\n\r\n    },\r\n    appendItem: function(item) {\r\n      var itemView = new PersonView({\r\n\tmodel: item\r\n      });\r\n      $(\"ul#rocketeers\").append(itemView.render().el);\r\n    }\r\n  });\r\n\r\n  var App = new AppView;\r\n});\r\n\r\n```\r\n\r\nThere's no magic at this point, still just to render out a static page, but we are now running this through the Backbone framework. The AppView render() function loops through all the people and append each person's rendered html to \"ul#rocketeers\". We are, however, pulling off people's profile photo from directly from Twitter with their Twitter handle.\r\n\r\nThe next iteration took me a while. I wanted to actually hit Twitter's search API for each person to fetch mentions with the hashtag of \"#hugarocketeer\", and refresh the board on the callbacks. There's a bit of \"gotcha\" here: though Backbone supports automatically sorting of models in the collections, it happens at the time models are inserted into the collection. In our case, the retrieval of data from Twitter is asynchronous and the \"hugs\" attribute on Person won't get set until after they are inserted into the collection. So we cannot just rely on Backbone's collection sorting (by defining \"comparator\" on the collection.)  The solution I came up with was to clear the board and sort and re-render on every callback. The result is that you'll see the list flickering a few times until finally settling on the final rank.\r\n\r\n```javascript\r\n$(function(){\r\n  var Person = Backbone.Model.extend({\r\n    defaults: {\r\n      img_url: '',\r\n      name: '',\r\n      hugs: 0,\r\n    },\r\n    refresh_hugs: function() {\r\n      var self = this;\r\n      $.getJSON(this.get(\"search_url\"), function(data) {\r\n\tvar hugs = data.results.length;\r\n\tself.set({hugs: hugs});\r\n      })\r\n    }\r\n  });\r\n\r\n  var PersonView = Backbone.View.extend({\r\n    tagName:  \"li\",\r\n    template: _.template($('#person-template').html()),\r\n    initialize: function() {\r\n      _.bindAll(this, 'render');\r\n      this.model.bind('change', this.render);\r\n    },\r\n    render: function() {\r\n      $(this.el).html(this.template(this.model.toJSON()));\r\n      return this;\r\n    }\r\n  });\r\n\r\n  var rocketeers = ['marianphelan', 'mrmicahcooper', 'p_elliott', 'knwang', 'martinisoft', 'adam_lowe', 'bthesorceror', 'higgaion', 'camerondaigle', 'ChrisCardello', 'biggunsdesign', 'daveisonthego', 'jonallured', 'joshuadavey', 'videokaz', 'mattonrails', 'mattpolito', 'therubymug', 'shaneriley', 'shayarnett', 'voxdolo', 'syntaxritual', 'johnny_rugger'].map(function(twitter_handle){\r\n    return new Person({\r\n      img_url: \"https://api.twitter.com/1/users/profile_image?screen_name=\" + twitter_handle  + \"\u0026size=normal\",\r\n      name: '@'+ twitter_handle,\r\n      search_url: 'http://search.twitter.com/search.json?q=' + '@'+ twitter_handle + '%20' + \"%23hugarocketeer\" + \"\u0026rpp=100\u0026callback=?\"\r\n    });\r\n  });\r\n\r\n  var People = Backbone.Collection.extend({\r\n    model: Person,\r\n\r\n    comparator: function(person) {\r\n      return -person.get('hugs');\r\n    }\r\n  });\r\n\r\n  var AppView = Backbone.View.extend({\r\n\r\n    el: $(\"#hug_a_rocketeer\"),\r\n\r\n    returned_results: 0,\r\n\r\n    initialize: function() {\r\n      var self = this;\r\n      this.collection = new People();\r\n\r\n      _.each(rocketeers, function(person) {\r\n\t$.getJSON(person.get(\"search_url\"), function(data) {\r\n\t  var hugs = data.results.length;\r\n\t  person.set({hugs: hugs});\r\n\t  self.collection.add(person);\r\n\t  self.returned_results += 1;\r\n\t  if (self.returned_results == rocketeers.length) {\r\n\t    self.render();\r\n\t  }\r\n\t})\r\n      });\r\n      _.bindAll(this, 'render');\r\n    },\r\n\r\n    render: function(){\r\n      var self = this;\r\n      _(this.collection.models).each(function(item){\r\n\tself.appendItem(item);\r\n      }, this);\r\n    },\r\n\r\n    appendItem: function(item) {\r\n      var itemView = new PersonView({\r\n\tmodel: item\r\n      });\r\n      $(\"ul#rocketeers\").append(itemView.render().el);\r\n    },\r\n\r\n  });\r\n\r\n  var App = new AppView;\r\n});\r\n\r\n```\r\n\r\nThis is actually when I stopped last week after about 5 hours of hacking... not bad to pick up a new technology and make a fun app to entertain my coworkers.\r\n\r\nJust as I was finishing this blog post, I asked [@shaneriley](http://twitter.com/#!/shaneriley), our Javascript expert-in-residence, for the asynchronous callback ordering problem, and he suggested a better solution - I could count the number of returned callbacks from Twitter queries and render just once once I know all have come back. So here it is for that:\r\n\r\n```javascript\r\n....\r\n\r\nreturned_results: 0,\r\n\r\ninitialize: function() {\r\n  var self = this;\r\n  this.collection = new People();\r\n\r\n  _.each(rocketeers, function(person) {\r\n    $.getJSON(person.get(\"search_url\"), function(data) {\r\n      var hugs = data.results.length;\r\n      person.set({hugs: hugs});\r\n      self.collection.add(person);\r\n      self.returned_results += 1;\r\n      if (self.returned_results == rocketeers.length) {\r\n        self.render();\r\n      }\r\n    })\r\n  });\r\n  _.bindAll(this, 'render');\r\n},\r\n\r\n....\r\n```\r\n\r\n[Check it out live and start hugging rocketeers!](http://www.hugarocketeer.com)\r\n\r\nCode is [here on github](https://github.com/knwang/hugarocketeer)","content_text":"Here at Hashrocket, every Friday afternoon is reserved for contributing to open source or learning new skills. Last Friday I decided to use this time to teach myself Backbone.js, with the goal of building a toy app with it in a few hours.\n\nThe idea is a simple \"leaderboard\" to rank Rocketeers by the number of \"hugs\" they received on Twitter - to \"hug\" a rocketeer, you  just add the hashtag \"#hugarocketeer\" on a tweet mentioning a rocketeer's Twitter handle. (You can see the implemented app here.) The main reason for this idea is that I could lean on Twitter APIs to provide data so I can focus on just the front end with Backbone. I have never done a Backbone app before, and I didn't feel like starting from scratch. I grabbed the Backbone implementation of ToDo.js to serve as a starting point as well as to learn from the code base.\n\nFirst I mocked up the UI with plain HTML and CSS, just to have an idea what the end product will be. The ToDo.js serves as a great base here with all the supporting files and boilerplates set up, so I only had to change the \"body\" part of the index.html, and modified the css to make it look right.\n\u0026lt;body\u0026gt;\n  \u0026lt;div id=\"hug_a_rocketeer\"\u0026gt;\n    \u0026lt;ul id=\"rocketeers\"\u0026gt;\n      \u0026lt;li\u0026gt;\n    \u0026lt;div id='pic_and_name'\u0026gt;\n      \u0026lt;img src='http://dummyimage.com/50x50/574b57/bcbfeb.png'/\u0026gt;\n      \u0026lt;span\u0026gt; Marian Phalen \u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;ul id=\"props\"\u0026gt;\n      \u0026lt;li\u0026gt; 3 Hugs \u0026lt;/li\u0026gt;\n    \u0026lt;/ul\u0026gt;\n      \u0026lt;/li\u0026gt;\n      \u0026lt;li\u0026gt;\n    \u0026lt;div id='pic_and_name'\u0026gt;\n      \u0026lt;img src='http://dummyimage.com/50x50/574b57/bcbfeb.png'/\u0026gt;\n      \u0026lt;span\u0026gt; Micah Cooper \u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;ul id=\"props\"\u0026gt;\n      \u0026lt;li\u0026gt; 4 Hugs \u0026lt;/li\u0026gt;\n    \u0026lt;/ul\u0026gt;\n      \u0026lt;/li\u0026gt;\n    \u0026lt;/ul\u0026gt;\n  \u0026lt;/div\u0026gt;\n\u0026lt;/body\u0026gt;\n\nExtracting Underscore template from the markup: \n\u0026lt;body\u0026gt;\n  \u0026lt;div id=\"hug_a_rocketeer\"\u0026gt;\n    \u0026lt;h1\u0026gt; Hug a Rocketeer! \u0026lt;/h1\u0026gt;\n    \u0026lt;span\u0026gt; Add #hugarocketeer to your tweet mentiong your favorite rocketeer, and watch them climb up on this board. :)\n    \u0026lt;ul id=\"rocketeers\"\u0026gt;\n    \u0026lt;/ul\u0026gt;\n  \u0026lt;/div\u0026gt;\n\n  \u0026lt;script type=\"text/template\" id=\"person-template\"\u0026gt;\n    \u0026lt;div id='pic_and_name'\u0026gt;\n      \u0026lt;img src=\"\u0026lt;%= img_url %\u0026gt;\"/\u0026gt;\n      \u0026lt;span\u0026gt; \u0026lt;%= name %\u0026gt; \u0026lt;/span\u0026gt;\n    \u0026lt;/div\u0026gt;\n    \u0026lt;ul id=\"hugs\"\u0026gt;\n      \u0026lt;li\u0026gt; \u0026lt;%= hugs %\u0026gt; hugs \u0026lt;/li\u0026gt;\n    \u0026lt;/ul\u0026gt;\n  \u0026lt;/script\u0026gt;\n\u0026lt;/body\u0026gt;\n\nOn to the javascript file: ToDo.js has a nice and small Backbone implementation and with helpful comments - I learned the fundamentals of Backbone just by reading the code. For our purpose, I kept the code structure and removed all its logic, and put in our own Backbone model, collection and view. \n$(function(){\n  var Person = Backbone.Model.extend({\n    defaults: {\n      hugs: 0\n    }\n  });\n\n  function retrieveTwitterInfo(element) { \n    {\n      img_url: 'https://api.twitter.com/1/users/profile_image?screen_name=' + element + '\u0026amp;size=bigger'\n    }\n  };\n\n  var rocketeerTwitterHandles = ['marianphalen', 'mrmicahcooper', 'knwang'];\n  var rocketeers = rocketeerTwitterHandles.map(retrieveTwitterInfo);\n\n\n  var People = Backbone.Collection.extend({\n    model: Person\n  });\n\n  var PersonView = Backbone.View.extend({\n    tagName:  \"li\",\n\n    template: _.template($('#person-template').html()),\n\n    initialize: function() {\n      _.bindAll(this, 'render');\n      this.model.bind('change', this.render);\n\n    },\n    render: function() {\n      $(this.el).html(this.template(this.model.toJSON()));\n      return this;\n    }\n  });\n\n  var AppView = Backbone.View.extend({\n\n    el: $(\"hug_a_rocketeer\"),\n\n    initialize: function() {\n    },\n    render: function(){\n      var self = this;\n      _(this.collection.models).each(function(item){\n    self.appendItem(item);\n      }, this);\n\n    },\n    appendItem: function(item) {\n      var itemView = new PersonView({\n    model: item\n      });\n      $(\"ul#rocketeers\").append(itemView.render().el);\n    }\n  });\n\n  var App = new AppView;\n});\n\n\nThere's no magic at this point, still just to render out a static page, but we are now running this through the Backbone framework. The AppView render() function loops through all the people and append each person's rendered html to \"ul#rocketeers\". We are, however, pulling off people's profile photo from directly from Twitter with their Twitter handle.\n\nThe next iteration took me a while. I wanted to actually hit Twitter's search API for each person to fetch mentions with the hashtag of \"#hugarocketeer\", and refresh the board on the callbacks. There's a bit of \"gotcha\" here: though Backbone supports automatically sorting of models in the collections, it happens at the time models are inserted into the collection. In our case, the retrieval of data from Twitter is asynchronous and the \"hugs\" attribute on Person won't get set until after they are inserted into the collection. So we cannot just rely on Backbone's collection sorting (by defining \"comparator\" on the collection.)  The solution I came up with was to clear the board and sort and re-render on every callback. The result is that you'll see the list flickering a few times until finally settling on the final rank.\n$(function(){\n  var Person = Backbone.Model.extend({\n    defaults: {\n      img_url: '',\n      name: '',\n      hugs: 0,\n    },\n    refresh_hugs: function() {\n      var self = this;\n      $.getJSON(this.get(\"search_url\"), function(data) {\n    var hugs = data.results.length;\n    self.set({hugs: hugs});\n      })\n    }\n  });\n\n  var PersonView = Backbone.View.extend({\n    tagName:  \"li\",\n    template: _.template($('#person-template').html()),\n    initialize: function() {\n      _.bindAll(this, 'render');\n      this.model.bind('change', this.render);\n    },\n    render: function() {\n      $(this.el).html(this.template(this.model.toJSON()));\n      return this;\n    }\n  });\n\n  var rocketeers = ['marianphelan', 'mrmicahcooper', 'p_elliott', 'knwang', 'martinisoft', 'adam_lowe', 'bthesorceror', 'higgaion', 'camerondaigle', 'ChrisCardello', 'biggunsdesign', 'daveisonthego', 'jonallured', 'joshuadavey', 'videokaz', 'mattonrails', 'mattpolito', 'therubymug', 'shaneriley', 'shayarnett', 'voxdolo', 'syntaxritual', 'johnny_rugger'].map(function(twitter_handle){\n    return new Person({\n      img_url: \"https://api.twitter.com/1/users/profile_image?screen_name=\" + twitter_handle  + \"\u0026amp;size=normal\",\n      name: '@'+ twitter_handle,\n      search_url: 'http://search.twitter.com/search.json?q=' + '@'+ twitter_handle + '%20' + \"%23hugarocketeer\" + \"\u0026amp;rpp=100\u0026amp;callback=?\"\n    });\n  });\n\n  var People = Backbone.Collection.extend({\n    model: Person,\n\n    comparator: function(person) {\n      return -person.get('hugs');\n    }\n  });\n\n  var AppView = Backbone.View.extend({\n\n    el: $(\"#hug_a_rocketeer\"),\n\n    returned_results: 0,\n\n    initialize: function() {\n      var self = this;\n      this.collection = new People();\n\n      _.each(rocketeers, function(person) {\n    $.getJSON(person.get(\"search_url\"), function(data) {\n      var hugs = data.results.length;\n      person.set({hugs: hugs});\n      self.collection.add(person);\n      self.returned_results += 1;\n      if (self.returned_results == rocketeers.length) {\n        self.render();\n      }\n    })\n      });\n      _.bindAll(this, 'render');\n    },\n\n    render: function(){\n      var self = this;\n      _(this.collection.models).each(function(item){\n    self.appendItem(item);\n      }, this);\n    },\n\n    appendItem: function(item) {\n      var itemView = new PersonView({\n    model: item\n      });\n      $(\"ul#rocketeers\").append(itemView.render().el);\n    },\n\n  });\n\n  var App = new AppView;\n});\n\n\nThis is actually when I stopped last week after about 5 hours of hacking... not bad to pick up a new technology and make a fun app to entertain my coworkers.\n\nJust as I was finishing this blog post, I asked @shaneriley, our Javascript expert-in-residence, for the asynchronous callback ordering problem, and he suggested a better solution - I could count the number of returned callbacks from Twitter queries and render just once once I know all have come back. So here it is for that:\n....\n\nreturned_results: 0,\n\ninitialize: function() {\n  var self = this;\n  this.collection = new People();\n\n  _.each(rocketeers, function(person) {\n    $.getJSON(person.get(\"search_url\"), function(data) {\n      var hugs = data.results.length;\n      person.set({hugs: hugs});\n      self.collection.add(person);\n      self.returned_results += 1;\n      if (self.returned_results == rocketeers.length) {\n        self.render();\n      }\n    })\n  });\n  _.bindAll(this, 'render');\n},\n\n....\n\nCheck it out live and start hugging rocketeers!\n\nCode is here on github\n","summary":"Here at Hashrocket, every Friday afternoon is reserved for contributing to open source or learning new skills. Last Friday I decided to use this time to teach myself Backbone.js, with the goal of building a toy app with it in a few hours.\n","image":"blog/bg_default_article_.png","date_published":"2012-05-21T05:00:00-04:00","data_modified":"2016-08-25T11:46:27-04:00","author":{"name":"Kevin Wang","url":"https://hashrocket.com/team/kevin-wang","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/73/bb1ab55dc8c1931091dd2aadc9058475"},"tags":["Javascript","Development"]},{"id":"https://hashrocket.com/blog/posts/jquery-tips-adding-filter-expressions","url":"https://hashrocket.com/blog/posts/jquery-tips-adding-filter-expressions","title":"jQuery Tips: Adding Filter Expressions","content_html":"Are you writing complex filter methods like this to get at the elements you need?\n\n```javascript\r\n$(\"form\").find(\":input\").filter(function() {\r\n  return this.value === \"active\";\r\n});\r\n```\r\n\r\nThen it might be time to start writing your own jQuery expressions. They're really simple to make and are, in most cases, easier for others to read because it hides the DOM element inspection behind the expression filter.\r\n\r\nHere's a quick example: just like the example code above, you want to find only inputs with a given value. To convert this to an expression, you'd write something like this:\r\n\r\n```javascript\r\n$.expr[\":\"].value = function(el, idx, selector) {\r\n  return el.value === selector[selector.length - 1];\r\n};\r\n```\r\n\r\nThen you can find an input with a value of active using a selector like this:\r\n\r\n```javascript\r\n$(\":input\").filter(\":value(active)\");\r\n```\r\n\r\nIn the method you create for any jQuery expression, you are given three arguments. The first is the DOM element for that particular iteration. Second is the index of the DOM element relative to the original collection the expression was called on. Last is an array similar to what you'd get from a RegExp exec method call. The full expression is the first array position, second is the text from the selector between the colon and opening paren, and the last is the string that occurs within the parens. Let's take a look at another example to illustrate how this selector array is structured.\r\n\r\nHere's an expression that will return you any elements with a particular data attribute that equals a specific value.\r\n\r\n```javascript\r\n$.expr[\":\"].data = function(el, idx, selector) {\r\n  var attr = selector[3].split(\", \");\r\n  return el.dataset[attr[0]] === attr[1];\r\n};\r\n```\r\n\r\nIf you had a div with a data attribute of data-default=\"Hello world\" and you wanted to get at it in a single selector, you could write:\r\n\r\n```javascript\r\n$(\"div:data(default, Hello world)\")\r\n```\r\n\r\nIf you want to create multiple expression filters, it's best to use jQuery's extend method to combine a new object with your filter methods and the $.expr[\":\"] object.\r\n\r\n```javascript\r\n$.extend($.expr[\":\"], {\r\n  data: function(el, idx, selector) {\r\n    var attr = selector[3].split(\", \");\r\n    return el.dataset[attr[0]] === attr[1];\r\n  },\r\n  value: function(el, idx, selector) {\r\n    return el.value === selector[selector.length - 1];\r\n  }\r\n});\r\n```","content_text":"Are you writing complex filter methods like this to get at the elements you need?\n$(\"form\").find(\":input\").filter(function() {\n  return this.value === \"active\";\n});\n\nThen it might be time to start writing your own jQuery expressions. They're really simple to make and are, in most cases, easier for others to read because it hides the DOM element inspection behind the expression filter.\n\nHere's a quick example: just like the example code above, you want to find only inputs with a given value. To convert this to an expression, you'd write something like this:\n$.expr[\":\"].value = function(el, idx, selector) {\n  return el.value === selector[selector.length - 1];\n};\n\nThen you can find an input with a value of active using a selector like this:\n$(\":input\").filter(\":value(active)\");\n\nIn the method you create for any jQuery expression, you are given three arguments. The first is the DOM element for that particular iteration. Second is the index of the DOM element relative to the original collection the expression was called on. Last is an array similar to what you'd get from a RegExp exec method call. The full expression is the first array position, second is the text from the selector between the colon and opening paren, and the last is the string that occurs within the parens. Let's take a look at another example to illustrate how this selector array is structured.\n\nHere's an expression that will return you any elements with a particular data attribute that equals a specific value.\n$.expr[\":\"].data = function(el, idx, selector) {\n  var attr = selector[3].split(\", \");\n  return el.dataset[attr[0]] === attr[1];\n};\n\nIf you had a div with a data attribute of data-default=\"Hello world\" and you wanted to get at it in a single selector, you could write:\n$(\"div:data(default, Hello world)\")\n\nIf you want to create multiple expression filters, it's best to use jQuery's extend method to combine a new object with your filter methods and the $.expr[\":\"] object.\n$.extend($.expr[\":\"], {\n  data: function(el, idx, selector) {\n    var attr = selector[3].split(\", \");\n    return el.dataset[attr[0]] === attr[1];\n  },\n  value: function(el, idx, selector) {\n    return el.value === selector[selector.length - 1];\n  }\n});\n","summary":"Are you writing complex filter methods like this to get at the elements you need?\n","image":"blog/bg_default_article_.png","date_published":"2012-04-16T20:00:00-04:00","data_modified":"2013-11-26T12:08:42-05:00","author":{"name":"Shane Riley","url":"https://hashrocket.com/team/shane-riley","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/20/shane-riley.jpg"},"tags":["Javascript","Development"]},{"id":"https://hashrocket.com/blog/posts/selleck-a-jquery-handlebars-like-templating-system","url":"https://hashrocket.com/blog/posts/selleck-a-jquery-handlebars-like-templating-system","title":"Selleck: a jQuery Handlebars-like Templating System","content_html":"Recently, I was working on a project that refreshed search results and their corresponding Google Map markers via Ajax and wanted to make it as easy to write the JSON data to HTML as possible. I initially thought to use Handlebars and write my HAML partials as part of the view, but thought that might be more than what I needed for our use, and since we're loading jQuery, jQuery UI, Google Maps and our search script, I wanted to try to keep the total amount of code added to the front end as small as possible. 10 lines of code later, I had my [first version](https://gist.github.com/2214424) of [Selleck](https://github.com/shaneriley/selleck) parsing my JSON data and writing my HTML search results for me.\n\nAs a disclaimer, let me point out that Selleck isn't nearly as robust as Handlebars or Mustache (or underscore templates, etc.), but with all of the Hashrocket Javascript plugins, they are just enough to get the job done and give us the flexibility we want to make them useful in all of our projects.\r\n\r\nOn to the flashy bits! Here's how you'd use Selleck to transform a JSON object and HTML partial into a fleshed-out view. First, you write you HAML/HTML within a container that will show up on the page (or load it via Ajax!). Something like this will do the trick:\r\n\r\n```haml\r\n%script#tweet(type=\"text/selleck_tmpl\")\r\n\t%article(id=\"{{id}}\")\r\n\t\t%blockquote(cite=\"{{author}}\")\r\n\t\t\t%p {{tweet}}\r\n\t\t%time(datetime=\"{{post_date}}\") {{localized_post_date}}\r\n```\r\n\r\nThen you can take a tweet object like this:\r\n\r\n```javascript\r\nvar tweet = {\r\n\tid: \"08486\",\r\n\tauthor: \"@shaneriley\",\r\n\tpost_date: \"2012-03-30\",\r\n\tlocalized_post_date: \"Mar 30, 2012\",\r\n\ttweet: \"Waterfall Selleck sandwich!  http://vurl.me/QDSJ\"\r\n};\r\n```\r\n\r\nand write it to your page with jQuery like this:\r\n\r\n```javascript\r\n$twitter_stream.html(selleck($(\"script#tweet\").html(), tweet));\r\n```\r\n\r\nThat's it. You've got HTML that has been filled in with the JSON data that was a much lighter request than your standard $.load or other typical get-HTML-via-Ajax request and much cleaner than what you'd have to write to fill a series of HTML elements with object properties.\r\n\r\nSelleck will also process an array of JSON-like objects and create a completed HTML partial for each automatically. If our previous example were to look like this:\r\n\r\n```javascript\r\nvar tweets = [{\r\n\tid: \"08486\",\r\n\tauthor: \"@shaneriley\",\r\n\tpost_date: \"2012-03-30\",\r\n\tlocalized_post_date: \"Mar 30, 2012\",\r\n\ttweet: \"Waterfall Selleck sandwich!  http://vurl.me/QDSJ\"\r\n}, {\r\n\tid: \"08487\",\r\n\tauthor: \"@shaneriley\",\r\n\tpost_date: \"2012-03-30\",\r\n\tlocalized_post_date: \"Mar 30, 2012\",\r\n\ttweet: \"It's too bad Mustache May is no more.\"\r\n}];\r\n```\r\n\r\nSelleck would iterate over each tweet and return one combined string of all the filled-in partials.\r\n\r\nWhat about loops within a partial? Let's convert our tweet example to blog posts and add tagging. We may have 0 to n tags for each post, and rather than create another template for the tags and run Selleck on that collection afterwards, why not spit them out on first pass? Selleck's got you covered.\r\n\r\n```javascript\r\nvar posts = [{\r\n\tid: \"08486\",\r\n\tauthor: \"@shaneriley\",\r\n\tpost_date: \"2012-03-30\",\r\n\tlocalized_post_date: \"Mar 30, 2012\",\r\n\tcontent: \"Witty blog post goes here.\"\r\n}, {\r\n\tid: \"08487\",\r\n\tauthor: \"@shaneriley\",\r\n\tpost_date: \"2012-03-30\",\r\n\tlocalized_post_date: \"Mar 30, 2012\",\r\n\tcontent: \"Kickball at 2 this Saturday!\",\r\n\ttags: [\"kickball\", \"weekend\"]\r\n}];\r\n```\r\n```haml\r\n%script#post(type=\"text/selleck_tmpl\")\r\n\t%article(id=\"{{id}}\")\r\n\t\t%p {{content}}\r\n\t\t%footer\r\n\t\t\t%p\r\n\t\t\t\tby {{author}} on\r\n\t\t\t\t%time(datetime=\"{{post_date}}\") {{localized_post_date}}\r\n\t\t\t%ul\r\n\t\t\t\t{{- tags.each do |tag|}}\r\n\t\t\t\t%li {{tag}}\r\n\t\t\t\t{{- end}}\r\n```\r\n\r\nOh noes! Since there's no tags in the first post, we get an li with the text {{tag}} in it! That's embarrassing. Let's remove any empty elements while we're processing the template.\r\n\r\n```javascript\r\n$blog.html(selleck($(\"script#post\").html(), posts, { remove_empty_els: true }));\r\n```\r\n\r\nYay! Now we're rocking nested loops and removing any elements whose content is nothing more than an empty template tag. Note that this option doesn't remove an element if it has a combination of plain and template text. In the near future, I hope to add if blocks that will allow you to conditionally remove or provide alternate content for missing properties. For now, remove_empty_els is the only option you can pass to Selleck.\r\n\r\nAnd that's it! Now you no longer have to write those nasty Ajax callbacks that generate gobs of HTML in a place where it doesn't belong. Want to give it a try? Grab [the code from GitHub](https://github.com/shaneriley/selleck) and get to it!","content_text":"Recently, I was working on a project that refreshed search results and their corresponding Google Map markers via Ajax and wanted to make it as easy to write the JSON data to HTML as possible. I initially thought to use Handlebars and write my HAML partials as part of the view, but thought that might be more than what I needed for our use, and since we're loading jQuery, jQuery UI, Google Maps and our search script, I wanted to try to keep the total amount of code added to the front end as small as possible. 10 lines of code later, I had my first version of Selleck parsing my JSON data and writing my HTML search results for me.\n\nAs a disclaimer, let me point out that Selleck isn't nearly as robust as Handlebars or Mustache (or underscore templates, etc.), but with all of the Hashrocket Javascript plugins, they are just enough to get the job done and give us the flexibility we want to make them useful in all of our projects.\n\nOn to the flashy bits! Here's how you'd use Selleck to transform a JSON object and HTML partial into a fleshed-out view. First, you write you HAML/HTML within a container that will show up on the page (or load it via Ajax!). Something like this will do the trick:\n%script#tweet(type=\"text/selleck_tmpl\")\n    %article(id=\"{{id}}\")\n        %blockquote(cite=\"{{author}}\")\n            %p {{tweet}}\n        %time(datetime=\"{{post_date}}\") {{localized_post_date}}\n\nThen you can take a tweet object like this:\nvar tweet = {\n    id: \"08486\",\n    author: \"@shaneriley\",\n    post_date: \"2012-03-30\",\n    localized_post_date: \"Mar 30, 2012\",\n    tweet: \"Waterfall Selleck sandwich!  http://vurl.me/QDSJ\"\n};\n\nand write it to your page with jQuery like this:\n$twitter_stream.html(selleck($(\"script#tweet\").html(), tweet));\n\nThat's it. You've got HTML that has been filled in with the JSON data that was a much lighter request than your standard $.load or other typical get-HTML-via-Ajax request and much cleaner than what you'd have to write to fill a series of HTML elements with object properties.\n\nSelleck will also process an array of JSON-like objects and create a completed HTML partial for each automatically. If our previous example were to look like this:\nvar tweets = [{\n    id: \"08486\",\n    author: \"@shaneriley\",\n    post_date: \"2012-03-30\",\n    localized_post_date: \"Mar 30, 2012\",\n    tweet: \"Waterfall Selleck sandwich!  http://vurl.me/QDSJ\"\n}, {\n    id: \"08487\",\n    author: \"@shaneriley\",\n    post_date: \"2012-03-30\",\n    localized_post_date: \"Mar 30, 2012\",\n    tweet: \"It's too bad Mustache May is no more.\"\n}];\n\nSelleck would iterate over each tweet and return one combined string of all the filled-in partials.\n\nWhat about loops within a partial? Let's convert our tweet example to blog posts and add tagging. We may have 0 to n tags for each post, and rather than create another template for the tags and run Selleck on that collection afterwards, why not spit them out on first pass? Selleck's got you covered.\nvar posts = [{\n    id: \"08486\",\n    author: \"@shaneriley\",\n    post_date: \"2012-03-30\",\n    localized_post_date: \"Mar 30, 2012\",\n    content: \"Witty blog post goes here.\"\n}, {\n    id: \"08487\",\n    author: \"@shaneriley\",\n    post_date: \"2012-03-30\",\n    localized_post_date: \"Mar 30, 2012\",\n    content: \"Kickball at 2 this Saturday!\",\n    tags: [\"kickball\", \"weekend\"]\n}];\n%script#post(type=\"text/selleck_tmpl\")\n    %article(id=\"{{id}}\")\n        %p {{content}}\n        %footer\n            %p\n                by {{author}} on\n                %time(datetime=\"{{post_date}}\") {{localized_post_date}}\n            %ul\n                {{- tags.each do |tag|}}\n                %li {{tag}}\n                {{- end}}\n\nOh noes! Since there's no tags in the first post, we get an li with the text {{tag}} in it! That's embarrassing. Let's remove any empty elements while we're processing the template.\n$blog.html(selleck($(\"script#post\").html(), posts, { remove_empty_els: true }));\n\nYay! Now we're rocking nested loops and removing any elements whose content is nothing more than an empty template tag. Note that this option doesn't remove an element if it has a combination of plain and template text. In the near future, I hope to add if blocks that will allow you to conditionally remove or provide alternate content for missing properties. For now, remove_empty_els is the only option you can pass to Selleck.\n\nAnd that's it! Now you no longer have to write those nasty Ajax callbacks that generate gobs of HTML in a place where it doesn't belong. Want to give it a try? Grab the code from GitHub and get to it!\n","summary":"Recently, I was working on a project that refreshed search results and their corresponding Google Map markers via Ajax and wanted to make it as easy to write the JSON data to HTML as possible. I initially thought to use Handlebars and write my HAML partials as part of the view, but thought that might be more than what I needed for our use, and since we're loading jQuery, jQuery UI, Google Maps and our search script, I wanted to try to keep the total amount of code added to the front end as small as possible. 10 lines of code later, I had my first version of Selleck parsing my JSON data and writing my HTML search results for me.\n","image":"blog/bg_default_article_.png","date_published":"2012-04-16T09:00:00-04:00","data_modified":"2013-11-26T10:41:27-05:00","author":{"name":"Shane Riley","url":"https://hashrocket.com/team/shane-riley","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/20/shane-riley.jpg"},"tags":["Javascript","Development"]},{"id":"https://hashrocket.com/blog/posts/book-club-recap-javascript-patterns","url":"https://hashrocket.com/blog/posts/book-club-recap-javascript-patterns","title":"Book Club recap: Javascript Patterns","content_html":"Hashrocket Book Club is a chance for Rocketeers and viewers like you to meet up at 12:12 each week and walk through a book on any number of topics.\n\nIn the past we've done Ruby books, David Allen's [Getting Things Done](https://secure.davidco.com/store/catalog/Books-p-1-c-3.php), usability books, you name it ... but we just wrapped up a multi-week session on Stoyan Stefanov's [Javascript Patterns](http://oreilly.com/catalog/9780596806767). \n\nIt was an interesting couple of months, to be sure. While we found some common ground in many of his patterns (and a few were downright enlightening), there were definitely some sections that could have used a couple of more revisions, especially towards the end of the book.\n\nBut that's exactly why Book Club is great! With enough heads in the same space, those iffy parts tend to be caught more often than not, and make for great discussion material. \n\nHere's episode 1 in all of its Javascripty glory.\n\n\u003ciframe src=\"http://player.vimeo.com/video/18748237?title=0\u0026amp;byline=0\u0026amp;portrait=0\u0026amp;color=969696\" width=\"620\" height=\"340\" frameborder=\"0\"\u003e\u003c/iframe\u003e\n\nYou can watch the rest of the episodes [here on our Vimeo](http://vimeo.com/hashrocket/videos/search:javascript%20patterns/sort:newest). Enjoy!","content_text":"Hashrocket Book Club is a chance for Rocketeers and viewers like you to meet up at 12:12 each week and walk through a book on any number of topics.\n\nIn the past we've done Ruby books, David Allen's Getting Things Done, usability books, you name it ... but we just wrapped up a multi-week session on Stoyan Stefanov's Javascript Patterns. \n\nIt was an interesting couple of months, to be sure. While we found some common ground in many of his patterns (and a few were downright enlightening), there were definitely some sections that could have used a couple of more revisions, especially towards the end of the book.\n\nBut that's exactly why Book Club is great! With enough heads in the same space, those iffy parts tend to be caught more often than not, and make for great discussion material. \n\nHere's episode 1 in all of its Javascripty glory.\n\n\n\nYou can watch the rest of the episodes here on our Vimeo. Enjoy!\n","summary":"Hashrocket Book Club is a chance for Rocketeers and viewers like you to meet up at 12:12 each week and walk through a book on any number of topics.\n","image":"blog/bg_default_article_.png","date_published":"2011-03-09T19:00:00-05:00","data_modified":"2013-11-26T10:41:28-05:00","author":{"name":"Cameron Daigle","url":"https://hashrocket.com/team/cameron-daigle","avatar":"https://dkj231ikyz7c1.cloudfront.net/uploads/rocketeer/profile_image/3/cameron-daigle.jpg"},"tags":["Javascript","Video","Development"]}]}