Advanced Elm Html msg
Transcript from the "Html msg" Lesson
>> Richard Feldman: The third thing we wanna talk about is Html msg. That is msg with a lowercase m. So this is a form of reuse in which we're essentially saying, I've got a piece of my view, some view code, some rendering logic for the UI. And I wanna use this in multiple modules, or potentially in multiple places.
[00:00:17] I guess it could be within the same module. Here's an example of the absolute, simplest possible, reusable view you can get. Html msg, it's not even a function. This is our loading icon. And if you reference it with loading.icon, in those places where we were doing the loading slowly custom type variant, this is what we displayed to the user.
[00:00:42] We don't wanna write out this entire asset.src, and specify the width, and height, and the alt text every single time. So I just extracted one piece of code here. This is a helper, it's not even a helper function. That just says I'm gonna do all that for you, and you can drop it in anywhere.
[00:00:57] The reason you can drop it in anywhere is because it has an unbound type variable. So no matter what page I'm on, no matter what that page's message type is, this will work with it. I can just drop it in, no sweat. We have a slightly more complicated version for error.
[00:01:10] As you can see, it's doing a whole lot of work here. It takes the string and then wraps it in the words, Error loading, and then passes that to Html.text. Really complicated one, super glad that I don't have to write that everywhere. Also, I mean, there is some benefit to centralization.
[00:01:25] I mean jokes aside, it's a very small amount of reuse, but it is still useful. But if I ever decided that I wanted to format the errors in a particular way, change the font or maybe color them red, because I'm reusing this so much different places, I can change it in one place and have it be consistent across all of my views.
[00:01:42] One of the considerations that comes up for views, that doesn't typically come up when we're using business logic is that, in a lot of cases, there is an element of consistency that you want across your branding, or your theming, or things like that. And reuse is a good way to achieve that.
[00:01:55] So make sure that you're staying consistent among things. And if you change something in one place, it's going to get changed across the board. Having said that, there's also a bit of a risk where, just as in our data model that we saw earlier, sometimes things are the same.
[00:02:09] But actually, some other times, things are slightly different, but they're sorta temptingly close to being the same. And so you can end up with UIs where, if I use this grid layout and I specify these widths exactly right, that'll be really great. And then you come across a situation where it's like, yeah, but really the best design is to have this not quite in the grid.
[00:02:29] And there's sort of an innate pressure to say, yeah, but I wanna reuse all this nice grid stuff that I have and all these helpers. It's gonna be so beautiful and easy, from a code perspective. Maybe I'll just let the user sacrifice that, and I'll just do this slightly suboptimal design, so that I can reuse this code.
[00:02:44] It's just so much of a trap on the side of user interfaces as it is anywhere else, arguable more so. So again, there's that word of caution, that it can be a pro, but with great power comes great responsibility. Always be asking the question, are these things actually the same, should I use reuse here?
[00:03:01] Or is it actually a better idea to just say, nope, these are different, I'm gonna implement them separately. Okay, so these are two examples of using Html with unbound type variable. Where in both cases, they're just returning Html that's compatible with absolutely anything. But sometimes, you have a type variable that is bound to something else in the function.
[00:03:22] So here's an example of that. This is our follow and unfollow buttons from earlier. So both of these have a fairly complicated msg type here, as we talked about previously. Because that's actually, the goal of the design is to encourage having a msg type that incorporates Cred and unfollow an author.
[00:03:42] But we can look at another example of reuse that actually takes a simpler msg type, because it doesn't have that design goal, which is favorite and unfavorite. So this is the equivalent of following, but for articles. You can favorite an article or fave it, if you will, by clicking on it and giving it a little heart.
[00:03:57] And now that appears in your list of favorites. And also that author gets bragging rights of the additional heart showing up on that article. So this takes a couple of arguments. But for our purposes, we're most interested in the ones that have this msg type variable. So Cred is serving the same function as it did before, saying that you should only be able to render a favoriteButton if you're signed in.
[00:04:19] But the msg here is saying, hey, give me a msg that represents favoriting something. That's what I'm gonna produce when I click. And then, I'm also gonna take a list of attributes and a list of children, basically so that I can call favoriteButton in the same way that I would call button from the Html library.
[00:04:36] This is a design where you sort of intentionally make it so that when you call this thing, you're going to specify a list of attributes and a list of children, and sort of incorporate those into whatever it is that you're deciding to reuse. So we can see, these both delegate to toggleFavoriteButton.
[00:04:54] Which is a helper function, much like we saw earlier. And once again, all it does is it renders this same structure that we saw with the follow button, which is an Html.button that's got an i in there, with the class ion-heart. And then it just takes the class that we specified.
[00:05:10] Which is to say, either this one or this one, depending on whether it's fave or unfave, and then attaches that to the other list of default attributes that we wanna pass along. By the way, worth noting that this is an example. This favoriteButton is an example of the thing that we talked about previously with the idea of enforcement arguments.
[00:05:28] This actually is not using the Cred that you give it. It really just throws it in the trash. It's only being passed in because we want to enforce that you only can render favorite and unfavorite buttons when you're logged in. So the last piece to note about this is that, again, we are not actually returning Html with an unbound type variable.
[00:05:46] It's bound to the msg in these other things. So what this is essentially saying, is it's saying hey caller, you need to tell me what the msg is that you're gonna use to record that the user clicked favorite, and another one that you're gonna use to tell me that the user did unfavorite.
[00:06:04] This is the sorta pattern I like to call, teach me how to message. It's like, I don't actually know how to do one of these messages. I don't have a notion of favoriting and unfavoriting. You've gotta teach me that. When you call me, tell me how to do that, and I'm just gonna wire that up to the onclick handler.
[00:06:19] Which is exactly what it does. So we can see how this thing is used, if we search for it in the code base. It's used in a couple of different places. So used on the article page, it's used on the article feed. And it's used on the,
>> Richard Feldman: Actually, I guess just those two places.
[00:06:35] So here it is in the feed. And here it's passing it an actual concrete. Sorry, that was the favorite, not the, or yes, sorry, that is what we want. So here it's passing this ClickedUnfavorite and ClickedFavorite, depending on which of the two it is. And that's part of an actual concrete Msg type.
[00:06:54] So this is an example of this module sort of teaching the fave and unfave buttons how to record one of these messages when the user clicks that action. But everything else is being reused inside of that. So this thing doesn't need to know about the actual structure of that button.
[00:07:08] It just says, hey, I'm gonna give you some Cred. I'm gonna give you the clicked fave and unfave messages, and you just make sure those get sent when appropriate.
>> Richard Feldman: Questions about that as a tactic for reuse, passing in a msg? And by the way, you're not limited to just one msg.
[00:07:25] Another thing you could do here is you could say something like, fave is this msg. And then we can have something else that's like, I don't know, funkyFave, which another msg. Then we have superFave, I don't know. We could have a bunch of different configuration options here and say, give me different messages for different scenarios, and I'm gonna wire them all up.
[00:07:45] Who knows how. But in this case, we only need to give them one message and that's enough.