Angular Lesson 3

Much as I hate to do something so mundane, I think it’s time to explore how Angular handles passing data down into child components and up into parent components. I’m going to aim this lesson slightly downward from my level of experience, so it may be of more use to people who are just starting to learn their first modern JavaScript framework. In other words, this is a beginner-intermediate lesson. But even so prepare to follow whatever side missions I run into that seem interesting. I don’t like being bored, so I don’t write micro-lessons where you learn one tiny thing in isolation.

If you want to actually understand what I write here, I suggest following along in your own coding environment and also referring to the code I wrote which is available at
https://github.com/xerocross/angular-examples under the branch ng-parent-child-example. You can find the relevant source files here:
https://github.com/xerocross/angular-examples/tree/ng-parent-child-example/src/app.

I don’t even have to be familiar with Angular to know that there has to be some way to pass data down into children, and there also has to be some way for children to pass data back up. Here and throughout, when I refer to parent-child relationships between components, I mean composition. This has nothing to do with inheritance.

Modern tools and techniques strongly encourage separation of these actions. There’s something called two-way data binding. That would mean that if either parent or child changes the data, then the other gets updated with the new value in some kind of automatic way. And that is usually something we don’t want. Because of the hierarchical nature, the child component should take whatever the parent gives it and it should always be kept up to date. But we no longer like to give the child direct access to modify that data.

For example, Parent might contain some state data, and Child might be a dumb form component that gets auto-filled with some of this state data from the parent. The purpose of this form is to give a user the ability to alter some of this state data, but the child does not get to make that decision. The child just interacts with the user, and when the user changes a value or presses a button or something, the child simply alerts the parent that this event happened. If there is some payload, like the value of the input from the user, that can get sent along with the event.

Back at the Parent level, the parent receives the event and the payload and acts on it or ignores it as the Parent sees fit. This is the crux of one-way data binding as it differs from two-way data binding. And this is a design pattern, not something specific to any framework—certainly not Angular specific. I know that Angular will do this without even checking any books because you can’t not. Any current framework must support this pattern.

What we need to do is learn is how you do this in Angular. But let’s try to make it interesting somehow, because otherwise this is just a trip to the hardware store to buy some plumbing equipment.

Let’s start with a fresh Angular application. I’m using the Angular CLI again and run ng new angular-examples. As I’ve said before, when I program all I really want is a text editor and a shell. I have the shell open to my new angular-examples directory. I will open the same directory in VS Code, which is my favorite text editor at the moment. It’s the worst one except for all the others. I’m also going to publish this on Github with a lot of commits you can follow if interested.

On Github, I created a new repo, xerocross/angular-examples. This one tiny example is not substantial enough to be an entire repo. I’ll organize things by branches, and I’ll offer some tips on working with Git as we go too since I think Git is something everyone just learns bit-by-bit as needed.

I created the repo on the github website. In the shell in the directory of my new project, I type git remote add origin https://github.com/xerocross/angular-examples.git. After I do that, I always like to execute git remote -v to just show me the remote URL explicitly. Now I’m going to take the existing pre-made Angular app as given and push it as my new master branch by executing git push --set-upstream origin master. I just did it. It’s there now.

But I’m not going to work on the master branch. Typically one creates a new git branch with the intention of eventually merging it into master. But you can also use branches just to organize different paths in coding one could take from the same origin—different “branches”, if you will. So right now I create a new branch called ng-parent-child-example. To do this I just execute git checkout -b ng-parent-child-example. This command both creates the branch and takes us over there making it the active branch. Now anything we commit is committed to the ng-parent-child-example branch. You can always check which branch you are on by executing git status, which I tend to do after almost every git command I run.

You can run git branch to just list available branches. And to switch between existing branches, you use the git checkout command without the -b option. For example, I just had to switch back to master (git checkout master) to make a small correction. And now I’m switch again to ng-parent-child-example (git checkout ng-parent-child-example).

Every developer should get very comfortable with using Git, but now let’s do some Angular.

By the way, the Angular CLI we used to create this app has some other “scaffolding” commands you can run in the shell. There’s a command line tool built in just for creating a new component: ng generate component component-name. You can use that or just create the files from scratch. The tool might save some time.

This app comes packaged with an app-root component called AppComponent. That is the component that gets bootstrapped. Right now I’m not going to alter that except for just deleting everything in its template. Now app.component.html is an empty file. Over in app.component.ts I’m going to remove the title = ... line because I don’t need that stored as a state variable. Let’s commit.

Git status shows that I have altered those two files. git add ./ quickly stages them both for commit. git commit -m "remove extraneous stuff from appcomponent" commits the changes. This is like saving, but it’s a lot better than just saving. I’ll be committing changes frequently as I go without further comment.

Now let’s dream up a parent-child relationship of some kind that isn’t just a useless textbook example but actually does something at least mildly interesting.

Ok: thinking of something interesting is a tall order. Maybe in the meantime we can at least also use some of the weird Angular idioms like observables while we do something that is otherwise pretty boring. That way we learn more than just one thing and we see them working together in a single example at the end.

Here’s what I have in mind. We will have two child components. One is just dumb text input—maybe with a button. The other is a dumb list view component. It just stupidly displays a list of things in some nice way. The data is actually stored in the parent. In a situation like this, one can always ask, “Why would you bother factoring that bit of code out into its own component?” There are numerous reasons why you might. In this case we’re doing it just to learn how it works.

The shell command ng generate component text-input creates one of my child components. Or, rather, it creates an outline for me to work with including a text-input-components.ts file and text-input.component.html. From that I guess we can infer the naming convention. The class that is defined in text-input-components.ts comes with an empty constructor function constructor() { }. I don’t know anything about overriding the constructor of an Angular component yet, so I’m just going to delete that.

This text-input will include, obviously, an html <input> element, and we are going to bind the input to the controller using Angular’s reactive form pattern. The docs are here: https://angular.io/guide/reactive-forms, but I’ll explain what I’m doing. The reactive form pattern is very object-oriented. Or perhaps what I mean to say is that it’s very explicitly object-oriented. In one programmer’s opinion, foisting object structure on things isn’t always a good thing. Sometimes it’s just unnecessary complexity. You learn that the first time you try to make Java print “hello world”. But for now I’m willing to reserve judgment on reactive forms.

We have to import ReactiveFormsModule in our app.module.ts file to make it work, so we do that. In the component’s control file, we need to import the FormControl class. Now we declare inputTextControl : FormControl; and inside the ngOnInit method we define it: this.inputTextControl = new FormControl();. To bind this to an input element in the template we add the notation [formControl]="inputTextControl" inside the tag. Now it’s wired up internally, but it’s not connected to anything and it doesn’t do anything.

Let’s also create a component called “list-manager”. That will be the parent. Now the AppComponent template includes just one line <app-list-manager></app-list-manager>. The ListManager template includes just <app-text-input></app-text-input>. Pretty useless, but now we can try the live server (ng serve --open) and see if it even shows the text input. It does. That’s a good start. Here’s a good place to commit our changes.

Let’s also test that the input form is bound to the text-input controller. An Angular FormControl object has a property valueChanges that points to an Observable that tracks the changes on the form element. In our TextInputComponent class we have the FormControl object inputTextControl tracking our text input element. Inside the ngOnInit method, we can subscribe to inputTextControl.valueChanges and track them or log them in some way.

Specifically, now, we have this inside our TextInputComponent:

ngOnInit() {
    this.inputTextControl = new FormControl();
    this.inputTextControl.valueChanges
      .subscribe(val => {
        console.log(val);
      });
  }

This work as expected. As we type into the text input element, a new console log fires with each new value. A stream of updates. This is the observable paradigm of application data. We don’t think of the value of the form as being static at any fixed time and equal to whatever you see in the form. We think of it as a stream of data over time as the user makes any change. Each new letter and each backspace.

This particular way of looking at the data may or may not prove useful depending on the specific nature of the app we want to build. Let’s say in this instance that we don’t want our text-input component making any decisions about whether each individual keystroke is important. Rather, its job is to report everything upstairs to the Parent. The parent can decide what to do with that stream of information.

Now is when I go looking for whatever mechanism people use in Angular to emit data from a child to a parent. Googling leads immediately to this page of the docs:
https://angular.io/guide/component-interaction#parent-listens-for-child-event. Instantly we can see that Angular is trying very hard to make itself look like Java. Evidently, in order for a component to emit data, I have to create an EventEmitter object. This is absolutely the worst, most annoying part of Java. JavaScript does not need some kind of ActionDoer object to perform an action, but in Java just to print “hello world” you have to have a HelloWorldPrinter object. Somebody at Google seems to love that ActionDoer pattern.

So, we import EventEmitter from @angular/core. It is a generic, which is a syntax borrowed directly from Java. The payload of our emit events will be the value of a string input, so we define our emitter by adding the declaration and definition line @Output() emitter = new EventEmitter<string>(); at the top of the class. The Angular docs use that “@Output()” decorator in this situation. I do not know what that does, and I don’t care enough about it to research it right now. I’m just going to include it for now, and that means we also have to import the Output function from @angular/core. Now our emitter object has an emit method which is the action of our ActionDoer object.

Just to clarify: there isn’t actually something here called “ActionDoer”. That is a derogatory term for the anti-pattern of encapsulating an action into an object that performs that action for no reason other than making things “object oriented”.

I’m going to look again at that subscribe function where I’m logging the text input values to the console. Instead of logging them, I’m going to emit them. So I add the line this.emitter.emit(val); and remove the console log. If this works as expected, then typing things into the text input should do…nothing—at least for now. I still have the live-updating server running, so I can play with it interactively, and I see that I am correct. Now when I type into the form element, there are no console logs. In principle, the values are being emitted, but nothing is catching them. So emitting those values does nothing.

Let’s look at the docs again and see how the parent catches and handles the data emitted by a child. It seems I need to use a syntax like this: (emitter)="handler($event)". This goes inside the child component’s tag in the template. Now I see why the example in the docs used a name like “voted” to refer to their instance of EventEmitter. To make any sense of the notation (emitter)="handler($event)" the name of the emitter should sound like an event—something that happened, some action to respond to. In reality, however, this name is a reference to an EventEmitter object, which is an ActionDoer object, not a type of event. This is a situation where my typical scathing sarcasm just isn’t enough to express myself. Forcing me to use the same expression to refer in one place to an ActionDoer and in another place to an action is the ugliest thing I’ve seen so far from the Angular team.

For the sake of transparent meaning when reading the template, I have to rename the thing I previously called “emitter”. At the least, it needs to be specific about what has happened. So now I change the name to textInputEmitter. That is specific enough for now because the component only contains one text input. Later I might add a button, and watching that will call for a different emitter.

Now we can go up to the parent, which is list-manager, and add handling for these emit events. It’s simple. For now I’m just going to log them to the console. I define this method on the class: logTextInput (val) { console.log("inside parent:" + val); }. Then in the template I add (textInputEmitter) = "logTextInput($event)" inside the child component’s tag. This causes a series of console logs, a new one for each keystroke just as before. But this time the logs come from the parent.

This stream of data emitted from child to parent looks a lot like those observables Angular foists on us, but as it stands in the parent we do not have an Observable object. We just have an unpredictable stream of function calls. Let’s explore that. If we actually do want to turn that into an Observable, can we? We almost certainly can, right? If I remember correctly, observables are the dopest thing ever. Pretty sure I saw that in the Angular docs somewhere. Let’s commit everything here before going down that alley.

I’d bet $5 right now there is some built-in way to automatically wrap those emit events into an observable, and I haven’t even done my Googling yet.

Oh, God. I found something on Google that looks promising, but it’s a video. Ain’t nobody got time for that. Here’s the video:
https://egghead.io/lessons/angular-consuming-events-as-observables-in-angular-2 . The video is exactly on point, but I’ll explain here so you don’t have to watch a video.

Translating that stream of emitted values into an Observable isn’t quite as automatic as what I imagined, but it’s still easy. In our ListManagerComponent, we import Subject from rxjs. Subject is a kind of observable. We declare an instance of one like so: textInputs$ : Subject<string>;. Inside the ngOnInit method we define it this.textInputs$ = new Subject<string>(); and we subscribe to it this.textInputs$.subscribe(val=> { console.log("from observable in parent: " + val);});. Then we alter the template slightly. Now it handles the child emit event like this: (textInputEmitter) = "textInputs$.next($event)".

As a result of all that, we now have a handle on the stream of inputs as an Observable and we can reference it by textInputs$. If we serve this and play with it, we can see that the console logs work the same way—a new one for every keystroke—but now it comes prefixed with “from observable in parent: ” so I could be sure that the logging came from subscribing to the observable.

This is at least mildly cool because we can do things like debouncing, filtering, etc.—all the vast options provided by RxJS. And that’s where we are going next before we travel back down to a different child to present the data. Let’s debounce so that the parent only looks at a text input if the user hovered there for a moment before changing it.

This part is surprisingly easy and pretty nice actually. I piped my new textInputs$ observable through a series of maps and filters as follows: .pipe(debounceTime(600)) .pipe(map(val => val.trim())) .pipe(distinctUntilChanged()) .pipe(filter(val => val.length > 0)). The first one filters out everything except input you hover on for 600 ms. The second one removes trailing whitespace. The third one keeps you from including extraneous repeated values in a row. The fourth one filters out empty strings. This doesn’t solve any particular problem. I just made this up to play with my options. There’s a billion different ways you could manipulate this stream by piping it through different operators.

After performing that sequence of filters and maps, the resulting stream of data gets pushed to a good old fashioned list I call inputValues : string[]. To do that, I just included the line this.inputValues.push(val); inside the subscribe function.

To see the result of this, I added a line to the template to just list the values inside
this.inputValues: <ul><li *ngFor = "let item of inputValues">{{ item }}</li></ul>. The result of this is an unusual experience when you play with it. If you type in a word like “adam” and leave it for just a bit (600 ms), then it gets added to the list. If you keep typing and make it “adam cross” then “adam cross” gets added to the list. If you backspace until all of that is gone and type “john” then “john” get’s added to the list. The list is cumulative, and you don’t have to press enter or click a button to register that you stopped somewhere. Just: as soon as you hesitate for a brief moment on some input, that input gets logged. But note that it does not include all the intermediaries: a, ad, ada, adam. That stuff has been filtered out. Our system of pipes was smart enough to recognize that “ada” doesn’t mean anything to me. But if I hesitate just a moment on “adam” then that is meaningful input.

For the sake of completeness, now let’s do the easy part where we send data down from the parent to the child. For no particular reason, I factor the list view out into its own child component called list-view. In the class, I declare that the ListViewComponent receives a prop “theList” from its parent as follows: @Input() theList: string[];. In the parent template now, I remove the list element that was there before, and I replace it with <app-list-view [theList] = "inputValues"></app-list-view>. Of course the list-view template itself just iterates over the values of theList and displays them.

I think this is a good stopping place. Remember that all the code I wrote about here is available on GitHub at https://github.com/xerocross/angular-examples on the branch ng-parent-child-example. Also, if you know how to use Git pretty well, you can step through my changes from one commit to the next.

One thought on “Angular Lesson 3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s