Introduction to Elm, v2 Interaction Exercise
Transcript from the "Interaction Exercise" Lesson
>> Richard Feldman: So for our exercise, we're going to actually implement the thing that we've been talking about in this section. We're going to change our homepage, and make it so that instead of displaying the complete list of feed articles, it's going to filter them based on which tag you have selected.
[00:00:16] So when you click a tag, it's going to say, this is a new selected tag. And based on that new selected tag, we're gonna show a different set of things in the feed. So let's hop on over to part3, so we'll cd into part3. And once again, we will open it up in our editor of choice.
[00:00:34] Okay, we will see here that the directory structure is very similar to before. One difference is now we have one extra thing inside source, which is we have a new file. So previously we just had Main.elm in source. Now we have Main.elm, we also have Article.elm. This is our second module ever.
[00:00:53] So let's take a look at what that actually does. So inside of our Main, you may notice that we have a new import in here. So previously we just had a couple of imports, Html, Html.attributes. We now have three new imports. One is Browser, which is going to give us Browser.sandbox, which is essentially a way to tell the Elm runtime, hey, here's what my view function is, here's what my update function is, and here is what I want you to initialize the model to.
[00:01:23] Previously, we just had it rendering HTML, so we didn't need to tell it any of that. But now that we're making things interactive, we actually have to specify what are view function is, what our update function is, etc. So we could name these whatever we wanted. By convention, usually, you name it view and you usually name it update.
[00:01:37] But if we wanted to, we could give them different names if we so desired. So that's what that browser import is for, it's just for that one thing. We also have Html.Events, and you can see that it's exposing onClick, whereas these two are exposing all the values that they have.
[00:01:53] So onClick, we're gonna use a little bit later for making it so that when you click on the tag, it actually does the interactive stuff we've been talking about. And these modules up here all come from a package of some sort or another. Whereas this one, this import Article, this actually comes from our code.
[00:02:12] So this is a module article. This is a new module that we've written that is basically just a way to move this hard coded data somewhere outside of Main.elm so it's not cluttering up Main.elm. This is not really a good use of Elm modules. As we'll learn in the advanced workshop, there actually is quite a lot of useful functionality to them, and this is not really such a great use of them.
[00:02:36] But I wanted to demonstrate if you want to make a new Elm module, you name it the same thing as thing as the file name without the .elm extension. So because this is Article.elm, it's called module article. And we're able to import it just by saying import Article.
[00:02:53] So there's no, once again, no need to introduce any kind of special build process for this. Elm just has a module system built into it, it just works right out the box. Okay, so that's what's going on with those imports. If we look into the README, it also mentions that we're going to need to install a new package for this one.
[00:03:13] Now for the rest of the workshop, I done this for you. But I wanted to demonstrate what it's like to install a new package. So on your command line, you gonna wanna copy and paste this command elm install elm/browser, which is the name of the package that includes that browser that we're now using for the first time.
[00:03:30] Okay, so I say elm install elm/browser, it says here is my plan, we're going to add elm/browser, and elm/virtual-dom which is a transitive dependency. Would you like me to update your elm.json accordingly? Yes I would, done. Okay, so again, this loaded dependencies from local cache, because we actually already had Elm browser downloaded and installed in our cache when we did the setup from earlier.
[00:03:53] So we didn't actually need to even go to the network to get that. It's like, well, that's already cached in your home directory, so I'm not gonna bother downloading it again. This works offline. So it happens that you're ever on an airplane or something, you have no WiFi, and you wanna work on a brand new Elm project, you can actually just install all of your dependencies.
[00:04:10] And it's all gonna work as long as they're all things that you've ever downloaded before. They're all just gonna be read straight of the cache, and it's not even gonna bother going to the network to see if there's anything different because there can't be anything different. So now we have Elm browser installed, and we are ready to complete the exercise.
[00:04:30] We're not really gonna go over elm.json. But I'll briefly mention what's going on in here. There's a source directory which lists, if you want to have other directories besides the source directory, you can list them there. This specifies what version of Elm we're building again, and then here are our dependencies.
[00:04:46] We are not gonna get into test-dependencies cuz we are not actually doing any test today, but dependencies break down into direct and indirect. Direct dependencies are things that we actually can use inside of our application. Indirect dependencies are essentially things that our direct dependencies need, and this is how the elm package installer has resolved them and figured out the versions that are compatible with one another.
[00:05:07] So these things, we cannot actually import in our project. The only reason they're in there is to make sure that when different teams are working on the same code base, they're all getting exactly the same version of these things resolved when they install. So in NPM, this is the equivalent or this is a separate file called packagelock.json, wherein Ruby there's like gemfile.lock.
[00:05:29] Elm just has this as part of elm.json, so you don't have a separate file to worry about, it's just right there in the elm.json. Okay, as for the actual exercise, we're gonna open source/Main.elm in our editor, and once again resolve the todos. We're gonna use the same build command as we did before.
[00:05:47] So if you've already got that on your terminal, you can just pull it up like before, and that should just work now that we've installed the browser package. So let's take a look at this Main.elm. All right, so you can see that we have a couple of sections here.
[00:06:05] And I've done this comment heading thing. This is a convention in Elm that's widely used, which is slice up your module by just having these all caps comment headers. As I previously mentioned, Elm's module system is used for more than just files. There's actually important boundaries about which things modules do and do not expose.
[00:06:27] So its considered best practice not to chop things up by moving them into different files, but rather just to say, this is my module, I want this module to do particular things. If I want different subsections within that, I'll just gonna put a heading, like I would if I were writing a blog post.
[00:06:41] So here's our section for the model, we have this initialModel. The reason I named it initialModel is because this top level model here is only gonna be used in one place. It's only gonna be used to kick off our application. After that, we're never gonna use it again.
[00:06:56] After that, we're only going to be working with the current model, which is to say an argument that gets passed into update, or an argument that gets passed into view. initialModel is only used to kick things off. Okay, so we have a couple of to-dos here. The first one is we're going to implement our update.
[00:07:14] Now update is going to be looking at a field called msg.description, like we talked about in the slides. And it's gonna say if that description is ClickedTag, then set the model's selectedTag field, which starts off being Elm, to be whatever msg.data happens to be. So if we get a message that says ClickedTag, and data is programming, then selectedTag should get changed to be programming.
[00:07:39] We're gonna use record update syntax to do that. Remember, you just need to return the new model with the update applied to it. Another hint, remember that if always needs an else. Okay, and then down here in the view, we're going to introduce filtering. So we already have a little bit of the syntax filled out here, so we have this list of articles, which is going to be used to render those articles on the side of the feed.
[00:08:04] And we're already calling List.filter on it. The only problem is List.filter is taking the world's most boring is keepable function. Article, it takes an article and then it always returns true. So we're gonna replace that with some actual logic so that it will filter it out, based on whether the article should or should not be rendered in the feed.
[00:08:22] And to figure that out, we're gonna need to use these three ingredients, List.member, article.tags, and model.selectedTags. So each of these articles has a list of tags inside of it, and we're gonna use List.member to try and determine, is the currently selected tag one of those tags for this article?
[00:08:43] That's what's gonna be our filter condition. If you wanna see the documentation for List.member, there is a link right here. So this is the Elm package website, this is where all documentation for Elm packages lives. And here is the member function, so it has an example of how you can call it, passing these different arguments to get back different return values.
[00:09:05] Okay, let's take ten minutes and go through these. I'm sorry, I missed something, the third to-do. [LAUGH] The third to-do is we're gonna add an onClick handler which links the two together. So back in update, we talked about we're gonna get a message that looks like this. It's gonna have a description and a data.
[00:09:22] Down here in this view, we are rendering one particular tag. So viewTag takes the currently selected tagName, which is gonna be passed in from the model. And then it takes the tagName of this particular tag that it's going to be viewing. So we wanna use that tagName to create this onClick to make sure that when we send this message to model it corresponds to the actual tag the user clicked.