This month at the St. Louis Angular Lunch (which we host), John Baur explained the “controller as” syntax, which has come to be the preferred and recommended approach. Afterward, he showed examples of dividing a long, complex controller into shorter, more modular controller-components. Here is a video – we are experimenting with the best way to record these lunch talks, hopefully the code is readable enough. We’ll do something better as we learn how.
Transcript
We have also transcribed the the talk to text, provided below. This is a rough, first-draft transcription, so any errors are probably in that proces./strong>
A few months ago, Oasis Digital started a monthly St. Louis Angular Lunch. At the November 2014 lunch, we had a guest speaker: Mark Volkmann of OCI. Mark talked about ES6 and the Traceur compiler, and briefly how they fit in to developing Angular applications today.
Transcript
We have transcribed the talk about to text, provided below. This is a rough, first-draft transcription, so any errors are probably from that process.
I wrote recently about data/API services for complex “single page” JavaScript-heavy web applications. Everything there applies to AngularJS as well as other frameworks (Ember, Knockout, React, etc.), but there are some particulars to keep in mind for ease of interaction between an AngularJS web application and a data service. This topic is also asked about in almost every “Angular Boot Camp” class I teach, so by posting this I have an online resource to point at.
Use RESTful URLs and HTTP Methods
By following RESTful patterns rather than inventing ad-hoc patterns for a server API shape, your code will fit in more easily with the rest of the software industry. This reduces the friction in bringing on more developers, in integrating your application with others, and so on. A RESTful server API will operate easily with AngularJS’s built-in mechanisms for working with a server, whether you use plain $http, $resource, or Restangular. For example, all of those already assume that the HTTP status codes indicate success and failure, so do not make up your own way of encoding failure within HTTP “200” success.
Avoid dogma in REST design, though. Most of the time, you can make a server API shape reasonably RESTful with approximately the same implementation effort as something ad hoc. If you have a case where a RESTful design will require substantially more work than something else, consider an “escape hatch”: a portion of your API that isn’t particularly RESTful, hidden behind a generic POSTable endpoint.
Make Data Binding Easy
I assume that by choosing AngularJS, developers aim to leverage its strengths rather than fight it. That means embracing “HTML enhanced”, data binding, and other banner AngularJS features, rather than manually manipulating the DOM (which immediately fails code review here), mostly passing information around using bindings and $watch rather than events, etc.
Therefore, if possible make your data service return data in a shape already suitable for binding in your AngularJS application. Done well, this can reduce the amount of JavaScript code you must write to populate a complex screen to almost nothing. By complex, I mean a screen which contains the modern equivalent of numerous master detail relationships, hundreds of fields, etc.
Of course, JavaScript is a powerful and convenient language for data structure manipulation, so in a sense it does not matter what shape of data is returned from a server; it is always possible to process that data into a shape suitable for data binding. Hence this guideline is oriented toward ease and speed of development when the server code is being written fresh.
(As an aside, I am not 100% sold on data binding; there are other approaches, like that taken by React, which have significant advantages. But if you are using AngularJS, embrace its strengths. Use data binding pervasively.)
Make Commands / Saves Easy Also
Following along from the above, a conveniently and idiomatically crafted AngularJS application will typically yield JavaScript data structures suitable for PUT or POSTing back to a data backend. Make your server accept PUT or POST of updated or new data in the same object “shape” as it returned data. This way, the data can make a round-trip from the server, through data bindings, to be manipulated by a user, then back to the server; all with very little code to write.
This applies to commands (in the CQRS sense) also. If a user is working with the screen via data binding, then that data binding can often yield a data structure ready to send to a server with a couple of lines of code. If you find it necessary to write extensive lines of code to formulate a server interaction, perhaps either your data binding or server should be adjusted stat.
Consider NodeJS on the Server Side, for Code Sharing
Working with complex client-side web development, you will almost certainly use NodeJS in your development toolchain, even if you are not using NodeJS for your data services.
But NodeJS is worthy of consideration for this key reason: it enables code sharing between the front end and back end. With some care in crafting modules, chunks of functionality which must be implemented on both sides of the client/server API (such as validation, but also many kinds of domain logic) can be reused across both, ensuring consistent implementation and eliminating duplication.
There are numerous other platforms worthy of consideration for other reasons. Stay tuned here (and follow me on twitter), I will have more to write about that.
Guard Your Innovation Budget
If your organization is in the process of adopting AngularJS for the first time, consider not simultaneously adopting a new server-side technology. As humans we each have a certain ability to absorb change, and doubly so for organizations comprised of many humans working together. You will probably get better results if you switch to a new client-side development toolkit in a different year then you switch to a new server side development toolkit. I think of this as having an “innovation budget” which can either be concentrated on one layer at a time, or diluted across more than one layer.
Help Wanted
If anyone knows of convenient source code examples already online of doing these things well versus poorly, please contact me – I would love to add links. Given sufficient time I may create the examples, but with the pace of change in our industry these tools could be obsolete before that happens!
Over the last few years the team at Oasis Digital has created various complex “single page” web applications, using AngularJS, KnockoutJS, and other tools. These applications’ assets and code are be statically hosted (cheaply on CDN if needed), but of course each application needs a backend data service (possibly comprised of smaller or even “micro” services internally) to provide data and carry the results of user operations to permanent storage.
Context and Background
Our work context is typically data- and rule-centric line-of-business applications, hosted in a company data center or on virtual/cloud or dedicated hardware, or occasionally a (more cloudy) PaaS like Heroku; so advice here is for that context. “Backend as a Service” solutions are appealing, but I don’t have any experience with those.
The systems we build most often store data in a traditional RDBMS like PostgreSQL or MS SQL Server, but data service needs are similar with a NoSQL or other non-RDBMS data store. (Among the many topics I should write about: making effective, justified use of polyglot persistence with CQRS and related ideas.)
We have also worked extensively with multi-tier desktop applications, which have essentially the same data service needs, albeit with different data serialization formats. As a result, we have worked on and thought about data services extensively.
Building a Data API Service
For convenient, rapid, efficient development of robust data/API services, your tool set should have as many as possible of the following:
A server-side programming language / platform, with its runtime environment, native or VM.
A way of routing requests (hopefully with a RESTful pattern matching approach).
Automatic unmarshaling of incoming data into data structures native to the programming language. If you find yourself writing code that takes apart JSON “by hand”, run away.
Similarly, automatic marshaling of ordinary data structures into JSON. If you see code which uses string concatenation to build JSON, it should either be to meet some specific needs for extra marshalling performance of a huge data structure, or shouldn’t be there at all.
Analogous support for other data formats. Successful systems live a long time. If you create one, someone will want to talk to it in a situation where JSON isn’t a good fit. A few years from now, you might think of JSON the way we think of XML now. Therefore, stay away from tools which are too deeply married to JSON or any other single data format.
If you’re using a relational database, your data server toolkit should make it quite easy to talk to that database. Depending on the complexity of the relationship between your data services and the underlying data store, either an object relational mapper (ORM) or perhaps a table/query mapper is suitable. If you find yourself working with the low-level database API to pluck fields out, you are looking at a long investment with little payoff.
Good support for a wide variety of database types (relational and otherwise). This reduces the risks from future database support requirements.
A reasonable error handling system. Things will go wrong. When they do, an appropriate response should flow back to the client code, while a fully detailed explanation should land in a log or somewhere else suitable – ideally without re-inventing this on each project or for every API entry point.
Depending on application needs, some way of maintaining a persistent connection (SSE, websocket, or fallback) to stream back changing information.
A declarative way to specify security roles needed for subsets of your API (RESTful or otherwise).
Monitoring / metrics.
Scalability.
Efficiency, so you are less likely to need to scale, and so that if you must scale, the cost isn’t awful.
Rapid development supported by good tooling. Edit-compile-run cycles of a few seconds.
A pony.
Is That All?
This is quite a checklist; but a toolset lacking these things means that a successful, growing project will probably need to reinvent many of them – shop carefully. In later posts, I’ll write more about particular technology stacks.
At the St. Louis Angular Lunch this month (which we sponsor), Mark Volkmann of OCI gave a talk on localization. In his talk he addresses how to setup AngularJS services and filters to determine which rules to apply for i18n and l10n runtime support. AngularJS includes good i18n support, probably because of extensive use inside Google across their global operations.
Video below:
Transcript
Here is the talk, transcribed to text. This is a lightly edited, draft transcription, so any errors are probably from that process.
AngularJS directives have a wide array of uses, some more difficult to implement than others. Understanding how the link function, directive controllers, scopes, required, restrict all interact and how to implement them can be daunting. Bill Odom and Matt Follett recently gave a talk at the St. Louis Angular Lunch explaining in detail about advanced directives they have written.
Transcript
We have transcribed the talk about to text, provided below. This is a rough, first-draft transcription, so any errors are probably from that process.
Here is another talk from the St. Louis Angular Lunch we sponsor / operate here at the Oasis Digital. In this talk, Mark Volkmann of OCI explains how to use AngularJS to extend HTML to include custom, modular components.
The ability to define custom directives is one of the most powerful features of AngularJS, and as always Mark does a great job summarizing it in a short lunch talk.
Enjoy the video, embedded below.
Transcript
We have transcribed the talk about to text, provided below. This is a rough, first-draft transcription, so any errors are probably from that process.
At a recent St. Louis Angular Lunch Brian Gilstrap talked about his experience with learning AngularJS. In his talk Brian outlined a classic scenario that we have seen in many of our Angular Boot Camp classes. A polyglot developer with years of experience finds themselves in need of, or with a desire to learn, Angular. What is the best way to get started and dig deep into Angular? See the video of Brian’s talk below.
AngularJS contains basic client-side routing functionality in-the-box. This is great for small applications and simple websites. However, the functionality needed for larger enterprise solutions is lacking. UI-Router is a third-party solution built for large AngularJS applications. At a recent St. Louis Angular Lunch Mark Volkmann of OCI gave a talk on using UI-Router in your Angular applications. A video of the talk is available below.
Transcript
Here is the talk, transcribed to text. This is a lightly edited, draft transcription, so any errors are probably from that process.
Mark: But I'm going to go over some basic things and then what I think are some more advanced things. So you can be the judge of that, and then we'll have some time for discussion after this. We've got 37 slides. We can't go through 37 slides in half an hour. So I'm going to skip some things and really try to focus on demonstrations and looking at code that runs those things.
So the basics are that in older versions of AngularJS, it came with the routing service, this thing called $routeProvider. And you can still use that but it doesn't come with Angular, and couple of the problems with that is that it didn't support nested views or sibling views. So now let's pull out core, you download it separately if you wanted but there are other alternatives and ui-router seems to be the most popular one probably because it does work nested views and sibling views.
If you want to use this, the few basic things you do to set it up. One is of course you have to pull in the JavaScript for this thing, and so that's the first box up there. Second you have to say that you depend on this. And I bet this is the thing that gets most people the first time they use it because the thing is called ui-router. But when you set up your dependency on it, it is ui.router, so catch the dot there. And then you set up views on your pages and the views can have names but they don't have to and so here is just a very simple one. It is a div tag but it is marked with the ui-view directive and you don't have to put any content in it. But if you do, then that's what will display until you put something into the view and so that can be a useful thing to have some default content. Then the biggest part of this is setting up the configuration so I'm doing that in the app.config and there are two services I need. I don't understand, maybe someone else can explain this to me why I choose services. Because I don't know why I can't set up my default state on state provider, why they didn't make the API that way. But it's not the way it is. And so I'm telling you with UI router provider, that if I don't tell you what state to go to, the default is whatever I specify there. Then the most important part is $stateProvider. I'm defining my states.
So in the older router service, states were defined by URL. You say you want to go to that URL now, you see that route. In ui-router, your states have names, and when you say you want to transition it to the state, that can be in a lot of things. In the most basic case, it means you're going to a route that has a certain URL associated with it. You want to use a certain controller and you want to use this template to display something. That's the basics. But beyond that, there are a lot of options we can specify when you configure a state and I've got four slides on this. I don't know if I have the time to talk about any of it. So blah blah blah blah and so let's see some real examples of using some of those options.
So first of all, assuming I have configured my states, you've got to have a way to switch from one state to another. And there are three basic ways to do this. One way, is that you get hyperlinks on the page and when the user clicks that hyperlink, you want to go to a new state. And so you do that by using the ui-sref directive on an anchor tag. So instead of href, it's ui for ui-router and s for state, switching through that state. So you don't give this a URL or a path. You give it the name of one of your states. And then in your JavaScript code, another alternative is to use the $state service, you call go and pass it the name of the state. And then last, if you actually navigate to a URL that's associated with one of your states, then you go into that state. If you could do it from JavaScript with $location.path or you can just type the URL in in the browser address bar and you'll go to that state.
The most basic example is just using the anchor tags that I talked about. So I have this kind of dummy weather forecast here and there are two ways you can look at this. There's the five-day forecast and the hourly forecast. If you just click the hyperlinks to switch between them. And so let me demonstrate this thing working. And so here it is, then I can click to look a the hourly forecast, and I can go back to the five-day forecast. Pretty basic stuff. So my view is that main part of the page, not the heading at the top. And then I'm able to switch between those two states.
So let's look at the code behind this. So here is my main HTML page and I'm pulling in of course Angular itself and then ui-router part, and then my own JavaScript weather.js and I've got some CSS. We don’t take the time to dig through the CSS. We just stress some nice things for us but then I have this div with the links in it and there you see the ui-sref. So I can switch between the hourly and the daily states, and then at the bottom, I have this div that's going to hold my view my view. And so I switch to a state, it's going to populate that div with the UI view directive on it.
Here are my two partials. So when I set up the states, I'm going to say these the templates I want to use, and so one for the five-day and one for the hourly. And this is some pretty basic Angular stuff here. I'm assuming that on my scope, I'm going to have day forecast which is a collection of day forecast objects. And those have properties on them of day, and the high and the low and similarly for the hour forecast. That's going to be on my scope and I can pull out the hour and the temperature.
On the JavaScript part, I'm setting up my main module which is called weather and you see my dependency on the ui.router module. And then I have my factory for the weather service. Now in a real application, I'd probably be making a REST call. I'd go out and get real data. But I'm just adding some dummy data here to demonstrate these ui.router. So I've got an array of forecast that I'm pushing in to that, a bunch of objects that represent an hourly forecast and then just returning it. And then similarly for the daily forecast, I have a day and a high and a low. And this is a very typical way that I set up my Angular services where I create this empty object that I call Service. And then I attach it onto a functions onto that and then at the end return that Service object. Of course when you define a service, if you want, you can return just a single function but it seems pretty common for me though and I want my services to have more than one function so that's a pattern I usually follow. And question, yes.
Kyle: Other than it's a ui-router, but there's this thing called factory, this thing called service. And unfortunately, it seems like your standard terminology that every [6:46 inaudible] pops is the safe service literally using factory. Do you have any insight deeper than what you get from the factory application that not touch on whether you’re supposed to use factory and service? You kind of accomplish the same thing in fact.
Mark: So it turns out that there are four different ways to define service and Angular and I have slides that show examples of these four different ways. But in my experience, it seems like 99% of the time people use factory. And I have yet to run across the case where I wanted to use one of the other three ways doing it. So maybe that could be a future talk or kind of a group discussion and talk about these different ways of…
Kyle: Paul is always using [inaudible].
Mark: I do. Yeah.
Male 1: One of that was on both parts of this is things people like to at least talk about the [7:30 inaudible]. Kind of a philosophy behind when you use different . . . .
Mark: Right, so even though I had value than the other three ways if I show examples of using them, other people might say, “Oh that's why I would use that.” So yeah we'll give you that. Then we get down to my controller, my weather controller at the bottom and I'm injecting into this, my weather service and then a colon, and this function. I'm filling that data on this scope so that I can display it in my view.
And then the most important part of this is defining my states. And so I'm setting that the default is to show me the daily view and then I have my two states defined. And each state has a URL associated with it. I'm telling it what controller I want it to use for getting the data that I'm going to render and then what is the template they'll use for displaying that view. Any question about that? This is really like the most basic thing here to do with ui router.
And then we'll talk about having sibling views when you switch to a state. It could be that your page contains multiple views and you want to update all of them when you go into this new state. And so this stage here has four views on it obviously, two borders up there. And there's a little bit of functionality here. When I'm in this state on the left and I click, red, green, and blue, it's going to change the color of the text that's right there. And when I'm in this state, and I click these, it's going to change the size of that text. So let me demonstrate that. Local 300. I can change the color of that text. And I'd switch to the other view, change the size of that text.
Okay so here is my main HTML and a big difference you'll notice here is they don't have just one UI. I have four of them and they have names. So I'll be referring to those names when I set up the configuration from my states.
So here's the CSS, we can skip past that. And then my partials, I have a lot more partials this time. I have four areas of the page so I have two partials for each of those areas. The headers and the body part, and the footer are all pretty simple and then that nav that was off to the left where I had all my functionality has some hyperlinks. And when you click those hyperlinks, and when you click those hyperlinks I want to call some functions that are on the scope to change the color or change the font size and then when I want to change what view I'm looking at when I'm in the first view, I want to be able to switch to the second one, or I should say state. When I'm in the first state, then when I switch to the second state, and when I'm in the second state, I can switch to the first state.
So on the JavaScript for this, again I'm creating my main module and I'm depending on UI router. Then in the controller, I think I'm doing something that people frown upon. I'm doing a little bit of dom work here but it's simple stuff. I'm just changing some CSS properties. So when I call change color on the scope, there has to be color name. And it's going to find all the elements that are sections and change their color. And if I go back to my main HTML, the only place I have sectioned right here that's the body, changing the color of everything in the section when I call that. Similarly for changing the font size, find all the sections and change their font.
And then for configuring this, that is really the interesting part, I tell it that I want to start in the first state, and then for the state provider, the green part there is what you in the boxes on this slide. Here's the definition of the first state. And the big thing that is different here is that rather than tell it about template URLs and controllers in that top object that I'm passing in, I have a views property which is not just has all of that in it. And the properties of the views I have picked are the names of my views. And so those are the four names I had and then each one has a template URL. The header and the body, and the footer, there's nothing active going on there. So I don't have a controller. But I need a controller for the nav part because I need to call some functions that the controller adds to the scope when you click those things. And so the second stage is very similar to that. Any questions about that? Okay.
Audience: And we have to provide those alternatives that are really the same like headers and shoulders are really the same.
Mark: They're not the same. I mean these two states.
Audience: Yeah.
Mark: Because there's one here, that's header one and this one uses header two.
Audience: Yeah I guess maybe I missed something. It looked visually like they didn't change at all.
Mark: Oh no, yeah let me go back to the browser. See it says header 2 and footer 2, and when I switch views, that changes in all four sections.
Audience: But you didn't specify those with [12:57 inaudible] when I switched states?
Mark: Right, nothing would happen.
Audience: So if I had sections on my pages, that are kind of static, I could just not touch it.
Mark: Although, probably in that case, you want what I'm going to talk about next which is nested views.
Audience: Okay.
Mark: Yeah. Alright so nested views, and this is maybe one of the more complicated parts of this. And I don't believe I'm describing all the options here but it seems like the common way of defining nested views is that there's a name of a state that is your parent state. And when you define one that is nested, you use a dot for that. So the presence of a dot in the name is what makes it be considered a child view. These URLS can be parameterized. Just by putting the colon there means that you'd be able to extract some data from it and do something different based on the passed in. One of the things I ran into is that you have to be careful about the order that you define these states. You have to define the parent states before the child, or else you'll get this error message but it doesn't really give you much of a clue of what you did wrong. Okay now, and if you do that parameterized URLs then you use the $stateparamservice to get to those values.
So this is how I want to demonstrate this. We have the Volkmann diner. At the top, I have three menus you can pick from, breakfast, lunch and dinner. And when you pick one, I'll display a different selection of items below that. And then when you click one of the hyperlinks for a food item, then it will show you a picture of it. And so the point of having nested views here is that we've already listed whether I want to see the spaghetti or the pizza, I'm still going to be in the dinner view, and then below that, nested inside that view, I'll show you a picture of the spaghetti or the pizza. So here it is and I'm looking at the dinner menu. I can switch to the lunch menu and the breakfast menu. Notice it says click an item to see the detail. That's the default content in that nested view. And then when I click this link, it gets populated with some other data. But that is the nested view inside the breakfast view.
Let's walk through that code. So in my main HTML, first I have the dinner controllers specified in my HTML because that's going to add to the scope, the string bulk, so it displays who's done or who this is. And then I have my hyperlinks for switching my state for the different meals, and then, finally this is where I'm going to display the menu but this view is going to have a nested view inside it. We'll look at the CSS.
Here are my partials. These are what I have for each of the meals of breakfast and then I'll have a bunch of these. I'm showing just a few here but you can add as many as you want there. And then similarly for the lunch, and for the ones that I do have a picture to show then I have a nested view. You notice the dot in here that's what makes it a nested view. So I have pictures for some of the food items. And then here is where I’m going to to display it. So notice that I have a ui view. It doesn't have a name, and neither did the parent one but it can keep that straight because they're nested. And so when I tell it to display the omelet, it's going to know to put it inside that view. I have my default content for what you see until you click on those.
Here are the partials for the actual food items. So they just have an h4 telling you what it is and then an image stack.
And then on to the main JavaScript, I set up my main module which is called diner and then I'm depending on UI router and the diner controller. All I'm doing is adding the name property to the scope. And then meal controller, I'm just putting this in so that I can demonstrate the use of the go method to change the state. So I'm saying wait two seconds and then switch to the lunch menu. And maybe I have that commented out of my actual code but that's just an example of how you can switch state from the Java code.
So as in all these examples, the most interesting part is setting up the state provider and so I have the parent state for breakfast and then it's got its own template. And then here's a child state for the omelet and notice the URL is just /omelet. Well you can't go to /omelet but you can go to /breakfast/omelet. So the URLS of the child states are appended on to the URL of the parent state. So the same kind of thing is happening with the lunch and dinner being parent states and lunch.pizza, a child of that, dinner.pizza, a child of that one. And that's all there is to that. So if I go back to the browser, I want to show you that if I go to the lunch menu, I'd see that there is pizza there. So if I go back to breakfast, and I go up to here in the browser, where I can type lunch/pizza and it should switch to showing the lunch menu and show me the picture of the pizza. And that's correct. Any questions about that?
The last thing I want to talk about is resolve. So the point of resolve is that when you switch to a new state, you don't necessarily want to begin rendering immediately. And that's because maybe some of the data that you're planning to render is going to come from a REST service and it's going to take a while for that data to come back. And your page can actually look pretty bad if you don't wait for the data to come back first. So let me show you an example of a bad page. So you notice that the page is going to come up immediately. Then some data is going to fill in and then some other data is going to fill in. Okay, so that's not nice. That's not to say that there won't be cases where you want to have your page up there and data is dynamically being plugged in so this is kind of a design sort of thing whether you want it to behave this way or not. But if I want it to wait until that data comes back, before I display something, I can do that. And so let's walk through the code first and then I'll show you that working in this better way. So I want it to display a table at the top, all that data's coming from a REST service and then I have a list at the bottom that's also going to come from a REST service.
So in my main HTML, there's nothing really special going on there. I have just one view but notice I have some default content that says the view is loading. So when I fix this, that's what you'll see initially. And then when I have the data, it'll all come up at once. And then, this is a partial that I'm going to display and so I’ve got the table at the top where I display all the marathons, and then there's the list of runners at the bottom. So marathons is going to be a collection that is added to the scope at some point. And runners is the same.
And then in the JavaScript, I'm creating my main module called marathons. I'm depending on ui-router. I have one service called the marathon service and this is an example of using the $qservice, you'll see that on the next slide. And also using a time out. And what I'm trying to do here is simulate a REST service without writing one.
So I've got the getMarathons as a function on my service. And normally right here, what you'd be seeing is perhaps a use of the $ http service to make a call-out to the REST service and that would return a promise and then you could wait for the promise to be resolved. But what I'm doing instead is I'm saying I'll create my own defer object and then I'm going to immediately return the promise. So something else can choose to wait on that and in this case, it's going to be the ui router and it's all of the functionality that's going to do that for me, yeah. But it wouldn't be any different if I was using $http.get to call the REST service. So I use the timeout service and say I want to wait 1 and a half seconds and when that's up then I'll create this w array of data. And then I call resolve and now that comes the value to get back from the promise. Similarly for the runners, it created a defer object. Immediately returning the promise. Then when one second elapses, return this array from the promise.
So in my controller, I want to call those two things but I don't have the data available until the promise gets resolved for each of them. So when I call to getMarathons, I'm getting back a promise. I call then on it, and I give it two functions: one for success and one for error. And if it's successful, then I attach that data onto the promise. And notice that the controller we're looking at here is bad controller. That's because it's going to render the data as soon as it becomes available. And it doesn't care about what other services you may be invoking. They'll all be handled independently. So as soon as I get the runner data back, then I’ll add back to the scope. And of course, the way I have it set up, that's the one that will come back first, because that one only waits one second and the other one waited a second and a half. So contrast that with the good controller where I'm injecting into the controller marathons and runners and we're going to see show that happens through resolve on the next slide. But here, we're just trusting that something is going to give me this data and when it gives it to me, I'm not waiting anymore. I've had it and I attach it to the scope and I'm ready to render that view. So at the bottom there I'm starting to set up my states and saying that I'm going to begin in the marathons state. And turns out in this example, I only have one state, but the reason I'm using ui router even with one state is that I want this resolve capability.
So at the top showing I'm showing setting this up to use the bad controller and it's really simple. I just tell it what URL is associated with the state, what template I want to use. Notice both of them are using the same template and then that one has the bad controller that makes the calls and whenever the data comes back, attaches it to the scope. In the good one though, I have this resolve property. And I'm telling it do not try to render this view until all of the promises return down here, come back. So you've the passed the resolve an object where the keys are the names of things that can be injected into the controller. So marathons and runners, and if I go back to the previous slide, that is where these things are coming from. Those names have to match up. And then I return anything that will give me back a promise and these functions give me back promises. So I'm all set and that's going to know that it should wait until all of these things get resolved before it attempts to render that. Yes.
Audience: So I'm a noob at all this but I know [24:53 inaudible] in some places.
Mark: Yes and the way to fix this is that the key thing is wherever you're injecting things like this, —i's a bit ugly—but what we have to do is instead of passing in a function, you have to pass in an array where the things in the beginning of the array are all strings. There are these names just in quotes. And then the last thing it's the function. And so then the identifier and go ahead and change these to something else but because it has the actual name as a string, it's able to view on that. So you have to write the code a little different to prepare that.
Kyle: Mark, we've stopped doing that, and one of the process is switching projects to use a grunt plug-in to perform that transformation programmatically.
Mark: Oh cool.
Kyle: What do you think about that? On one hand, we're kind of like let’s step farther away from us writing the code that runs. In other hand, the duplication between the strings, it’s just morally offensive there, the duplication in there.
Mark: I like what you’re saying because the code is just ugly the other way. Did someone create that plug-in or did you create it?
Kyle: We didn't create it. It's just sitting out there. It's a thousand other pre-1.0 plug-ins on GitHub.
Mark: I'd like to know what the name . . . .
Kyle: Well somebody here, he can probably tell you what it is.
Mark: Yeah.
Audience: One of the things that we run into is we resolve very heavily and that means that we have a lot of things like this where we don't have to pester. Can you come up with a good way to [26:24 inaudible] the actual outside of [inaudible] that some people messed up the resolve blocks?
Mark: Well I guess I have to admit that my own personal preferences lean heavily towards integration or end test. And so if I wrote that kind of test, if I'm understanding your question right, that wouldn't be an issue because I'm just going to run the whole code and look at what ended up on the dom.
Kyle: So you're talking about testing whether getMarathons works?
Male 1: Well so here's an example that's very simple for this, just one line to return Marathon service to getMarathons. But like if you were saying doing a resolve that’s taking state parameters something like that and then performance or some service calls off of that, that might actually be something significant enough.
Kyle: So my preference would be if that function was any less trivial than that, it would be to make it delegate to something in some other code.
Male 1: Kind of like the service that . . . .
Kyle: Yeah right and test that.
Mark: Okay so I'm pretty much done. The only thing additional that I want to say is that there's some events you can listen for that are associated with this and it's pretty useful for everybody and especially listening for state change errors and getting some details so that you know that there was an error when you were in this state and you tried to transition to this other state, or maybe you tried to go to a state that you never defined and you get a state that you’re done. So these are things you could listen on and these are all on the root scope and so you could listen to them from any scope and you'll get that. And so there's some examples of the syntax there and how you set that up. And then you can listen for a view to finish being loaded here. You get the view content loaded event. That's all I've got. Did I make it in half an hour? Probably not.
One of our specialties at Oasis Digital is in rules-based configurator systems of various types. For example, we have worked on complex model number configuration systems, quotation systems with complex sales commission rules, graphical system of configuration widgets, and other similar tools.
For our 2013 intern program, one group (led by Zach Kimberg) worked on a 3-D rules-based configuration system. This project lasted about half of the summer, and got far enough along to produce a visually interesting demo, shown below. The screenshot doesn’t really do it justice – it is dynamic and continuously moving.
This works entirely as a web application, with no use of flash or any other plug-in. It relies on WebGL, built-in to most modern web browsers. It is coded entirely in JavaScript, with help from AngularJS for the user interaction and Three.js for the 3-D model management. The 3-D model is hacked up and simplified from a 3-D model example from a website we have forgotten.
We see this as a proof of concept showing that it is possible today to build complex rules-based configuration systems, with a rich visual display, that run easily in a browser. It is no longer necessary to build difficult-to-deploy desktop software for this type of need.
Update: We recently added this short video demo of the configurator. Unfortunately the video doesn’t show the rules in action – there are rules to block certain combinations of settings in the configuration. The video shows a slightly jerky rotation of the swing-set – in the live software, it is completely smooth. Enjoy: