{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application",
    "result": {"data":{"site":{"siteMetadata":{"title":"your friend Joel's digital garden","description":"Articles and notes from a collaborator at egghead.io. Musings on software, business, and life from a skilled virtual assistant.","author":{"name":"Joel Hooks"},"keywords":["Video Blogger"]}},"mdx":{"excerpt":"Respect your data. Contain your state. \nData and state are the foundation of your application. These two items should be\nabsolutely respected. As you work through the AngularJS documentation, these two\nitems are generally stored on the…","fields":{"github":"https://github.com/joelhooks/joelhooks-com/tree/master/content/legacy_blog/2013-04-24-modeling-data-and-state-in-your-angularjs-application.markdown"},"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"layout\": \"post\",\n  \"title\": \"Modeling Data and State in Your AngularJS Application\",\n  \"date\": \"2013-04-24T00:00:00.000Z\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", null, \"Respect your data. Contain your state.\"), mdx(\"p\", null, mdx(\"span\", {\n    parentName: \"p\",\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"250px\"\n    }\n  }, \"\\n      \", mdx(\"a\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-link\",\n    \"href\": \"/static/8f9eec445566df5625fedcea508c1814/63868/AngularJS-Shield-large.png\",\n    \"style\": {\n      \"display\": \"block\"\n    },\n    \"target\": \"_blank\",\n    \"rel\": \"noopener\"\n  }, \"\\n    \", mdx(\"span\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"106%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,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')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"a\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"AngularJS Shield large\",\n    \"title\": \"AngularJS Shield large\",\n    \"src\": \"/static/8f9eec445566df5625fedcea508c1814/63868/AngularJS-Shield-large.png\",\n    \"srcSet\": [\"/static/8f9eec445566df5625fedcea508c1814/63868/AngularJS-Shield-large.png 250w\"],\n    \"sizes\": \"(max-width: 250px) 100vw, 250px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\",\n    \"decoding\": \"async\"\n  }), \"\\n  \"), \"\\n    \"), \"\\nData and state are the foundation of your application. These two items should be\\nabsolutely respected. As you work through the AngularJS documentation, these two\\nitems are generally stored on the controllers. This works OK, but as your app\\ngrows beyond the \\\"todo list\\\" it quickly breaks down. Controllers need shared\\nstate, data needs to be contained, and it needs to be done in a consistent manner\\nthat is easy to comprehend.\"), mdx(\"p\", null, \"I've \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://joelhooks.com/2011/03/12/an-introduction-to-robotlegs-as3-part-2-models/\"\n  }, \"written about\\nthis\"), \"\\nin the past, in the context of ActionScript 3 and the Robotlegs framework. This\\napproach is valid for JavaScript, but requires some translation to build something usable for\\nAngularJS.\"), mdx(\"h2\", null, \"What is a Model?\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"A model notifies its associated views and controllers when there has been a\\nchange in its state. This notification allows the views to produce updated\\noutput, and the controllers to change the available set of commands. A passive\\nimplementation of MVC omits these notifications, because the application does\\nnot require them or the software platform does not support them.\\n\", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller\"\n  }, \"from\\nWikipedia\"))), mdx(\"p\", null, \"As the \\\"M\\\" in \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.codinghorror.com/blog/2008/05/understanding-model-view-controller.html\"\n  }, \"MVC\"), \", model classes \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"encapsulate\"), \" your application\\u2019s data and \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"provide an API\"), \" to access\\nand manipulate that data. The other classes in your application will make\\nrequests of models via this API. When data on the model is updated, the model\\ndispatches events that the other classes within your application can react to.\\nModels are appropriate for capturing the domain logic, such as performing\\ncalculations or other manipulations.\"), mdx(\"p\", null, \"An example of this might be a shopping\\ncart. When an item is added to the shopping cart model, the new total for all of\\nthe items in the cart will be calculated. The new total will then be stored on\\nthe model for access by other classes within the application. By placing this\\nlogic within your models, you ensure that it isn\\u2019t scattered across the entire\\napplication and know exactly where to look to see how and when your data is\\nbeing manipulated.\"), mdx(\"p\", null, \"In addition to controlling access to data, models maintain the state of your\\napplication. Consider a list of objects. You want to keep track of which of\\nthese objects is selected, so the data model has a selected property which is\\nupdated with the currently selected item. Other areas of your application can\\nnow access this property to discover which item is selected and react\\naccordingly.\"), mdx(\"p\", null, \"As you can see, data and state are intimately related. \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"State is data, data is\\nstate\"), \".\"), mdx(\"p\", null, \"Models are portable. There are many common sets of data that can easily\\ntransport between one application and the next. Think of, as an example, a\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"UserLoginModel\"), \" or a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ShoppingCartModel\"), \". Portability takes a bit more thought and\\nenergy, but no more than writing the same code over again for each project.\\nObviously every model isn't going to qualify for this, but many will so it is\\nsomething to watch out for.\"), mdx(\"p\", null, \"The model is the core of your application. The\\nvisual components get all the ooos and aaahs, but as a developer you know that\\ndata is the man behind the curtain. It is our jobs, as developers, to curate the\\ndata and deliver it to those beautiful interface items accurately. This is why\\nisolating domain logic in the model is so important. By isolating it you have\\nmade it easier to locate, update, and maintain.\"), mdx(\"p\", null, \"We've dug into what a model is, but if you're like me, you are waiting to see\\nhow to actually use models within an AngularJS application, so let's take a look\\nat that.\"), mdx(\"h2\", null, \"Exploring the Code\"), mdx(\"iframe\", {\n    width: \"100%\",\n    height: \"300\",\n    src: \"https://jsfiddle.net/joelhooks/jWmck/embedded/js,result,html/\",\n    allowFullScreen: \"allowfullscreen\",\n    frameBorder: \"0\"\n  }), mdx(\"p\", null, \"This is a simple/naive example that has a list of authors with a poignant quote from\\neach. If you explore the example you will quickly notice that all of the data\\nand state is stuffed in the controller. This is OK for a trivial demo, and\\nsomething more complicated/non-trivial is difficult to present in a blog post.\\nIt should be enough to present the concepts and gain some understanding on how\\nusing models can reduce the overall cognitive load of more complex applications.\"), mdx(\"p\", null, \"This \\\"everything stuffed in a controller\\\" approach works, but \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"we can do better\"), \".\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"note:\"), \" I'm using jsFiddle, and it puts obvious restrictions on how you\\norganize your code. I will be adding some thoughts on that in a future post, but\\na monolithic single JS file isn't going to scale very well.\"), mdx(\"p\", null, \"If you'd like to see a good write-up on structuring larger Angular apps, my\\nfriend \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://cliffmeyers.com/blog/2013/4/21/code-organization-angularjs-javascript\"\n  }, \"Cliff\\nMeyers\"), \"\\nwrote a great article on the subject.\"), mdx(\"h2\", null, \"Introducing a Model to Store Data\"), mdx(\"p\", null, \"In the above example, all of our data is stored within the controller on the\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$scope\"), \". It is all hardcoded in the app as well, but we will talk about service\\nintegration in the future. The task we want to accomplish now is a little\\nseperation of data and presentation. This doesn't mean that we won't be using\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$scope\"), \". We will. AngularJS uses the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$scope\"), \" as a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://martinfowler.com/eaaDev/PresentationModel.html\"\n  }, \"Presentation\\nModel\"), \". This is\\nfine, but we can still provide much nicer separation with a \\\"proper\\\" model for\\nthe data.\"), mdx(\"iframe\", {\n    width: \"100%\",\n    height: \"300\",\n    src: \"https://jsfiddle.net/joelhooks/jWmck/7/embedded/js,result/\",\n    allowFullScreen: \"allowfullscreen\",\n    frameBorder: \"0\"\n  }), mdx(\"p\", null, \"If you look at the JavaScript above, I think you will agree that it is already\\nstarting to look a bit cleaner. While the controller's \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$scope\"), \" is still\\nultimately supplying the view with the data it craves, the actual data is housed\\nin a model. The model is a 'singleton' (of the lowercase 's' variety) defined as\\nan Angular service.\"), mdx(\"p\", null, \"I found Angular's definition of \\\"service\\\" confusing at first. It has nothing\\ndirectly to do with what I consider a service, but is simply one of the methods\\nfor defining dependencies for injection.\"), mdx(\"iframe\", {\n    width: \"100%\",\n    height: \"300\",\n    src: \"https://jsfiddle.net/joelhooks/jWmck/9/embedded/js,result/\",\n    allowFullScreen: \"allowfullscreen\",\n    frameBorder: \"0\"\n  }), mdx(\"p\", null, \"This is more like it. Now we have a tiny controller, and all of our data and\\nstate is offloaded onto \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"authorListModel\"), \". We've added the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"setSelectedAuthor\"), \"\\nto the model, which dispatches an event. Our controller is listening for the\\nevent, so it updates the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$scope\"), \" appropriately and the view displays the\\ninformation we expect.\"), mdx(\"p\", null, \"This clean separation is going to pay huge dividends as the application grows in\\nscope. We can now easily separate the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"textarea\"), \" that contains the quote from\\nthe list of available authors.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"anti-pattern alert:\"), \" you might be tempted to add event listeners on your\\nmodel. Don't. It makes them harder to test and generally kills models in terms\\nof single-responsibility-principle. Since a model \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"has an\"), \" event dispatch, which\\ncan also listen for events, the temptation is always there. Ignore this warning\\nat your own peril! ;)\"), mdx(\"iframe\", {\n    width: \"100%\",\n    height: \"300\",\n    src: \"https://jsfiddle.net/joelhooks/jWmck/10/embedded/js,result,html/\",\n    allowFullScreen: \"allowfullscreen\",\n    frameBorder: \"0\"\n  }), mdx(\"p\", null, \"This is a bit silly, but shows the flexibility of this approach. The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"textarea\"), \"\\nis now driven by its own controller. That controller is also listening for the\\nupdate event the model dispatches. Instead of inspecting the model, it uses a\\nparameter sent with the event to update \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$scope.quote\"), \" and the magical Angular\\nbinding does the rest. Nice.\"), mdx(\"h2\", null, \"Do I really need to use events like this?\"), mdx(\"p\", null, \"Most definitely \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"not\"), \". Instead of the eventing I outlined above, you could\\nsimply bind to the model. One of Angular's greatest strengths is its awesome\\ntwo-way \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"{{binding}}\"), \".\"), mdx(\"iframe\", {\n    width: \"100%\",\n    height: \"300\",\n    src: \"https://jsfiddle.net/joelhooks/jWmck/12/embedded/js,html,result\",\n    allowFullScreen: \"allowfullscreen\",\n    frameBorder: \"0\"\n  }), mdx(\"p\", null, \"This is pretty nice, and is arguably cleaner than the eventing approach. In many\\n(most) cases, this approach might be preferred.\"), mdx(\"h2\", null, \"Conclusion?\"), mdx(\"p\", null, \"Models provide an excellent way to separate data and display. By migrating your\\ndata and state to a model, you have much more flexibility with how that data is\\npresented. Models are also \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"prime\"), \" candidates for unit testing, as they\\ntypically have exactly \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"one dependency\"), \" (some form of event emitter, in this\\ncase the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"$rootScope\"), \") and contain highly testable domain\\nlogic.\"), mdx(\"p\", null, \"Hopefully this gets you started down the road of using models in your AngularJS\\napps. In the near future, I will expand on models to discuss services (the\\nexternal kind) and how they play with this approach.\"), mdx(\"p\", null, \"If you have any comments, questions, or critique please share. I'd love to hear\\nabout how you solve this problem of separating view and data with AngularJS.\"), mdx(\"p\", null, \"If you are looking for nuts & bolts lessons on AngularJS, it is hard for me to\\nexpress in words how awesome \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://www.egghead.io/\"\n  }, \"John Lindquist's\\negghead.io\"), \" is. If you haven't already, go there now.\"), mdx(\"p\", null, \"Join the conversation on \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://redd.it/1d31fh\"\n  }, \"Reddit\"), \" and \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://news.ycombinator.com/item?id=5607330\"\n  }, \"Hacker\\nNews\")));\n}\n;\nMDXContent.isMDXComponent = true;","frontmatter":{"title":"Modeling Data and State in Your AngularJS Application","date":"April 24, 2013","banner":null,"slug":null,"keywords":null}}},"pageContext":{"id":"850ec444-559d-5cf0-a126-a75a6f5ff63c","prev":{"id":"b0046216-c8ca-59d6-bb34-48c977c066f6","parent":{"name":"2013-04-26-a-rose-by-any-other-name","sourceInstanceName":"legacy"},"excerpt":"I posted  an\narticle  about using models to assist in creating leaner, meaner controllers. It resulted in a lively conversation in the comments that spawned an interesting  rebuttal  from  Rob Conery  that essentially says I am full of it, and the…","fields":{"title":"A rose by any other name?","slug":"blog/2013/04/26/a-rose-by-any-other-name","date":"2013-04-26T00:00:00.000Z"}},"next":{"id":"e9af66f4-e970-5964-aec5-8aa0c8d2456e","parent":{"name":"2013-04-23-5-essential-vim-plugins","sourceInstanceName":"legacy"},"excerpt":"There are  a lot  of VIM plugins to choose from. An individual's list of what\nwould be considered \"essential\" is largely a personal matter. For any given\nplugin, there is also probably going to be an excellent alternative plugin that does the\nsame…","fields":{"title":"5 Essential VIM Plugins That Greatly Increase my Productivity","slug":"blog/2013/04/23/5-essential-vim-plugins","date":"2013-04-23T00:00:00.000Z"}}}},
    "staticQueryHashes": ["1045846374"]}