What do you want to learn?
Skip to main content
Angular Component Communication
by Deborah Kurata
For real-world Angular applications, you need effective solutions for tracking and sharing state and sending notifications between components. This course teaches you numerous communication techniques and, more importantly, which to use when.
Start CourseBookmarkAdd to Channel
Table of contents
Hello. My name is Deborah Kurata. Welcome to my course, Angular Component Communication from Pluralsight. When building real world Angular applications, you need effective solutions for tracking and sharing state and sending notifications between components. This course presents numerous communication techniques and, more importantly, which technique to use when. You'll see ways for a component to communicate with its template, including binding, getters and setters, input and output properties, and the ViewChild and ViewChildren decorators. You'll examine how to build services as an intermediary for communication between components and how to broadcast notifications from services using Subject or BehaviorSubject. Router components provide additional communication techniques including required, optional, and query parameters. By the end of this course, you'll have a deeper understanding of the many different ways for component communication so you can apply the most effective techniques for your application. I hope you'll join me on this journey through Angular Component Communication from Pluralsight.
Good communication is an important quality of any relationship, including the relationships within an application. Welcome to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and this course covers a range of techniques for communicating between the parts of an Angular application. The components and other parts of our application need to work together to get things done. And to work together successfully, they need to communicate clearly. A component often needs to communicate with its associated template. For example, a component needs to notify its template when data has changed so it can update the view. The component needs to react when the user updates a value in the template. A component may ask a template element to set a property or perform a task. Or a component may ask a form or a control on the template about its state before performing an action. In addition to communicating with its template, a component may need to communicate with itself or another component. One of the best ways to do that is to use a service as an intermediary. For example, a component needs to retain its state, such as its current filter value or the last user-selected sort order. The component wants to share data with other components, or the component wishes to notify other components of an event or action that occurred. Another option for intercomponent communication is the router. For example, a component needs to pass a value to a routed component, or the component wants to pass optional data to a routed component. Let's summarize these communication scenarios in a diagram that we'll reference throughout this course. When a component communicates with its template, it can talk directly with a specific element, such as an input element, or it can communicate with child components, such as this one with a pm-star selector. A component can use a service as an intermediary to communicate with itself or with any other components. Or a component can use the router to pass information along to other routed components. We'll cover these scenarios and more as we proceed through this course. In this first module, we consider several suggestions for getting the most from this course, we explore the sample application that we'll work with throughout this course, and we'll look at the topics we'll cover in the remainder of this course. Let's get started.
Get the Most from This Course
To get the most from this course, it's important that you minimally know the basics of Angular. This means understanding Angular modules, components, templates, services, and routing. If you don't have the requisite knowledge, consider taking one of the introductory Angular courses first, such as Angular: Getting Started or Angular: First Look. You do not need extensive component communication experience. We'll cover what you need in this course. Another great way to get the most from this course is to join the discussion. Thoughts, comments, or questions as you watch this course? Feel free to use the Discussion tab. You can find the link to the discussion on the Pluralsight page for this course. Or follow me on Twitter. It would be great to hear about your experiences with component communication. There is also a blog post specifically for this course at the URL shown here. This post identifies common issues along with their solutions. If you have problems with the code for the course, check here first. There may already be a solution provided. As with most software development, there are multiple ways to accomplish the same objective, and we'll cover many different techniques in this course. So, how do you know which to use when? That's when some good guidelines can help. I'll present guidelines at the end of each module. We'll use them as a brief review of the techniques presented in that module and as a set of suggestions for which technique to use when as you build your own applications. Coding along through the demos is another great way to get the most from this course. Though not required, it is often helpful to try out the presented techniques. I've set up a public GitHub repository specifically for this course. It is called Angular-Communication, and you can find it at this URL. The starter files for the sample application are here. You can use these files as a starting point if you want to code along with the demos. The final files are here, but note that in many cases we achieved the same goal using multiple techniques, and these final files only show the last technique we used. If you're new to GitHub, simply click this button to download all of this code as a zip file. Because this is a course on component communication, I've created the majority of the components and templates as part of the starter files. The hands-on activities then focus on communication techniques. So, what is this sample application? Let's take a look.
We'll be working with a sample application throughout this course. Let's see the starter files for that application in action. Welcome to Acme Product Management. As its name implies, this application manages our company's current list of products. You may remember this application from such courses as Angular: Getting Started and Angular Routing. The root app component contains only a router outlet. That way we can route pages such as our login page to this router outlet and not display any title or navigation bar. Note that the login does not check your credentials, so you can enter any values here to log in. We then have a shell component that provides the application shell. It has a title, a navigation bar, and another router outlet where the router places the main views, such as the Product List page. On the Product List page, there's a button to hide and show the product images. And here is a Filter by box to enter a value to filter the list, but the page does not currently filter. We need to communicate the user's entry to the component so the component can filter its list. We'll do that shortly trying out several different techniques. Clicking on a product name navigates to the Product Detail page. Clicking on the Edit button navigates to the Product Edit page. Here we can edit or delete a product. If there is a validation error, a validation message appears, and the Save button is disabled. If we try to navigate away, a route guard asks for confirmation. This helps prevent the user from leaving the page accidentally and losing their changes. The Add Product option takes us to the edit page to add a new product. That's the basic operation of the sample application that we'll use throughout this course. We'll also examine an alternative layout for this user interface that displays the product list and product detail concurrently and look at ways for these components to communicate. Now let's finish up this introductory module with a look at the outline for the remainder of this course.
In this course, we start with the basics and look at how a component communicates with its template. We cover binding and getters and setters. Next, we examine how to get a hold of an element in a template and work with it directly using ViewChild or ViewChildren. We review the special relationship between a parent and child component and look at how a parent component communicates with its child and how the child component communicates with its parent. Then we shift gears and look at how a component can communicate with itself or with other components using a service as an intermediary. Next, we examine how to build a more complex service to retrieve, manage, and store our application's state and how to send notifications between components using Subject and BehaviorSubject. Lastly, we look at how to pass data between routed components using the router. Let's get the communication started.
Communicating with a Template
Every component has a template, and to display data or interact with the user, the component needs to communicate with that template. Welcome back to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and in this module, we examine several techniques for communicating between a component and its template. Our component's first attempts at communication were most likely with its associated template. Early on we learn how to use binding to pass information to and receive events from the template and how to use structural directives to control which elements appear within the template. Here again is our component communication diagram from the prior module. Our focus in this module is on communication between a component and basic elements in its template, such as the input element shown here. In this module, we start with a quick review of binding and structural directives. Then we cover other ways of communicating between a component and input elements on its template, including two-way binding, the long way, and the very powerful but underutilized getters and setters. We'll look at more advanced techniques, including the ViewChild decorator, in the next module. Now, let's get started.
Binding and Structural Directives
Binding is often the first technique we learn for communicating between an Angular component and its template, so it's assumed that you know how to use basic binding. Let's do a quick review just to ensure we are all on the same page with regard to the terminology. We use interpolation to display the value of a component property in the template. Property binding allows us bind a component property to an element property in the template, such as the style.width property of the image element. We use event binding to catch user actions in the view and notify the component by calling one of its methods. Here we catch the click event and call the toggleImage method in the component. Two-way binding keeps the value of an input element and its bound component property in sync. Another common way for a component to communicate with its template is through structural directives. We can leverage structural directives to affect the elements that appear in the view. Use ngIf to include or exclude elements from the view. In this example, when showImage is true, the defined image appears. When the component sets showImage to false, the image is removed from the view. Use ngFor to repeat an element in the view for each item defined in an array declared in the component. In this example, the table row element is repeated for each product in the array of products. As products are added or removed from the array in the component, the view displays the updated list. In preparation for trying out more complex techniques, let's take a quick look at the code of the sample application. I am in Visual Studio Code with the APM sample project open. If you are coding along, these are the starter files you downloaded using the link provided in the previous module. To use these starter files, you must first install the required packages. Open the VS Code Integrated Terminal, and type npm install. This installs all of the packages defined in the package.json file. I've already done this step, so I won't do it again. Let's focus for a moment on the product-list.component. I'll start by running it so we can review how this particular view works. In the terminal, type npm start. The Welcome page appears, and we click Product List to view the list of products. At the top is our page title. Here we have an input element for entry of a Filter by value. But trying it out, it doesn't currently filter our products. We'll look at how to fix that shortly. This table contains a row for each of our products. The button here hides and shows the product images. This column of the table contains a nested child component that knows how to take a number and display it as stars. Now let's check out the code. I'll close the terminal, then open the product-list.component and its associated template beside it and close the Explorer for more space. We use interpolation to display the pageTitle. The input element uses two-way binding to display any default filter value and to keep the listFilter property in the component class in sync with any user-entered value. Here is our table. Notice the ngIf here. Whenever we use binding in a template, we need to watch out for null or undefined values. Using the ngIf ensures we don't try to display the products until the products array contains data. We could instead write this expression using the safe navigation operator defined with a question mark. The safe navigation operator returns null if the value is null or undefined and does not continue evaluating the expression. In this example, when the page is first displayed, products is undefined, and this won't attempt to return the length. When the data is retrieved and the products property is populated, then it returns its length. It only navigates to the length property to evaluate it if it is safe to do so. The button in the table header uses event binding to execute the toggleImage method each time the user clicks the button. This flips a flag defining whether or not to display the product images. In the body of the table, we use the ngFor directive to display a row for each product. We are actually binding to the list of filteredProducts, so we'll see the filtered list, once we get the filtering working. The image element has an ngIf directive specifying whether or not to include the product image in the view, and it uses property binding to set the source of the image, its title, and some of its styles. We use interpolation and some pipes for the other table data elements. And here is our nested child component. It knows how to take a number and display the appropriate number of stars. We'll talk about communication between a component and its child component later in this course. So, binding and structural directives are a very powerful and useful mechanism. You'll find that in many cases, the best way to communicate between a component and its template is by leveraging binding and structural directives. But what if you need something more? Here we have a method to filter our products based on a passed in filter value. But how does the template tell the component to execute that method when the user changes the filterBy value?
Notifying the Component of User Changes
When we use two-way binding, we define a property in our component class that the template uses for the binding. If that property has an initial value, our component provides that value to the template as a default value for display. If the user then changes that value, our component property is automatically modified to match. But what if we need to perform some operation each time the value is modified, like actually filtering the list of products or recalculating a total when the user updates a quantity in a shopping cart application? We need a way for the template to communicate each change to the component so the component can perform the desired operation. There are several techniques available to achieve this requirement. In the template, we can use an updated version of two-way binding, the long way, or in the component class, we can specify a getter and setter, or we can leverage the Angular forms directives and use the valueChanges observable, even when the input element is not on a form, like in our example Filter by element. Let's try the first one first.
Two-way Binding, the Long Way
The two-way binding syntax is actually a combination of property binding and event binding, so we could change this shortcut syntax to its long form, like this. We bind the ngModel directive using property binding to the component's listFilter property. This one-way binding from the component to the template ensures that any default value defined in the component is set in the input element. And we use event binding to catch any user-entered changes to the input element. When we catch the user's change, we update the listFilter property in the component with that change as defined by $event. $event holds the payload, or contents, of the emitted event. In this case, the ngModelChange event tracks the value changes, so $event is that changed value. With this syntax, we still have our two-way binding, just in its long format, but this doesn't help us much. Our component property is changed, but our component is not notified of that change, so we still don't have a way to perform an operation in our component each time the value changes. Now that we have explicit event binding, however, we can change the event handler to call a method in our component and pass in the changed value with $event. When the user changes the input element value, the ngModelChange event is emitted, which calls the onFilterChange method in the component, passing in the modified value with $event. We then have a component method we can use to perform any additional operation we need. Note, however, that we really don't have two-way binding anymore at this point. Though we are leveraging the long format for binding, we are no longer setting the listFilter property when the ngModelChange event occurs. So, with this code as it is here, the listFilter property will not be set to the user-entered value. We'll have to handle that ourselves in our onFilterChange method. Let's try this out in our demo application. Looking again at the code in the product-list.component template, let's change the basic two-way binding syntax to a modified version of the long format using the ngModel and ngModelChange directives. We use property binding to bind the ngModel directive to the component's listFilter property. This sets any default value from the component. We then use event binding to bind the ngModelChange event, but instead of setting the listFilter property as we would in the two-way binding long format, we'll call a method in the component class. I'll name that method onFilterChange, and we'll pass $event to that method to provide the changed value. Next, we write the code for that method in the component class. In the template, we called the method onFilterChange and passed in the current input element value, which is a string, so that is the method we create here in the component class. This method won't return anything, so we set its return type to void. In the method, we first set our local property to the passed in value. We need this because the template is no longer using two-way binding to set this value for us. We then perform any other operations we need. In this example, we call the performFilter method to filter the products based on the current value of the listFilter property. Let's see if it works. Bringing up the browser, we type in a filter value, and the product list is filtered. Yay! But looking back at the template, this code looks a bit foreign, especially for other developers that are used to seeing normal two-way binding. Is there an alternative way to achieve our goal? Yes, there is.
Getters and Setters
Like other languages, such as Java and C#, TypeScript provides two ways to define a property. The most common way to define a property is to simply declare a variable and optionally define its type or default value. You've probably used this technique throughout your component classes, but there is another way. You can instead define two functions, one that is executed when the property value is requested, called a getter, and one that is executed when the property value is set, called a setter. The setter passes in the value to be set and does not specify a return type. Note that when using the property getter and setter syntax, there is no backing variable automatically defined to actually hold the value, so you need another, often private, variable to retain that value. The pattern for getters and setters then looks like this. We define a backing variable to hold the actual value, specifying the data type as appropriate for the property. This variable is private and therefore encapsulated within the component class. It should only be accessed using the getter or setter. Code in the getter returns the value of the backing variable. The getter is called any time the template needs the value for binding. Code in the setter sets the backing variable using the passed in value. If the template uses two-way binding to bind to this property, every time the value is changed by the user, the template communicates that change to the component by calling this setter. Hey, that sure sounds like what we need, notification each time the value is changed. Because these are methods, we can add any code we want. We could add code to the getter to execute every time the value is requested, to preprocess or format data for example, or more importantly for our scenario, add code to the setter to execute every time a bound value is modified. Using a getter and setter is often a great design pattern. Let's try it out. We are back in our APM application. Let's start by undoing the code we changed in the last demo so we can try filtering our list of products using the getter and setter technique instead. In the component's template, change the Filter by input element back to the shortcut syntax for two-way binding, the syntax that most Angular developers are more used to seeing. In the component class, we'll delete the onFilterChange method since it is no longer called from the template. Checking out the result in the browser, as expected, we see that the filtering no longer works. Now let's instead notify the component of changes by using getters and setters in our component class. First, we need to delete the current listFilter property. We can't define a property both with a declaration and a getter and setter. We can only have one or the other. I'll paste the code following the getter and setter pattern from the slides. We define a private backing variable to hold the actual value and set its type to a string since our filter criteria is a string. We use an underscore before the variable name to indicate that it is a private variable and should not be accessed except through the getter and setter. Here is the getter function that takes no parameters and has a return type of string. This function simply returns the value of our backing variable. Here is the setter function that takes in the value for the property. This function sets our backing variable to the passed in value. It is here that we want to filter our list of products by calling our performFilter method, passing in the value of the listFilter property. Bringing up the browser, we type in a filter value, and the product list is filtered. Yay! Looking back at the code, using a getter and setter is a clean, clear, and easy way to communicate user changes to the component. Every time the user modifies the input element, the two-way binding calls the setter and notifies the component of that change. So, we've seen two ways for the template to communicate value changes to the component class, modifying the long form of two-way binding in the template or using a getter and setter, primarily the setter, in the component class. Both techniques notify the component of user's changes in the template. Which technique you and your team choose depends on your style guidelines and preferences. Personally, I often use the getter and setter approach since I tend to favor logic in the component over details in the template. But what about this last option, using the valueChanges observable? Before we can examine that approach, we need to look at another type of communication between a component and its template, leveraging the ViewChild and ViewChildren decorators. We'll cover that in the next module. For now, let's finish up this module with some guidelines for using the communication mechanisms we just covered.
Guidelines and Summary
It's always fun to learn new techniques, but to truly master them, we need to understand when to use them. That's when guidelines can help. When possible, use binding and structural directives as the first line of communication between a component and its template. These techniques are simple, straightforward, and common, making it easy for any other developers on the team to maintain the application, but sometimes you need more functionality. If we need to notify the component when the user changes the value of an input element, we can expand the two-way binding syntax to its long form, then change the event handler to call a method in the component class. The event binding calls the method in the component, notifying it when the user changes the value. We can then add any logic to the method to perform any desired operation. Favor this technique if you want to catch the change in the template. The caveats to this approach are that we lose two-way binding. The associated property is no longer kept in sync, so you'll have to handle it manually within the defined method. Plus, we catch the change in the template. Depending on your team's coding standards, it may not be desirable having this type of logic in the template. And this syntax is a bit uncommon. That means that newer devs may not recognize what this code is doing. Alternatively, we could use a getter and setter. The setter is called each time the user changes the value of the input element, notifying the component of each user change. It is there we perform any needed operation. Favor this technique if you want to catch the change in the component class without any modifications to the basic two-way binding syntax in the template. This is the technique I turn to when my component needs to perform an operation on a value change. The primary caveat to this technique is that we are turning a single property declaration in one line of code into seven code lines, plus any additional required logic. In this module, we quickly reviewed basic binding and structural directives as the primary method of communication between a component and its template. We saw how to change two-way binding into its long form using property and event binding and used the event binding to notify the component when an input element value was changed. We then examined how to use a getter and setter in place of a simple property declaration. When a bound input property value is changed, the setter is called, notifying the component of the change. We've now covered several techniques for communicating between a component and basic elements in its template, but there are more techniques available. Let's look at the ViewChild and ViewChildren decorators and leverage the valueChanges observable.
ViewChild and ViewChildren
A component can obtain a reference to an element or directive on a template and access it directly. Welcome back to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and in this module, we examine the ViewChild and ViewChildren decorators for communicating between a component and its template. Our components sometimes just want to get a hold of an element in the template and interact with it. That's the purpose of the ViewChild and ViewChildren decorators. Here again is our component communication diagram. Our focus in this module is still between a component and its template, but now we'll use ViewChild and ViewChildren to get a direct reference to an element or directive. In this module, we start with an overview of the ViewChild decorator and see how to use it to communicate between a component and a native element on its template. We examine ViewChildren to obtain a reference to multiple elements. Then we pick up from where we left off in the last module and look at how to use the ViewChild decorator to communicate between a component and an input element using an Angular forms directive and the valueChanges observable. Lastly, ViewChild has a bit of an issue when using ngIf. We'll take a look at that issue and how to resolve it. Let's get started.
A component may need to interact with or retrieve information about a specific element or directive on the template. For example, the component may want to set focus to a particular element or pass the element to a third-party library or API that interacts with the element, such as the Google Maps API. The component may need to communicate directly with a directive on the template. For example, by talking directly to an Angular forms directive, a component could check a form's validity before saving. To accomplish these tasks and more, the component needs a reference to the element or directive on the template. You may have seen syntax such as this to obtain a reference to a specific element in the HTML with a defined ID. You could then access any of the properties or call any methods on the returned element. In Angular, we instead use the ViewChild decorator. We add the ViewChild decorator to a property in a component to request a reference to an element or directive in the component's template. We pass into the ViewChild decorator a selector that defines the element or directive we want to reference. When working with the ViewChild decorator, the selector can be an Angular directive, such as NgForm or NgModel. The ViewChild decorator then looks in the template for the specified directive. The selector could also be a custom directive or child component, such as the StarComponent in the sample application. The ViewChild decorator then looks in the template for the custom directive or child component of the defined type. Or the selector can be a template reference variable. The ViewChild decorator then looks in the template for a template reference variable with this name and returns a reference to the associated element. Notice that in the last case the selector is a string. In the other cases, the selector is of the appropriate directive or child component type. Let's try out ViewChild using a template reference variable first.
Demo: ViewChild and Accessing the Native Html Element
Let's use the ViewChild decorator with a template reference variable to access an element's native HTML element. Here we are in the APM project looking again at the product-list.component and its template. Let's say that we want a reference to the Filter by input element as a native HTML element so we can set focus to it. That way when the view appears, the element has focus, and the user can just start typing to filter the products. Our first thought may be to declare a property in the component with the ViewChild decorator, something like this. And we'll use the Quick Fix to add the required import for the ViewChild decorator. But this syntax won't work. The argument to the ViewChild decorator must be a queryable name for the element that we want to reference. How do we specify a queryable name? Here in the template, we add a template reference variable to the element. A template reference variable is defined with a hash symbol and a name added to an element so it can be queried or accessed by Angular. Going back to the component, now that we have a queryable name, we can use it as the argument to the ViewChild decorator. The filterElementRef property will then contain a reference to the input element so we can access the element's properties or call its methods. But wait, when exactly is this reference assigned? Let's try to examine the filterElementRef property in the constructor with a console.log. Restart the application with npm start, if it is not already running. When the browser comes up, open the developer tools, navigate to the Product List page, and we see undefined logged to the console. The rest of this is the logging from our data access service and is not relevant to our current discussion. We are only interested in the value of our filterElementRef, which is undefined. Going back to the code, why is our filterElementRef undefined here? It has to do with a component's lifecycle. A component is first constructed and initialized. Then the component's view is initialized and rendered. So, in the constructor, the view is not yet rendered, and the ViewChild cannot find the element with the specified template reference variable name. Angular provides a lifecycle hook that is called after the component's view is initialized and rendered, called AfterViewInit. It is then this lifecycle hook that we can reliably use a property with the ViewChild decorator. Let's try it out. We start by implementing AfterViewInit and use the Quick Fix to add the appropriate import. We then implement the ngAfterViewInit method and move our console.log statement here. At this point, the view is rendered, and our filterElementRef should be set. Let's check it out. Back in the browser, we now see the element logged to the console. Great! Now what? Looking at the console, the data type of the filterElementRef is an ElementRef, and notice that the key property of the returned element reference is nativeElement. This property provides access to the underlying native HTML element. Using this nativeElement property, we can access any of the native element's HTML properties or call its HTML methods, such as its focus method. Our goal for this example is to set the focus to our input element. Back in the component, let's start by strongly typing our filterElementRef. We now know that it has a type of ElementRef, and we'll use the Quick Fix to add the associated import. Now, instead of logging the value, we'll use the nativeElement property and call it's focus method. Let's give it a try. Refresh the browser, and it works. The Filter by input element has focus, and we can immediately start typing to filter the list. Looking back at the code, any time we need to work directly with an element on the template, we can add a template reference variable to the element, use ViewChild to reference the element, access its nativeElement property, and we are good to go. We can then use any of its HTML properties or call any of its HTML methods. There are a few considerations we need to keep in mind when working with ViewChild to access a native element. If we use nativeElement, we are directly accessing the DOM, or Document Object Model. The DOM is the set of data structures that manage the HTML elements for display in the browser. This means that we are tightly coupled to the browser and may not be able to use server-side rendering or web workers. One way we can protect our code from this issue is to check for the existence of the native element before accessing its properties or methods using code like this. We first check for the native element and only proceed if the value is not null or undefined. In addition, using nativeElement can pose a security threat, especially when accessing its innerHtml property. It can make an application more vulnerable to cross-site scripting, XSS, attacks. If these considerations are not relevant to your application, then you can freely use ViewChild to access the native element. There are several other ways to use the ViewChild beyond accessing its native element, as we'll see shortly. But first, what if you need to reference multiple elements? Then try ViewChildren.
As you may expect, ViewChildren provides a list of element or directive references. We pass into the ViewChildren decorator a selector that defines the elements or directives we want to reference. The key difference between ViewChild and ViewChildren is that ViewChildren returns a QueryList of element or directive references. We can then iterate through or work with that list. And it tracks changes in the DOM as elements are added or removed. For example, with ngIf or ngFor. The QueryList has a changes observable we can subscribe to if we want to perform an action as the list of references changes. Similar to ViewChild, the ViewChildren selector can be an Angular directive, such as NgForm or NgModel. ViewChildren locates every occurrence of the specified directive. The selector can be a custom directive or a child component, such as the StarComponent. ViewChildren looks in the template for every occurrence of the custom directive or child component of the defined type. The selector can be a single template reference variable. ViewChildren looks for all template reference variables with this name and returns the associated elements. Or the selector can be a set of template reference variables. ViewChildren looks in the template for each template reference variable and returns the associated elements. Notice that in the last two cases the selector is a string. In the other cases, the selector is of the appropriate data type. Let's give it a try. Back in the code, here in the product-list.component's template, let's temporarily add a second input element so the user can name the entered filter, and let's give it a template reference variable of nameElement. In the component class, we use the ViewChildren decorator to retrieve both elements using their template reference variable names in the selector. We'll name the property inputElementRefs. Its type will be a QueryList. Since the selector is a set of template reference variables, we'll get back ElementRef items in the list. So, our QueryList generic parameter is ElementRef. We'll use the Quick Fix to add the missing import statements. To try this out, in the ngAfterViewInit, when we know the view will be initialized, let's use console.log to view the returned list. Bringing up the browser, we see the QueryList. Drilling down, it contains two elements. Going back to the code, let's change the ViewChildren selector to NgModel and use the Quick Fix of the VS Code to add the appropriate import for NgModel. That will get us references to the control state for these elements, such as whether they have been changed or are valid. Bringing up the browser, we again see the QueryList here in the console. Drilling down, we see the NgModel references for the two controls. We'll talk more about NgForm and NgModel references in the next clip. Going back to the code, we've just seen how the choice of selector changes the type of references in the list. So now we know the basics of working with ViewChild and ViewChildren. Before we move on, remove this second input element, our ViewChildren and our logging. We'll leave the code that sets the focus. Next, let's see how ViewChild can help us receive notifications when the user makes changes.
ViewChild and Angular Forms
Recall from earlier in this course that we want to notify the component when the user makes a change so we can filter our list of products. In the last module, we looked at how to accomplish this goal using a modified version of two-way binding, the long way, or using a getter and setter, primarily the setter. Now that we know about the ViewChild decorator, let's see how to use it to access the built-in valueChanges observable and receive notifications leveraging the Angular forms directives. Even if an input element is not on a form, we can leverage the ViewChild decorator and the Angular forms NgModel directive to reference and to interact with the input element and, spoiler alert, tap into its valueChanges observable. But first, a little background information. Angular provides two techniques for building forms, template-driven and reactive. With template-driven forms, Angular automatically creates form data structures that track input element values and state. It creates the data structures based on information defined in the template. To access these data structures, we need a reference to the associated directive, so we use the ViewChild decorator. With reactive forms, we create the form data structures for the form and controls in the component class. We then don't need the ViewChild decorator because we already have a reference to the data structures we created. In either case, the resulting data structures provide a valueChanges observable. An observable is simply a stream of events that you can work with as an array. We subscribe to the observable to start receiving the events. Each time an event is emitted, the function defined within the subscribe method is executed. In this example, each time the user changes the filterInput element value, we'll call the performFilter method. Since accessing the valueChanges observable with reactive forms is straightforward and does not require ViewChild, let's look at what's required with template-driven forms. And keep in mind that if our input elements are not on a form, we can still leverage the template-driven forms' data structures. If you are new to Angular forms, the information on the prior slide may have been a bit confusing. You can find more information about Angular forms in the Pluralsight library. Check out the Angular Forms course for details on using template-driven forms or Angular Reactive Forms for a quick overview of template-driven forms and details on using reactive forms. If you'd like, you can skip the valueChanges technique for now and jump right into the summary for this module. You don't need to be concerned about getting behind if coding along because in the next module we replace the majority of the code from this module with yet another technique as we move the Filter by input element into its own child component. You can revisit the valueChanges technique later. Otherwise, let's move on. If a template contains a form element, Angular automatically adds a directive called NgForm with data structures containing information about the form, the controls on the form, and all of its associated state. This includes whether the form or any of its controls are dirty or invalid. If the template contains an input element with an ngModel directive, the NgModel directive provides data structures containing information about the single element and its associated state, even if the input element is not on a form. To access these data structures from the component, we use ViewChild and obtain a reference to the NgForm or NgModel directive. Let's jump back into a demo.
Demo: ViewChild and Angular Forms
In this demo, we'll use ViewChild to access the NgModel directive and subscribe to its valueChanges observable. Before we try the ViewChild decorator in our product-list.component, let's check out the product-edit.component. In the template, notice the form element here along with input elements for the product name, product code, and so on. We are using two-way binding, which indicates template-driven forms. In the component class, we use ViewChild to obtain a reference to the NgForm data structures. We use this reference is several places in the component class. Here we use the reference to access whether the form is dirty. We use it here to call the reset method and reset the form each time we retrieve a different product. And here we use the reference to ensure we don't save unless the form elements are all valid. So, we can access any of the NgForm data structure properties, including its state information, or call any of its methods, such as reset. Cool! Let's now use the ViewChild decorator in the product-list.component to access our input element's data structure, specifically its valueChanges observable. Last we left our Filter by implementation, we had used the getter and setter technique to filter our list of products as the user types in the Filter by box. Now let's undo that and try watching for valueChanges instead. We'll delete our getter and setter and go back to using a declared property. A quick check in the browser, and we see that our filtering is no longer working, again. Back in the code, let's check our template and ensure it is still using two-way binding with the ngModel directive, and it is. In the component class, we define a property that will reference the NgModel directive associated with our input element. We'll decorate that property with the ViewChild decorator, passing in NgModel. Note that even through the name of the directive in the template is ngModel with a lower case n, the directive type passed in here is NgModel with a capital N. Let's strongly type our filterInput property so we have IntelliSense. Note that accessing either NgForm or NgModel requires the FormsModule, which I already have set up in the SharedModule of this sample application. Recall that earlier in this module we used the ViewChild decorator with a template reference variable to access this same input element. What's the difference? If we use ViewChild with a template reference variable, we get a reference to the input element and can use its native element property to access its HTML properties and methods. If we use ViewChild with the NgModel directive, we get a reference to the input element's NgModel data structures and can access its state information, such as whether the control has been modified or is valid. We don't get access to the native element. Now let's examine the filterInput property with a console.log. Recall where we need to put this code? If you said the AfterViewInit lifecycle hook, you are correct. We already implemented that hook earlier in this module. Let's add a console.log statement here. Checking it out in the browser, we see that the reference property is indeed the NgModel directive. Looking at its data structure, we have access to all of the control's state information, such as dirty and valid. Note that the NgModel and NgForm data structures are read-only. So, we can't use our ViewChild reference to update these state values; we can only read them. Down here, we see a valueChanges: EventEmitter. So, we can subscribe to the valueChanges and watch for changes to this value. Let's jump back to the code. In the ngAfterViewInit, we use our reference property, access the valueChanges observable, and subscribe to it. The function we specify within the subscribe method is executed every time the user makes a change to the value. In our case, we want to call the performFilter method, passing in the current value of the listFilter property. Going back to the browser, we type in a filter, and the filtering works once more. Yay! Looking back at the code, we used ViewChild to access the NgModel data structure and subscribe to valueChanges. We've now seen the three ways for the component to react to changes in the template, using the two-way binding, the long way, using a getter and setter, and using the valueChanges observable. But there is a little caveat to using the ViewChild decorator. What happens if the element we want to reference is within an ngIf? Let's say we don't want to display the Filter by element unless there are some products to filter. So, we add an ngIf on the div around the input element here. We open the browser, and bang, can't read property valueChanges of undefined. Now what?
ViewChild and ngIf
The ngIf structural directive tells Angular not to include the associated element or its children in the DOM unless the provided expression is true. The code retrieving the products is asynchronous, so when the view is first rendered, there are no products. The ngIf is evaluated false, and the div element and all of its children are not included in the DOM. So, when the ViewChild tries to obtain the reference in the AfterViewInit, the element is not there, the property is undefined, and we see the undefined error message. When the asynchronous data retrieval is complete, the ngIf expression is evaluated to true, and the input element is added to the DOM, but the ngAfterViewInit method is not executed again, so the valueChanges subscription never occurs. One simple way to solve this is to remove the ngIf and use the hidden property instead. But the hidden property toggles a CSS class that could be accidentally overwritten, showing the hidden element when it is not intended. Another way to solve this is to use the getter and setter syntax we learned earlier. If we change the ViewChild decorator to decorate a property setter instead of a simple property declaration, the reference will update when the DOM changes, but it is a little more complex than that. Let's give it a try. Back in the code, in the product-list.component, we change the filterInput from a simple property declaration to a getter and setter. I'll paste the code using the getter and setter pattern we covered previously. Following our pattern, we define a private backing variable to hold the reference. We specify a getter returning that reference and a setter to set that reference with the passed in value. We then decorate the setter with the ViewChild decorator, passing in NgModel as the selector. Next, we copy all of the code from the ngAfterViewInit and paste it into the setter, then comment out the original code. Checking it out in the browser, we still see an error. Why is that? Back in the code, when the view is first displayed, the product data is not yet retrieved, so our ngIf excludes the input element from the DOM, and its value here is undefined. We need an if condition around each of these to handle that case. Looking back in the browser, there is no error, and the filtering is working, or it at least looks like it's working, but we have an issue. Any guess what it is? Going back to the code, let's add some logging to the setter. We'll add one just before our first if, one within our first if just before the subscribe, and another one within our subscribe method. Notice that when we change from a single line fat arrow function to a multiple line function we need to add curly braces. Now let's view it in the browser. And we see the reference starts as undefined. When the data retrieval is complete, the product's property is set, and the ngIf evaluates to true, displaying the Filter by element. The bindings are re-evaluated, the logging displays the reference to the NgModel and lets us know we are about to subscribe, but as we interact with the page, showing and hiding images for example, we see that it is executing this setter again and again. That means we are subscribing multiple times. Let's enter one value into the Filter by element. We see that our one change performed the filter multiple times, once for each subscription. That means we have multiple subscriptions watching for changes, and we are re-filtering our list unnecessarily. That's not good. Now what? Back in the code, one way to resolve this is to create a private variable for the subscription and use the Quick Fix feature to add the import for its type. This will hold a reference to our subscription. Set that variable when we subscribe, and check that variable as part of our first if. If the filterInput is set and there is no current subscription, then subscribe. Now it won't subscribe again if there is already a subscription. When we view it in the browser and refresh the page, we see the initial undefined value, then the appropriate reference, and our Subscribing message. As we work with the page, we still see that the setter is executed, but it is not setting up additional subscriptions. When we enter a Filter by value, we only see one Performed the filter message each time we change the value. It's working as it should, but look at all of this code. Not so easy to come up with and definitely not easy to remember what all of this is for. Recall that we need to do this complex code just because of the ngIf. Before we move on, let's undo this last change. We'll remove the ngIf. Without the ngIf, we can now use the simpler syntax, so we'll uncomment the ngAfterViewInit code and replace our more complex filterInput getter and setter with the simple property declaration. Check it out in the browser, and it again filters our products. Let's finish up this module with some guidelines for using these communication mechanisms.
Guidelines and Summary
Let's summarize the guidelines for using ViewChild and ViewChildren. Using ViewChild or ViewChildren to access an HTML element in the template provides a nativeElement property to reference its native element. We can then access any of its HTML element properties or call any of its HTML element methods, such as the focus method used in our example. A few caveats with this technique. The ViewChild reference is not reliably available until the AfterViewInit lifecycle hook. The ViewChild reference is not available if the element is not in the DOM at the time it is evaluated, which may be the case if it is added dynamically with an ngIf or ngFor. Accessing the native element does not work with server-side rendering or web workers, but we did see syntax to check for the native element before using it. This protects our code from generating an exception when the native element is not available, and using this technique could cause a security concern, especially when working with the innerHTML property of the native element due to the possibility of cross-site scripting. If we use ViewChild or ViewChildren to access an Angular directive in the template, it provides a reference to the directive's data structures. We can then access any properties of those data structures, including the valueChanges observable as we saw in the demo. A few caveats with this technique. The ViewChild reference is not reliably available until the AfterViewInit lifecycle hook. The ViewChild reference is not available if the element is not in the DOM at the time it is evaluated, which may be the case if it is added dynamically with an ngIf or ngFor. We saw this in the demo. When using ViewChild or ViewChildren to reference the NgForm or NgModel data structures, those data structures are read-only. We've now seen three techniques for notifying the component of user changes, using a modified version of two-way binding, the long way, using a getter and setter, primarily the setter, and using ViewChild to get a reference to the NgModel data structures and using its valueChanges observable. We saw the first two techniques in the previous module and this last technique in this module. Using ViewChild and subscribing to the NgModel valueChanges observable provides the component with notifications as the user makes changes to an input element. Favor this technique if you need other information from the NgModel data structures, such as validation, or state values, such as dirty or touched. We saw the big caveat to this approach in the demo. Recall what it was? That tricky little ngIf. When using ngIf, if the if condition is initially false, the element and any of its children are not added to the DOM and so cannot be found by the ViewChild decorator. In this case, we had to leverage a setter and add lots of extra code to successfully use this technique. And as with all of the other ViewChild techniques, the reference is not reliably available until the AfterViewInit lifecycle hook. In this module, we covered how to use the ViewChild decorator to return a reference to an element or directive in the template and examined ViewChildren to provide a list of references to elements or directives. We leveraged the ViewChild decorator to reference an element with the Angular forms NgModel directive and then used that reference to subscribe to valueChanges. That way the component is notified every time the associated element value is changed. Lastly, we saw how an ngIf can derail our ViewChild reference and how to get it back on track. We've now covered several techniques for communicating between a component and basic elements in its template. Up next, we'll examine techniques for communicating between a component and its child component.
Communicating with a Child Component
Using child components, we can break a view into logical and potentially reusable pieces. The parent component then integrates those pieces into a working whole, passing data to and receiving events from the child components. Welcome back to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and in this module, we examine several techniques for communicating between a parent component and its child component. There is a special relationship between a parent component and a child component. They can share secrets in ways that are only available between a parent and a child. But what is a child when talking about components? When designing a view in an application, we often want to break up the pieces of the view into constituent parts. Let's look at the Product List view as an example. The filter criteria input box and its associated criteria detail display could be built as a separate component. That component could then be reused on any view that requires a filter. The rating stars is already a separate, reusable component for displaying a number as stars. We use it here to visually display the product rating. We could even build a product row as a separate component if it made sense for our application. We then construct our product list template from these child components. In this example, the Product List is the parent component. Every component specified within the Product List template is a child component. Since these components are nested within the parent component's template, child components are sometimes called nested components. Bottom line, for components to have a parent and child relationship, the parent component's template must contain the child component. Let me say that again; for components to have a parent and child relationship, the parent component's template must contain the child component. That is the only way to establish that parent and child component relationship. Here once again is our component communication diagram. Our focus in this module is on the communication between a component and its child component. Components related through routing do not have a parent and child relationship, even if they are accessed with child routes. Many of the techniques in this module will not work if a component is displayed within another component using a router-outlet. We cover communication between routed components later in this course. In this module, we begin with an overview of child components and build one. We then look at how to communicate from a parent to that child. We define an input property and examine how to watch for changes to that input property using a getter and setter, or the OnChanges lifecycle hook. Next, we walk through how to communicate with the child component directly using a template reference variable or the ViewChild decorator. Let's get started.
Building Child Components
Pretty much any piece of a view can be built as a child component, such as the Filter by input element and its associated criteria detail display element. To build a child component, we begin by creating an Angular component with its associated template. We then add the desired elements to the template. In this example, we have the input element and an h3 display element. In the child component class, the Component decorator must specify a selector. The selector is the component's name when it's used as a child component. The child component can then be added to any parent component's template by specifying that selector name. Let's build a child component so we have something to communicate with. We are back in Visual Studio Code with the APM sample application open. Looking at the product-list template, the filter criteria input element and its criteria detail display make sense as a separate component. That way other components in the application can reuse it for their filtering. If the application is not currently running, type npm start in the terminal window. Click the plus to open a second terminal window. We'll create the new child component using the Angular CLI. If you don't have the CLI installed, you can install it using npm install -g @angular/cli, or you can build the component from scratch. I already have the CLI installed, so I'll use that. The CLI command for creating a new component is ng for Angular CLI, g for generate, c for component, and we want to share this component, so we'll add it to the shared folder that we already have created. We'll call the component criteria because we may want to add other criteria to this component at a later time. That's it. The CLI automatically generates the component class, unit test, template, and style sheet for us, and it declares the component in the SharedModule. Let's take a look. The Angular CLI automatically added our new component to the declarations array of the SharedModule; however, that's not enough. If we want components declared in other modules, such as our product module, to use this new CriteriaComponent, we need to share it. We do that by adding it to the exports array here. This SharedModule then imports CommonModule and FormsModule so that our shared components have access to common Angular directives, such as ngIf, and form directives, such as ngModel for two-way binding. It declares our two shared components, and it exports both components and the two imported modules so they are shared with any module that imports this SharedModule. Looks good. Now let's open our new criteria.component class and its associated template side by side. The CLI created the basics of the component, but not much useful here in the template. We already have the UI elements we want in our criteria.component defined in the product-list component template. Let's cut the filter elements out of the product-list template and paste them into the new criteria template. Since we changed this code a few times in the last few modules, be sure that the input element here uses the classic two-way binding and there is no other extraneous code or ngIf. Code in this criteria template binds to a listFilter property here, here, and here. So, let's add that property to the CriteriaComponent class. The Filter by input element is now in the criteria.component. If we still want to set focus to that element, we can cut the ViewChild property from the ProductListComponent and paste it into the CriteriaComponent. Then use the Quick Fix feature of VS Code to add the associated imports. Next, we implement the AfterViewInit and again use the Quick Fix feature for the associated import. Looking at the AfterViewInit method in the ProductListComponent, the component's template no longer has the filter by input element, so we can't subscribe to its value changes. We'll delete that, but we can use the rest of this code in the CriteriaComponent, So, I'll cut it and paste it over. In the ProductListComponent, there are a few more things we can delete, the AfterViewInit lifecycle hook, the listFilter property, and these two left over properties, and let's remove all these unused imports. That leaves us with a syntax error here because we are passing the listFilter property into this method. Let's remove that argument for now. When we are done deleting, we should only have these key properties, a constructor that injects in our productService and the ngOnInit, toggleImage, and performFilter methods. Let's check it out in the browser. Click on Product List, and open the console. It is good to see that there are no errors, but it also has no Filter by input element. We need to nest our new CriteriaComponent into our ProductList parent component to get back that functionality. Looking at the code, I'll move the CriteriaComponent class so we can see it and the product-list template at the same time. We'll nest this new component in our product-list template. I'll paste the code, and we can talk through it. For the elements to format nicely on the page, we start with a div and specify a style class of row using the Bootstrap style class. Then we use the child component's selector here in the parent component's template. In the Component decorator, the CLI generated a selector of pm-criteria, so that's the name we use in the parent template. And we'll give it a style class to format it within the row. Now that the ProductListComponent contains the CriteriaComponent, we have established that special parent/child relationship. Now, let's check out our child component in the browser. Our Product List view looks as it did before, with the child component now providing the Filter by input box, but once again the filtering is not working. We can't use the techniques we covered in the last module to communicate with the input element in the template because the product-list template no longer has a Filter by input element. That element is now encapsulated in the child component. Instead, we need to establish some communication between our parent ProductListComponent and the child CriteriaComponent.
Parent to Child Communication
There are many reasons that a parent component may need to communicate with its child. The parent component can push data to the child. For example, the child component may have configuration parameters that the parent can set up, such as a maximum entry length, enabled status, or in our Filter by example, whether or not to display the criteria detail. The parent may want to provide a default value. In our Filter by example, the parent component could pass in the last entered filter criteria. In the case where the parent component manages a list of items and the child component displays or edits one of those items, the parent can pass the item's data to the child. The parent component can also pull data from the child. For example, the parent can request the value of one of the child's properties. For our Filter by example, the parent wants the current value of the user-entered filter criteria. The parent component can even tell the child to perform an action by calling one of the child's methods. For example, the child may have a clear or reset method that the parent wants to call. There are many techniques a parent component can use to communicate with one of its child components. To push data to the child, as in the examples in the top row, the most straightforward and common technique is to use an input property defined with the Input decorator. This allows the parent component to pass data in to the child component. And if the child component needs to monitor changes to that data from the parent component, the child component can set up getters and setters or handle the OnChanges lifecycle hook. If the parent component wants to request information from or call a method in the child component, it first needs a reference to that child component. The parent component gets that reference using a template reference variable or the ViewChild decorator, and of course, though not shown here, we can use a service as an intermediary to communicate between the parent and child. We'll look at services as a communication mechanism later in this course. Let's try out these techniques, starting with the Input decorator.
A child component defines an input property by adding the Input decorator to that property. That property is then available for binding. A parent component can use that input property to pass data to the child component. The input property is the binding target on the left. On the right is the binding source. A parent component can bind a value, an expression, or one of its own properties to this binding target to pass data from the parent to the child. In this example, we simply pass true. But often we want the parent component to manage the value we pass to the child component. When that is the case, we define a property for the binding source in the parent component's class. That property holds the value to pass to the child component. In the parent component's template, we specify the input property as the binding target on the left side of the equals and specify the parent component's property as the binding source on the right side of the equals. Since we are binding to a property, we use property binding and enclose the binding target in square brackets. This syntax takes the current value of the parent component's property and passes it to the child component. Using the Input decorator is often the best choice for passing data from a parent to a child. Let's give it a try. I've rearranged the windows a bit, but still have both the product-list and criteria.components open. Let's start simple. We'll provide a mechanism for the parent component to optionally pass a configuration flag to the child component defining whether to display the criteria details. First, we define a property in the child component for that configuration flag. Let's call it displayDetail and give it a type of boolean. Next, we decorate the property with the Input decorator. Be sure to include the parentheses because even though we are not passing anything to the decorator in this case, it is a function. I'll use the Quick Fix feature to add the needed import. In the criteria template, we'll use this configuration flag to determine whether or not to display the criteria detail. Let's change this hidden property binding to use ngIf and specify our new flag. Our Filtered by detail will only appear if our displayDetail property is true. Our child component is now all set. It has a configuration property and is using that property to control whether the criteria detail is displayed. The parent component can then use this property to pass a value to the child component. In the parent component class, let's add a property to hold the value we want to pass to the child component. We'll call it includeDetail, define its type as boolean, and set it to true so when it is passed to the child component it will display its detail. Then in the parent's template we specify the child's input property, displayDetail, as the target of the binding. We specify the input property in square brackets because we want to use property binding and assign it to the parent component's property, which is includeDetail. This passes our configuration setting to the child component's configuration property. Checking it out in the browser, do we see the detail in the view? Yep, success! Let's add another input property. How about displaying the filtered hitCount in the criteria detail display element? That would allow the user to quickly see the number of matched products. Looking back at our code, let's start in the CriteriaComponent. We'll add a property to store the hitCount as a number. Our CriteriaComponent can't calculate this value because it doesn't have any information about the data displayed in the parent view. Instead, the parent component needs to pass this information in. So, what do we do? If you said add an Input decorator, you are correct. We add the Input decorator to our property. Next, we add the count to the display in the criteria.component's template. Our child component is now set to display the hitCount, but it doesn't yet have a value. In the parent template, we update the CriteriaComponent element to set the hitCount input variable and bind it to the length of our filtered list of products. When we have the filtering working, the filteredProducts array will contain the filtered items so we can use its length property to pass along the number of filtered elements. Checking it out in the browser, we see an error, Cannot read property 'length' of undefined. Looking at our parent template, we see that the error is caused here. That's because when we first bring up the page the filtered list of products is not set. There are many ways we could handle this. We could add an ngIf around our criteria here, for example, but the easiest way to prevent this error is to add the safe navigation operator, defined with a question mark. Adding a question mark ensures that the length property is only accessed when filteredProducts is set. If filteredProducts is null or undefined, it returns null and does not attempt to access its property. So it keeps us safe from that pesky Cannot read property of undefined error message, hence the name safe navigation operator. It only navigates down to the property if it is safe to do so. Does that fix it? Yep! It shows that we have 5 matches. Even though our list is not yet filtering, we now have a hitCount property in the child component that is populated by the parent component. Looking back at the code, we see that the child component now has two input properties, but what if the child component needs to perform an action if those properties change? What then?
Watching for Changes
Here on the left is our parent template and component and on our right the child component and template. The child component has a property that is used in its template to determine whether to display the criteria detail. Since the child component property has an Input decorator, it is an input property, and the parent can use binding to communicate a value from the parent to that child property. Angular then automatically notifies the child template of any change to that value. So, if somewhere in our parent component we change the value of the includeDetail property from true to false, the binding reacts to that change, that updated value is passed on to the child component, and the child template reflects that change. No extra code is needed. But let's say we have a requirement to perform an action in the child component when the parent component changes an input property. How can we react to those changes? There are several techniques we can use to watch for changes to an input property. We can use a getter and setter as we saw earlier in this course. In this example, we change the hitCount input property to a getter and setter. The child component can then react to any changes from the parent component using code in the setter. Alternatively, we can use the OnChanges lifecycle hook. The OnChanges lifecycle hook provides a method that is called whenever Angular detects changes to any input property. This method has an optional parameter that provides the current and previous value of each input property. And note that the OnChanges only works with input properties, so it is only available for use within a child component. Since we've already worked with getters and setters, we know how to use this technique, so let's try out the OnChanges lifecycle hook. Here in the CriteriaComponent, our goal is to monitor the hitCount property and to display a different message if the value provided by the parent component is 0. As with any lifecycle hook, we begin by implementing its interface, OnChanges. I'll use the Quick Fix to add the appropriate import statement. Next, we add the ngOnChanges method. The method takes a parameter of type SimpleChanges. I'll again use the Quick Fix to add the import. This Quick Fix feature of VS Code is great. The ngOnChanges method returns no value, so we specify a return type of void. In the method, let's start by simply displaying the value of the changes parameter in the console. Let's check it out in the browser and open the developer tools to see what we get. Drilling down, our child component has two input properties. For each input property, it provides the previous value, current value, and whether this is the value's first change. When the component is initialized, we see both input properties changed from undefined to a value. After the product data is retrieved, as we can tell from the logging here, the hitCount input property is changed again to the number of retrieved products, five in our example. We can use this information in the ngOnChanges method to perform our desired action. Looking back at the code, recall that our goal is to display a different message in the criteria detail if the hitCount is 0. Let's define a property to hold that message. In the ngOnChanges method, we want to check for a hitCount of 0 and display a No matches found type of message. I'll paste the code, and we can talk through it. We first check whether the hitCount property was changed. Recall that the ngOnChanges method is called if any input property is changed, and we only want to perform our action if the hitCount is the changed property. Since changes is a set of key and value pairs, we access the desired input property using the property name as the key. If the property was changed and it has no current value, we display a message in place of the hitCount. Otherwise, we display the hitCount. Lastly, we modify the criteria template to display the message instead of the hitCount. Viewing the result in the browser, when we refresh the page, we briefly see No matches found before the data is retrieved. Then we see the same criteria detail display we had before. We've now covered how to use the Input decorator to pass configuration, default, or item data from the parent to the child and how to use a getter and setter or the OnChanges lifecycle hook to monitor the parent's changes to that data in the child component and react accordingly. Next, let's look at how a parent component can access the value of any property in a child component or call one of its methods. The trick is to get a reference to the child component. Let's start with a template reference variable.
Template Reference Variable
As its name suggests, a template reference variable is a reference to an element within a template. It is denoted with the hash symbol and a name. We've used a template reference variable earlier in this course. Let's say our child component has several properties and a method. If a parent component's template needs the value of a property defined in a child component or wants to call one of the child component's methods, we can simply add a template reference variable to the child element in the parent's template. This template reference variable then references the child component. Once the parent template has a reference, it can read any of the child component's properties or call its methods. Let's try it out. To filter our list of products, the ProductListComponent needs the Filter by value, but only the child component has the user-entered value from the Filter by box. The parent ProductListComponent needs to get that value from the child component. What do we do? If you said template reference variable, you are right. In the parent template, let's add a template reference variable to the child component's element. A template reference variable needs a hash, and we'll name it filterCriteria. We then have a reference to the child component and can access any of its properties or methods. For now, let's display the value of the listFilter property in the template. We use the template reference variable, dot, and the property name, which is listFilter. Do you think that will work? Here in the browser we type in a Filter by value, and it appears in the parent's template. It works! But our list is still not filtering. Can we use the template reference variable in our component class to access the listFilter and filter our list? Yes we can.
As we saw earlier in this course, the ViewChild decorator allows us to obtain a reference to any element, directive, or child component in the template. There are two ways we can use ViewChild to get a reference to a child component. We can use a template reference variable. Just define a template reference variable on the element in the parent template, and in the component, specify that template reference variable as the ViewChild selector. Notice that the return type here is the type of the child component. This gives us strong typing and better IntelliSense. Alternatively, we can use the child component type. No need for a template reference variable. Simply pass the child component type in as the ViewChild selector. We can then use that reference to access the child component's properties or call its methods. But recall from the prior module that the ViewChild reference is not reliably available until the AfterViewInit lifecycle hook. Let's try it out. In the parent ProductListComponent, we start by defining a property that will contain the reference to the child component. We'll call it filterComponent. Its type is the type of the child component, which is CriteriaComponent. We'll use the Quick Fix to add the needed import. Then we decorate that property with a ViewChild decorator. We can pass in the template reference variable or the type of the child component. We'll use the child type and access the Quick Fix to add the needed import. Now that we have the reference to the child component, we can ask it for its listFilter property. Let's define a parent component property to hold the value of that child property. To prevent confusion, I'll call the property in the parent parentListFilter. Hmm. We need to set this property to the value from the child component, but where? Recall that we cannot reliably assume that we have the ViewChild reference until the AfterViewInit lifecycle hook, so let's set the value there. First, we implement AfterViewInit and use the Quick Fix for the import statement. Then we add the ngAfterViewInit method. In this method, we assign our parentListFilter value to the value from the child component, and we even get IntelliSense here showing us the child component's properties and methods. Now that we have the child component's listFilter value, we can perform our filter when we retrieve the products. We pass the parentListFilter into the performFilter method here. Let's do one more thing. Let's view the child component and define a default listFilter value of cart. Do you think we may finally have a filtered list again? Let's look, and it works. Our list is filtered to the default filter value, and our Hits displays the correct count. But if we change the Filter by value, the filtering does not change. Wah-wah! Looking back at the product-list.component we can see why. We are only performing the filter when we first retrieve the products. We don't refilter as the child's property changes. Our ViewChild technique works great if we need to read a property and aren't concerned with changes to that property. And we can use this technique to call any methods in the child component if we need to, but we can't use this technique to watch for changes. Going back to the browser, note that the parent's template is aware of changes made to the child component's listFilter value. As we type, the listFilter value from the child component is correctly appearing in the parent's template. Why did the parent template get notified of the change, but not the parent component class? It's because of Angular's change detection. In the parent's template, the child component's value is bound using interpolation. Change detection then picks up any changes made to the child component's value, and the display is updated. We'll talk more about change detection later in this course, but we don't currently have an easy way for the parent component to know that the value in a child component is changed. We really need for the child to communicate that information to the parent. We'll do that in the next module. But before we move on, let's remove our default listFilter value from the child component. Now let's finish up this module with some guidelines for using the communication mechanisms covered in this module.
Guidelines and Summary
We've covered several techniques for communicating from a parent component to its child component. So how do we know which to use when? Guidelines can help. First, how do we decide which pieces of our view should be built as child components? When the piece performs a specific task that we want to encapsulate, for example, an input element that provides auto complete or typeahead features. When the piece is sufficiently complex such that we want to build and test it as a separate component, for example, a calendar or a menu component. When the piece could be reused within a component or in multiple components. For example, we may expect that our customer list and order list will have filter functionality, so we can reuse the filter criteria piece as a child component. When would we not separate out a part of a component into a child component? If the piece is tightly integrated with the component or if it is easier to maintain the component as one unit. A parent component may need to pass configuration, default, or item data to a child component. Often, the best way to pass data from a parent component to a child is through an input property, which is a property in the child component decorated with the Input decorator. If the child component needs to perform an operation when the input property is changed, we can use a getter and setter or the OnChanges lifecycle hook in the child component. Favor the getter or setter approach if the child component has multiple input properties and you only need to react to changes to a select set of them. Favor the OnChanges lifecycle hook to react to any input property changes or if you need access to the current and prior values. Note that these techniques are for when the parent component changes an input property and the child component must react to that change. A parent component can also directly reference a child component to request information from its properties or to perform an action by calling its methods. Use a template reference variable to reference the child component from the parent's template. Use the ViewChild technique to reference the child component from the parent component class. We saw, however, that the ViewChild technique did not allow our parent component's class to watch for changes to the child component's properties. For that, the parent component needs notifications from the child, as we'll see in the next module. In this module, we started with a quick look at child components and built one so we could communicate with it. We then focused on parent to child communication. We saw how to use the Input decorator to define an input property and pass data from the parent component to the child. We then examined how the child component can watch for changes to those input properties using a getter and setter or the OnChanges lifecycle hook. We discovered how to use a template reference variable to access our child component properties or methods from the parent component's template and how to use the ViewChild decorator to reference our child component from the parent component's class. We've covered several techniques for communicating from a parent component to a child. Next up, let's see how a child can communicate with its parent.
Communicating with a Parent Component
In the last module, we built a child component and walked through several techniques a parent component can use to communicate with that child. Now we'll look at communicating in the other direction. Welcome back to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and in this module, we examine how a child communicates with its parent. With any healthy parent and child relationship, it is not only important for the parent to communicate with a child, but also for a parent to listen for information from that child. Here once again is our component communication diagram. Our focus in this module is on the communication from a child component to its parent. In this module, we look at several techniques for communicating between a child component and its parent, then focus in on output properties. Let's get started.
Child to Parent
We've seen several ways for the parent to communicate with its child. The parent can pass a value to the child using the Input decorator, or the parent can access the child properties and methods directly using a template reference variable or the ViewChild decorator, but we found that there is no easy way for the parent component class to determine when a child property changes. In that case, we really need the child to notify the parent of the change so the parent can react appropriately. There are several reasons that a child component may need to communicate with its parent. The child may need to notify the parent of changes or other events. In our Filter by example, the parent needs to know when the child's Filter by value changes so it can filter the list. The child may have data that it wants to provide to the parent, such as the result of a calculation or a message for display. A component has several techniques it can use to communicate with its parent component. The most common technique is to use an output property defined with the Output decorator. This allows the child component to emit events to the parent component providing notifications. The child component can provide properties or methods and expect the parent component to access them. The parent component uses a template reference variable or the ViewChild decorator to obtain a reference to the child component. Then once the parent has that reference, it can access the child's properties or call its methods. We walked through these techniques in the prior module. And of course, though not shown here, we can use a service as an intermediary to communicate between the parent and child. We'll look at services as a communication mechanism later in this course. Let's focus now on the output property technique.
The key mechanism that a child component uses to communicate with its parent component is to emit events. The child can notify the parent of a change event, or click event, or any type of custom event. The child can pass data as part of that event. That data is often called the event payload. To send out these events, the child component defines an output property using the Output decorator. The child component uses that property to emit an event and optionally sets the event payload value. A parent template can catch those events using event binding, access the event payload, and call a method to react to the event, in this example, by filtering the list. Does this sound a bit complex? It's really not. It just requires several steps. Let's try it out. Recall from the last module that we modified the product-list.component to display the listFilter value. Let's delete that before we begin. So, how do we implement filtering when the filter criteria we need is in the child component? We set up the child component to emit an event each time the user changes the Filter by input element and pass the current listFilter value to the parent as the event payload. We'll start by adding a property to the CriteriaComponent class that can emit an event. I'll call it valueChange. Since we want this property to emit events, its type is EventEmitter. I'll use the Quick Fix here to add the associated import statement for EventEmitter. Next, we define the type of data that we'll pass with that event and specify that type as the generic parameter. Since we want to pass the changed Filter by value, we define the generic parameter as string. If we want the parent component to use event binding and catch this event, we need to mark it with the Output decorator. Be sure to include the parentheses because even though we are not passing anything to it, the Output decorator is a function, and I'll use the Quick Fix feature to add the needed import. It is customary to initialize the EventEmitter when the property is declared. That way we can immediately start to use it to emit events. So, we set our property to a new EventEmitter setting the generic parameter as string. Next, we need to actually emit the event. Hmm. Let's think about that for a moment. We want to notify the parent component every time the user changes the Filter by input element. How does the child component's class know when the user changes the Filter by input element on its template? Luckily for us, we covered three techniques earlier in this course when we looked at how a component communicates with its template. In the child component's template, we could use a modified version of two-way binding, the long way. In the child component's class, we could use a getter and setter, or we could use the ViewChild decorator to get a reference to the ngModel directive and monitor its valueChanges observable. Let's go with option 2 and use a setter because it is straightforward and keeps the logic in the component. Going back to the code, note that we can't use the ngOnChanges method in this case. The ngOnChanges method is only executed when one of the input properties are changed by the parent component. We want to watch for user changes to one of the child's local properties, so we need one of the techniques from the prior slide. Looking at the criteria template, we see that the Filter by input element is bound to the listFilter property. So, it is that property that we'll convert to the getter and setter syntax using the pattern we examined earlier in this course. First, we delete the property declaration because we can't define the property with a declaration and a getter/setter. Then we add the getter and setter. I'll paste the code following our pattern. We declare a private backing variable. Then we define a getter that returns that backing variable and a setter that takes in a value and sets the backing variable to that value. Each time the user modifies the Filter by input element, the setter is called, so it is in the setter that we want to emit our notification event to the parent. To emit an event, we use our output property this.valueChange.emit and pass along the changed value. That changed value then becomes our event payload. So, our child component changes are complete. In the product-list template, we catch the emitted event using event binding. We use parentheses around the output property name, which is valueChange in this example, to denote event binding. We then specify an expression or method to execute when the event is caught. We'll call a method named onValueChange and pass in $event, which holds the event payload. Looking back at the CriteriaComponent, the event payload is the changed listFilter value. Since we bound the event to a method, we need to create that method in the ProductListComponent class. We named the method onValueChange and passed the event payload in as a parameter. In this example, that payload is a string, and this method doesn't return anything, so we define its return type as void. In this method, we simply call our performFilter method, passing in the value to use as the filter criteria. Checking it out in the browser, and the filtering works. Yay! Let's not touch it again. Going back to the code, let's review where we are with our parent and child components. Our parent is passing configuration and hitCount information to the child, and the child is emitting user change events back to the parent. Each time the parent is notified of a change, it uses the changed value to refilter the list. Cool. Let's finish up this module with some guidelines and a summary.
Guidelines and Summary
In this module, we focused on one key technique for communicating with a parent component. When a child component needs to communicate with its parent, emit an event using an output property. Use this technique any time the child needs to notify the parent of an action and optionally pass along some data. In this module, we focused on child to parent communication, and we walked through how to use the Output decorator to define an output property and use that property to send event notifications from the child to the parent. In these last two modules, we've examined techniques for communication between a parent component and its child component. Up next, services.
Communicating Through a Service
Services are a great intermediary for communication between components. Welcome back to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and in these next three modules, we examine techniques for communicating through a service. Sometimes we need a component to talk to itself. Well, not quite like that. Other times we need a component to talk to its peers. Well, not quite like that either. How does a service help a component talk to itself or its peers? In this context, we can think of a service as a box. A component can put something into the box and get it out later, thereby communicating that information to itself. Why would we want to do that? We'll answer that question in this module. We can also use a service to communicate between multiple components. A component can drop a value into the box, and any other components can retrieve or update that value. Bottom line, we use a service as a mechanism for storing data or state, thereby communicating that state with other components. In this module, we look at what state means and several techniques for managing state in an application. We then examine how to set up a service as a property bag. A component can use that property bag to communicate with itself or any other component in the application. Lastly, we look at service scope and lifetime which affects which components can access the service and when. Let's get started.
What is state in this context? Well, it can be information about the view, such as whether to display images or the user-entered filter string. It can be user information, such as the user's name and roles that is used to tailor the application for the user. State can be entity data, such as our product information that is displayed and manipulated by the application, but originally retrieved from and stored on a back-end server somewhere. It can be user selection and input, such as the currently selected product or edited product name, or any other information or data that the application tracks. There are many techniques available for managing state in an application. At the low end of the complexity scale is a property bag. We define a service that exposes simple properties that our components use to communicate state amongst themselves. We can think of services such as these as property bags, as they are just bags of properties with no real logic or state management. Moving up the complexity scale, we define a basic state management service that retrieves, manipulates, inserts, deletes, and stores application state. Our components use this service to share entity state and communicate state changes using Angular's built-in change detection. Taking it up another notch on the complexity scale, we add notifications using Subject, BehaviorSubject, or similar. What are subjects? We'll cover that a little later in this course. For now, we can think of them as a way to provide notifications when we can't leverage Angular's built-in change detection to communicate state changes. At the top of the scale is ngrx, which is a powerful state management library inspired by Redux. It is based on four core tenants, state is a singe immutable data structure, actions describe state changes, pure functions or reducers take the current state and next action to produce a new state, and state is accessed with a store, which is an observable of the state and an observer of all actions. Ngrx provides a well-defined pattern and techniques for handling complex state throughout an application, but it requires a lot of configuration and plumbing, which can result in a large amount of additional code to create and maintain. This module covers the first technique. The next two modules cover these two. To keep the course focused on component communication, this course does not cover the big topics of ngrx or Redux. Check the Pluralsight library for courses on these topics. For each bit of application state we can use the technique that is as simple or as complex as needed for the application's requirements.
Component to Itself
Sometimes a component needs to communicate with itself, and it can do so using a service. Why would it ever need to communicate with itself? Let's walk through an example. Here is the Product List page. Let's filter to only those products containing the text me and show the images. Now that we have our criteria settings just like we want them, let's view the detail of one of the pages. Looks good. Returning back to the Product List page, and we've lost all of our criteria selections. Bummer! Why is that? When the user makes selections, we store those selections in the component's property. If the user then navigates away from that component, the component is destroyed, and its component property values are lost. The navigated component is then initialized and displayed. When the user returns to the original component, it is reinitialized and displayed, and it has no memory of the user's selections. Gone! What we need is a way for the component to stash away its view state somewhere so it can get that state back each time the component is redisplayed. We do that by creating a service. In the service, we define a property for each bit of state that the component wants to track. In our example, we want to track whether or not to display the image and the user-entered Filter by value. We can think of the service then as holding a bag of property values, so this type of service is sometimes called a property bag. As the user works with the page, we use setters to store the values in the service properties instead of in local backing variables. That way the values are retained even when the component is destroyed. Each time the user modifies the Filter by value, the binding calls the listFilter setter, which in turn sets the property value in the service. When the user clicks the Show Image button the binding calls the showImage setter, which in turn sets the property value in the service. If the user then navigates to the ProductDetailComponent, the ProductListComponent is destroyed, but the service lives on. When the user returns to the ProductListComponent, the binding calls the listFilter getter, which retrieves the value from the service and applies the setting, and it calls the showImage getter, which retrieves and applies that value from the service. Bottom line, a component cannot remember its property values once the user navigates away because the component and all of its state is destroyed, so we build a service to retain those values. Let's look at how to build a property bag style service.
Property Bag Service
An Angular service is a class that can provide functionality above and beyond our components. We can build a service to provide cross-cutting tasks, such as logging; perform operations, such as calculations or business logic; encapsulate functionality, such as data access; or share data beyond the lifetime of a component or across components. When we create a service, we register its provider with an Angular injector. We can then inject the service into any component that needs it. By default, services are basically singletons, which means that Angular maintains a single instance of the service. That way, if we set service properties, any component that injects the service can access those property values. Now let's jump into a demo and start building this service. We are back in VS Code with the APM sample application open. Let's remind ourselves how the application works now. If the application is not currently running, type npm start, and the application is opened in the browser. Click on Product List, and the list of products appears. Let's filter the list by me, and notice how our list correctly filters the results. Yay! And if we click the Show Image button, the product images appear. Click on one of the products to view the detail, return back to the Product List, and our selections are gone. Let's fix this by building a new property bag service. Back in code, let's use the Angular CLI to create our new service. I'll open a new terminal window using the plus sign here. At the command prompt, type ng for the Angular CLI, g for generate, s for service, products/product-parameter to define the folder and file name for the service, and let's add -m products/product.module so the CLI will automatically register the service in the product module. We'll talk more about service registration at the end of this course module. And we see that the CLI created two new files and updated our module. Opening the product.module, we see that the CLI added our new service to the providers array. Now let's open our new ProductParameterService and add the properties that we want to track. I'll close the terminal so we have more space. We want one property to hold whether or not the ProductListComponent should show its image and one property to hold the user-entered filterBy string. And that's it. To build a service whose sole purpose is to be a property bag, all we need to do is define the properties. We could add any other properties here that we want to track, but these two are enough for our purposes. Next, let's open the product-list.component and modify it to use our new service. First, we inject the service into this component using the constructor, then use the Quick Fix to add the associated import statement. Now we can access the service properties. Let's start with showImage. We'll delete the simple property statement and instead create a getter and setter, but with no backing variable. We don't need a backing variable because we'll instead store the value in the service. In the getter, we get the value from the service. In the setter, we set the value into the service. Let's try out what we have so far. In the browser on the Product List page, we'll select to show the product images. Navigate to the details for one of the products, click Back to return, and it remembered our image setting. Yes! Now what about our filter? The filter is a bit more complex now because we no longer have a listFilter property in our component. Recall from the last module that we moved the Filter by functionality into a child component. Looking back at the code, instead of a simple setter here in our ProductListComponent, the ProductListComponent needs to track changes to the Filter by in the child component. This is actually easier than it sounds because we already receive change notifications. In the product-list template, we receive an event notification from the child component each time the Filter by value changes. The event binding calls our onValueChange method. We defined that method earlier in this course to perform our filter. It is here that we can set the filter value in the service. Now we just need to get the value from the service to use it as a default. Instead of a simple getter, we need to obtain a reference to the child component. Then we can use that reference to assign a default value to the Filter by. Recall from earlier in this course that we can get a reference to the child component using the ViewChild decorator. We specify the ViewChild decorator and pass in the name of the child component, which we called CriteriaComponent. We then specify a property name to hold the reference to the child component. Let's call it filterComponent, and its type is CriteriaComponent. We'll use the Quick Fix to add the appropriate import statement. Now, where should we get the service value and use it to set the Filter by default? In the ngOnInit we retrieve the products, and in the callback we perform the initial filter. Let's set the Filter by default there. We use the reference to the child component this.filterComponent. Notice that IntelliSense provides us with a list of all of the properties and methods that we can access. We want to set the listFilter property. We assign it to the productParameterService property value. Setting the child components listFilter property causes the child component to emit a change event that the product-list template catches and calls the onValueChange method. Since we are performing the filter here, we don't need to do it in the callback as well, so we can delete this line. Let's give it a try. We'll again filter the list by me, and notice how our list correctly filters the results. Click the Show Image button, and the product images appear. Click on one of the products to view the detail, return back to the Product List, and our selections are retained. It even set the correct filter by default. It works! We now have a property bag service for a component to communicate with itself. Can we use this technique to communicate between components? Yes we can.
Component to Component
We've just seen how to build a service as a property bag to hold a set of properties. A component can stash away values into these properties and retrieve them later. Our service then provides a way for a component to communicate with its future itself. We can also use a property bag type of service to communicate between components. For example, if we had an advanced search component, it could set values into service properties based on user entries. The list component could read and filter accordingly. In this example, our service provides an intermediary for communication between the search component and the list component. Build a service as a property bag any time a component needs to stash away some property values for itself or for communication with another component. Before we move on, let's take a moment to talk about service scope and lifetime.
Service Scope and Lifetime
When we create a service, we register it by adding the service to the providers array of a component or of a module. Which do we use when? Where we register the service defines the service scope and lifetime. The service scope determines which components can see and access the service. If we want to ensure all components can access a service or prevent access except by a few components, we need to carefully consider where we register the service. The registration location also affects the service lifetime. That lifetime is crucial if we are using a service to retain property values or share state. Let's look at registering a service in a component first. Then we'll look at registering it in a module. When we register a service in a component, the service is accessible to that component and all of its children. What do we mean by children in this context? We mean any components that are specified within the component's template or are routed into the component's router outlet. As an example, let's look at the component hierarchy for our APM application. As with most Angular applications, the root application component is the app component. Recall that in our example, the root app component contains only a router outlet. In that router outlet we can route to the shell component or login component. The shell component contains another router outlet. In that outlet we route to the product list, product detail, or product edit components. The ProductListComponent contains both the criteria and star child components, and the ProductDetailComponent contains the star child component. So, this is a simple view of our component hierarchy. If we register a service, such as our ProductParameterService in the root app component, the service is accessible to it and every component under it in the component hierarchy. And since the app component is our Bootstrap component, the registered service is retained for the lifetime of the application. If we register a service in the ShellComponent, it is accessible to it and every component under it in the component hierarchy, but not the app component nor the login component, and it means that the service is retained only while the ShellComponent is loaded. If we move the registration into the ProductListComponent, it is available to it and each of its children, but not the other product components, and it means that the service is retained only while the ProductListComponent is loaded. Registering our ProductParameterService in the ProductListComponent would seem to make sense because only the ProductListComponent needs it, but if the user navigates to the product detail, the ProductListComponent is destroyed along with the service instance. What happens if we register the service in both the ProductListComponent and the ProductDetailComponent? We get two different instances of the service. We won't be able to share data or communicate through the service, and each instance of the service is destroyed when its associated component is destroyed. But multiple instances of a service are not always a bad thing. Say that we want to display multiple ProductListComponents on a single page, maybe one for in stock products and one for out of stock products. We may want separate instances of the product data access service, one for each displayed ProductListComponent so they can each retain their own list of products. By registering the product data access service with the ProductListComponent, each instance of the ProductListComponent gets its own instance of the service. The bottom line here is that we need to consider both our desired service scope and lifetime when selecting to register a service in a component. If you want to try out the effect of component registration using the sample application or one of your own applications, you can instrument a service to monitor its lifetime using code such as this. Use the constructor to examine when the service instance is created and implement OnDestroy to monitor when the service instance is destroyed. Feel free to use the ProductParameterService and experiment with registering services at different levels of the component hierarchy. If we register a service in a module instead of a component, we don't have these issues. Regardless of the module we register the service in, the service is registered in the application's root, so is accessible to every component in the application for the lifetime of the application. However, if the module is lazy loaded, the story is a bit different. Services registered in a lazy loaded module are only available to components declared within that module. Once the module is loaded, the service is available for the lifetime of the application. Now let's finish up this course module with some guidelines for using the communication mechanisms we've covered.
Guidelines and Summary
With every technique, there are rules for its usage. Well, not really rules, more what you'd call guidelines than actual rules. Use a property bag service as a mechanism for a component to communicate with its future self. In this capacity, a property bag service is great for retaining view state or user selections. A component sets values in the service to retain its state so when the user navigates to another component the state is not lost. When the user returns to the original component, that component reads the values from the service to return it to its prior state. Note, however, that any component that has access to the service can easily read or change any of the property values. This may not be an issue if the application is a manageable size and care is taken not to inject the service into any component that should not access or modify the service properties. We can also use a property bag service as a mechanism for components to communicate amongst themselves. It provides an easy way for a set of components to share property values. And if the properties are bound in a template, Angular's built-in change detection automatically communicates state changes and updates the views accordingly. We'll talk more about change detection in the next module. And again, note that any component that has access to the service can read or change any property. Also, components are only notified of state changes if the properties are bound in the template. We'll look at generating explicit change notifications from our services later in this course. It is important to consider the required scope and lifetime of a service when deciding where to register that service. Register a service with a module or the root application component to ensure that the service is available to all components for the lifetime of the application. Note that service registration in a module works a bit differently if the module is lazy loaded. Services registered in a lazy loaded module are only available to any components defined within that module. They are still available for the lifetime of the application. If you wish to limit which components can access a service, define the service in the correct location in the application's component hierarchy, but be aware of how your choice affects the lifetime of the service. If the user can navigate away from the component, the service instance is destroyed along with any state it was retaining. On the plus side, we can register a service in a specific component if we want multiple instances of the service, one for each instance of the component. This is useful if you need the component displayed multiple times on the view and each needs its own unique instance of the service. In this module, we first looked at state, what it is and several state management techniques. Then we built a property bag service. Our component used that service to communicate with its future self. We can also use a property bag service to communicate between components. And we discussed the importance of carefully selecting where a service is registered. The registration location determines the service scope and lifetime. Here again is our diagram of increasingly complex techniques for managing state as a communication mechanism. We just covered the simplest option, building a property bag service to retain and share a set of property values. Next, let's build a basic state management service.
Communicating Through a State Management Service
Sometimes we need a service to be more than a bag of properties. We need it to retrieve, manage, store, and share our application's state. Welcome back to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and in this module, we build a basic state management service. Communication is all about sharing. A service shares state information to communicate that information to the components of the application. Recall from the prior module that we identified several techniques for managing and sharing state. Then we examined a property bag service and how to use it to retain state in a set of properties. Now we'll move up the complexity scale and work through a basic state management service. In this module, we build a basic state management service. With it, we share entity state, such as products, throughout the application. Then we look at an alternative user interface design that displays multiple concurrent components and examine a simple yet often overlooked mechanism for keeping the state of these components in sync. Then we take a brief look at Angular's change detection and how it communicates changes to that state. Let's get started.
Sharing Entity State
State Management Service
The purpose of a state management service is to provide state values, maintain and update state, and observe changes to that state. To turn a basic data access service into a state management service, we need to add a property to retain the list of entities. This property is populated the first time we retrieve the data, and any other requests for the data use this property. On a get, we then return that list if it's already been retrieved. On a get by ID, we locate and return the requested item from that list. Since we use the retained list as the source of entity data in the application, we'll need to keep that list updated as the user creates or deletes items. On a create, we not only save the new item to the back-end server, but we also add the item to that list. Similarly, on a delete, we delete the item from the back-end server and remove the item from that list. That way our retained list always shows the appropriate items. Note that the retained list will not include any changes made by any other user because it does not go to the database to get the current list of items. If having current data is a critical scenario for your application, you may not want to retain entity state and instead continue to use the data access service as it is, or minimally, consider getting the item directly from the back-end server when the user selects an edit operation to ensure they are always editing the items' current values. Optionally, we could add code to track the time and expire the retained list after a predefined amount of time. This is important to prevent stale data. Now let's jump in to a demo and perform these steps to change our data access service into a state management service.
Demo: Retrieving State
We can build a state management service to manage any application state. The service then shares that state with the components of the application by providing features to retrieve and maintain that state. Let's look at the retrieve features first. Back in our APM application, for our particular scenario, let's assume that the list of products that our company sells does not change very often. We can then improve performance of our application by changing our data access service to a state management service. We start by adding a property to retain the list of products. We'll make it private so that any code that uses this service gets the data through a get method. We'll call the property products, and its type is IProduct array. In the getProducts method, we add code to check this new property. If the property is set, we return its pre-retrieved list of products. Since this method is expecting to return an observable, we use the Observable of operator to create an observable from our products array. If the products array is not yet set, the existing code uses http to get the data from the back-end server. The existing code uses the Observable pipe method to pipe the returned data through a set of observable operators. First, we use the tap operator to display the returned data to the console. The tap operator allows us to tap in to the Observable and defines an action to take that does not modify the Observable stream. Logging the result helps us see when the data is retrieved from the back-end server. You may have noticed the result of this logging throughout this course. Is there something else we need to do here? Yes, there is. We need to set our new products property to the returned array of products. Since setting our property does not modify the observable stream, we can again use the tap operator. We could change this tap operator to a multiline function and perform both operations here. But let's just add a second tap operator. That way we could more easily modify or delete this debugging code. In the fat arrow function, we simply assign our products property to the returned data. Now when we get the products the first time, we'll retain those values to share throughout the application. We could add another property to this service to track the time and include a time check here to expire the product list if it is past its expiry date, but this is fine for our purposes. Let's see if what we've done so far works. In the browser, use the developer tools to view the console, navigate to the Product List page, and we see that the data is retrieved and logged. Click a product to view the detail, and return back to the Product List page. Note that the page appeared a bit quicker than before. That's because we are using the pre-retrieved list of products. Notice also that we don't have another set of data logged to the console. We are not re-retrieving the data. So far, so good! We are still, however, re-getting the data each time we display a detail view, so let's fix that next. Back in the code, now that we are retaining a cache of our products, we can use that list when the user requests to view product detail or to retrieve an item to edit. The getProduct method retrieves one product by ID. The existing code here first checks for an ID of 0. We use an ID of 0 to indicate a new product. In that case, we still want to return an initialized product, so this code is okay as it is, but if the ID is any other value, we want to retrieve the desired product from our cached list. I'll paste the code, and we can walk through it. This code first ensures that we have the list of products. If so, we find the defined products using the array find method. The find method finds the first element that matches the defined criteria, which in our case is a product ID matching the passed in ID. If we find the item, we return it, again using the of operator to create an observable from our item. If we don't find the item, we fall through and get the item from the back-end server using http.get. Notice that we log the data to the console so we can see when the data is retrieved. If all is working well, we should never see this logged message. Let's try it out. In the browser, refresh the Product List page. This reloads the application, so as expected, the list of products is re-retrieved from the back-end server and displayed in the console. Click a product to view its detail. Notice that detail view now displays more quickly, and no additional product data appears in the console. So, no additional HTTP requests was executed. It works! Looking back at the code, our service is now retaining the list of products and sharing that list with any component that requests it. But what about create, update, or delete operations? How do we keep our list updated as the user makes changes?
Demo: Maintaining State
So far, we've set up our state management service to retain application state and share that state with our components, but we also need to maintain that state as the user creates, updates, or deletes state data. Let's jump right back into the demo and see how it's done. A user may want to create a new product, update an existing product, or delete a product. We need to ensure our cached list reflects these operations. Let's see how the operations work now without any changes to our service. Bringing up the browser, let's close the developer tools for more space. Let's start with an Edit. We'll change the name to Long Handled Leaf Rake. Click Save, and our updated item appears in the list. Excellent, and with no other modifications to our code. How about adding a product? We'll add a Shovel, give it a product code, and Save. Our new product is not on the list. Any idea why the Edit worked and the Add didn't? Looking at the code, when we retrieve an item for edit, we are retrieving the item from our retained list of products. We return that item by reference, so when the item is edited, it is editing that item in the list, and our edit works as expected. But when the user creates an item, we return a newly initialized product. And when the user selects to save, the createProduct method posts the new item to the back-end server, but does not add it to the retained list of products. Let's fix that. We need to add the created item to our retained list of products, but where? We could add the code here, before the return statement, but then we are directly adding the new item to our list instead of first posting the item and adding the returned item to the list. There are two issues with that approach. First, it would then be possible to add the new item to our list even if the post operation fails. That would not be good. Second, we may be updating some of the property values on the server, such as assigning a new product.id or setting an update date. We instead want to add the product returned from the server to our retained list of products. So, we'll add the code here, after we post the product to the back-end server, and the created item is returned. We'll again use the tap operator to perform our operation and call the array push method to push the new item onto the products array. You may be wondering why are we setting the product.id to null here? That's because the in-memory web API that we are using as the back-end server requires an ID of null for it to assign a new ID. It then automatically assigns the product.id to the next available ID. Let's try it out in the browser. Notice that the change we made to the Leaf Rake is gone. That's because the in-memory web API that we are using for our back-end server refreshes the data from our hard-coded set of initial data every time we refresh the application. That won't be the case with a real back-end server. Click Add Product, and create the same product, a Shovel with a product code. Click Save, and our new product is on the list. It's working. Looking back at our code, we have a similar issue with the delete. We need to remove the product from the list when the item is deleted from the back-end server. Where would we put that code? If you said within the pipe method as part of a tap operator, you are correct. I'll paste the code, and we can talk through it. To remove the item, we find it by index using the array findIndex method. If the item is found, the index is greater than -1, so we use the array splice method to remove the one item found at that index. Checking it out in the browser, we can see our newly created item is gone. That's again because of the in-memory web API. To try out the delete, click on the Edit button for one of the rows. Then click Delete. Confirm the deletion, and our deleted product is correctly removed from the list. Yay! Going back to the code, we now have an operational, but basic state management service we can use to share entity state between the components of our application. With our current user interface design, we only display one primary component at a time. So even though we are sharing data using the state management service, we aren't really using it for direct communication between components of the application. Let's modify our design to display multiple components at the same time and see how keep their view state in sync.
Displaying Concurrent Components
Let's consider a different style of design for our user interface. Often, users want a list of possible items on the left. Selecting an item displays the details on the right. Looks nice. With this design, the list component on the left needs to signal to the detail component on the right when it's time to display a different product. Let's break down this new UI. There is an outer product shell component here that contains the two sibling components, the product shell list component on the left and the product shell detail component on the right. Since this is a course on component communication, I want us to focus on how to communicate between components without spending a lot of time creating these components and their associated templates, so I pre-built the three components for this user interface. Let's take a look at these components and add the code required for the component on the left to notify the component on the right when the user has selected a product. Back in the demo application, you can find the components for this new UI under the products folder in the product-shell folder. Here is the product-shell.component. In the HTML, you can see that it displays the product-shell-list and product-shell-detail components as child components. It uses the Bootstrap styling classes to lay them out side by side. The product-shell-list template uses an ngFor to display the list of products with their product codes. Each product is displayed in a button so the user can click on it to select the product. The product-shell-detail template displays the detail for a product, similar to our other product-detail.component. To hook up this new product shell, let's open the product.module. First, we add the three components to the declarations array, ProductShellComponent, ProductShellListComponent, ProductShellDetailComponent. Then we add the appropriate import statement for each. In the route configuration, we'll change the path for the default route from the ProductListComponent to the ProductShellComponent. This will display our new user interface. Since the list and detail components are child components in this new ProductShellcomponent, the list and the detail don't use routing, and this routing path won't be executed. We could remove it, but I'll leave it here so we can easily change back to the original user interface design later. Checking it out in the browser, Click on Product List, and here is the left part of our new user interface, but clicking on an item does not display the detail. We need to provide some way for this left side list component to communicate the selected product to the detail component so it will appear on the right.
Keeping State in Sync
When we display multiple concurrent components in a view, we often need to keep their view state in sync. For example, a user selection in one component may need to be synchronized with other displayed components. Let's take a look at one of the best, but often neglected, ways to keep state in sync between components. As we discovered in the last module, one way to provide communication between two components is with a property in a service. In this case, we want a property for the currently selected product. This list component would set the property when the user selects a product, and the detail component could read this property to obtain the current selected product and display the appropriate details. Going back to the code, where should we put this property? We could create a simple property bag service to hold this property as we did in the prior module, but since we already have a state management service that tracks information about our products, let's just add it there. We'll call the property currentProduct and define its type as an instance of IProduct or null. A vertical bar defines a TypeScript union type. A union type describes a value that can be one of several types. In this case, the type is a product or a null. We want to allow null in the case that the user has not yet picked a product or just deleted the selected product. While we're thinking about it, let's modify the deleteProduct method to ensure that the currentProduct is set to null after the item is deleted. That way the detail display won't show a deleted product. And let's modify the createProduct to set the currentProduct to the inserted item. Then that new item will appear in the detail page after the create operation. Note that when we change a single line fat arrow function into a multiline function we need to add curly braces. Now that we have the currentProduct property, we need to set it when the user clicks on a button in the list view. Let's open the product-shell-list.component and its template to the side. What are we trying to do here? When the user clicks one of the product buttons, the template needs to notify the component of the click event so the component can set the currentProduct property in the service. How does the template notify the component of the click event? We bind to the click event and call a method in the component class. We'll call that method onSelected. We'll need to know which product the user selected, so let's pass that in. The product variable here is provided by the ngFor structural directive. Now, when the user clicks on one of the products, the binding catches the click event and calls the onSelected method, passing in the selected product. Now we just need that onSelected method. In the associated component, we create the onSelected method, giving it a parameter for the passed in product. It is here that we use the service and set the currentProduct property to the passed in product. Now we just need the detail component to read that property and display that selected product. Let's close the product-shell-list and open the product-shell-detail.component and its template to the side. Looking at the product-shell-detail template, all of the elements are bound to a product property. So, let's define a product property in the component class. We'll set it to the value from the productService. Do you think that will work? Nope. This line of code only executes one time when the component is first initialized. At that point in time, the currentProduct property is most likely undefined. When the user clicks on a product and the currentProduct property in the service is changed, this code is not picking up that change. Any idea what we may need here instead? If you said a getter, you are correct. If we replace the product property declaration with a getter, Angular's change detection will get the latest value from the service every time it's changed. The getter returns a product or null if there is no current product using the union type. Within the getter, we return the productService.currentProduct property value. Note that we don't need a setter here because this detail component never assigns a value to this property. Its bindings only read it. Let's give it a try. Here again is our list of products. Select one, and it works. Any item the user selects is displayed with details on the right. Sweet! Let's try a delete. Select Edit, Delete, confirm the delete, and it correctly disappears from the list. No product is selected, so we see no detail. That's what we wanted. Let's try an add. We'll again add a Shovel, add a product code, Save, and it appears on the list. Plus, it was automatically selected, so its details appear. It's all working, but wait a minute. How does this work? How is the detail component notified of changes to the currentProduct property from the service? How does it know to redisplay with new product values? Looking back at the code, this works because of binding and Angular's built-in change detection. Let's examine that a bit more.
One of the key features of Angular is its binding. We set a property in a component's class, bind it in the component's template, and it appears on the page at runtime. To display the product details, we bind to properties of the product object that is defined in the component. If we change the product object or any of its properties in the component class, those changes are immediately reflected in the page. That's when we see the effect of Angular's change detection. The change detection watches for changes to the bound property values and updates the binding when it sees any changes to those properties. Okay, we know all of this, but how does it work when the changing value is in a service? In our current APM sample application, our ProductService retains a currentProduct property. Our list component sets this property when the user selects a product from the list. The detail component uses a getter to retrieve this value from the service. The product property is bound in the template, so Angular's change detection watches for changes to that property, even if that property is defined in a service. When the user selects a different product from the list of products, the list component sets the currentProduct property in the service to the selected product. Angular's change detection picks up that the value was changed and reevaluates the binding. The bindings call the getter, which returns the modified currentProduct from the service, and the newly selected product's details are displayed. All of this works because the property is bound, and change detection provides the change notifications. What if it wasn't bound? Is there a way to still get change notifications? Jumping back to a demo, let's change the property name so it is no longer the property bound to our template and call our getter and ngOnInit, logging out the result. Looking at this code, it's only going to calling the getter one time, when the component is first initialized. Without the binding and change detection, our component won't be notified of changes to the service property. When the user selects a product from the list, there is no event or other notification to let us know we need to call the getter again and retrieve the currentProduct property from the service. So, what do we do? One technique we can use is a timer. We can set a repeating timer, and every time the interval elapses, we can check for a new value. We'll use the timer from RxJS, which emits items periodically at a specified interval. Then we modify the code in the ngOnInit to use the timer. I'll paste the code, and we can talk through it. The first argument to the timer is the delay. We want to start the timer immediately, so we specify 0. The second argument is the timer interval. We want to check the value every second, so we set it to 1000 ms. This timer is an observable, so we subscribe to start the timer. Every second this code emits an item, and the function defined within the subscribe is executed. In this case, we call the getter to get the latest value of the service property and log it. Using a timer means we are not really reacting to a property change, but rather polling for a change. Let's check it out in the browser. Open the console, Refresh, and we see in the console that every second our timer is logging the currentProduct value. It's like have you changed yet, have you changed yet, have you changed yet? Click a product, and the next time the timer ticks it sees the new value of the product and again keeps checking every second. Going back to the code, we can use this timer technique if we need a simple way for a component to check for service property changes, but note that this is only needed when that property is not used in binding, and don't forget to unsubscribe to stop the timer. There are, however, some issues with using a timer like this, and we'll talk through those when we look at guidelines later in this module. Bottom line, if we are working with bound values, we can leverage the power of Angular's change detection. Our components are then automatically notified of changes to our service properties. If the service properties are not bound in the template, we won't get notified of changes. We implemented a quick and dirty way to poll for changes using a timer; however, there is a better way. We'll see how to send explicit change notifications from our service in the next module. For now, let's undo these last code changes and allow change detection to take care of the change notifications. Checking it out in the browser, close the developer tools, and let's ensure that our app still works. Yep. Let's finish up this module with some guidelines for using the communication mechanisms covered in this module.
Guidelines and Summary
Guidelines are helpful when evaluating communication techniques. In the last module, we built a property bag service, and in this module, we created a basic state management service. What is the difference, and when should we use which? A property bag service exposes a simple set of properties. It is often used to retain values for a component to communicate with itself for view settings, for example, or to pass data between components, such as search criteria passed from a search page to a list page. We use a state management service to retrieve, manage, and store state values, in addition to sharing those values between our components. We used it in our demos to work with our product entity data, but we could build a state management service for any type of application data that requires management or storage. Using a state management service has several benefits. It can provide a set of methods that encapsulate access to the data store to create, read, update, or delete state values. It retains and shares state values with the components of the application. Components can use the shared data in the service instead of contacting the back-end server for that data, minimizing hits to the back-end server and improving performance. And Angular's change detection provides notification of any changes to state values, if the properties are bound in the template. All we need to do is define a getter. But when using a basic state management service, such as the one we created, we do have some considerations to keep in mind. Any data retained by the service can get stale and won't pick up changes from any other user. This may not be a problem if you are retaining data that doesn't change often, but consider expiring the data after a specific time and re-get current values. Also consider always getting fresh data before performing an edit operation. As we saw in the demos, the service does not provide explicit change notifications. So, if a service property is not bound, the component is not notified of changes to that property. We'll look at this further in the next module. Also, as we have written it, our basic state management service does not enforce immutability. That means that the state values can be changed by any component with access to the service. For example, our list of products is not immutable. Once the service passes the list of products to a component, there's nothing stopping that component from adding, removing, or updating products directly in that list. We currently handle this by convention, meaning that we expect no other component to add or remove items, but we don't technically prevent it. This is okay if we are the sole developer or are working with a small team. On a larger team or with a more complex app, we may want to enforce immutability using more complex code or turn to a more full-featured state management library such as ngrx. So what is this magical and underutilized technique for keeping view state in sync across our components? Yep, it's a getter. We can use a getter any time we need to keep bound data in sync. We define a property in a service, bind that property in a template, and use a getter in the component class. Angular's change detection does the rest. This technique is simple and easy to understand; however, this technique only works for bound data. What about that timer technique we saw in this module? It provides a way to poll for changes to a service property. However, a timer is often not the best approach. Set a timer interval too short, and it processes much more than needed. Set a timer interval too long, and there may be a delay between the property change and the reaction to that change. Because timers impact the timing of operations, they can cause race conditions in the application. There is a better way to get change notifications from a service, and we'll see that in the next module. In this module, we created a basic state management service. Our components use that service to share entity state, such as our list of products. We changed our user interface to display multiple concurrent components and examined how to keep view state in sync using a service property and a component getter. Then we took a closer look at change detection and how it communicates state changes. Here again is our diagram of increasingly complex techniques for managing state as a communication mechanism. In the demos in this module, we built a basic state management service. That service worked great for bound values as change detection handles change notifications. Up next, let's see how to add explicit notifications to our state management service.
Communicating Through Service Notifications
Services are not only great for sharing data, they can also provide notifications. Welcome back to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and in this module, we examine how to communicate through a service by broadcasting notifications. A service may have something important to say. A different product was selected or the user just logged out. Instead of waiting for some component to ask for this new state, the service can broadcast a notification. Recall from the last module that we built a basic state management service. That service retrieves, manages, and stores state values, in addition to sharing those values with our components. However, it does not provide explicit change notifications. Now, we'll move up the complexity scale and add notifications to our state management service. But it doesn't have to be a state management service. We can use the techniques presented in this module to add notifications to any service. In this module, we examine several techniques for broadcasting notifications from our service. First, we'll talk through why we don't want to use, what seems like an obvious choice, an EventEmitter. Next, we discuss what a subject is and how to use it to broadcast notifications. Then we cover BehaviorSubject and when to use it instead. Let's get started.
In the prior module, we looked at how to use a service as an intermediary to communicate the selected product from the list component to the detail component. We achieved this communication by defining a property in a service, setting that property when the user selects a product from the list, and defining a getter to get the value from the service. When the user selects a product from the list, the list component sets the currentProduct property in the service. When the detail component needs that value, it pulls it from the service. If the user selects a different product, the list component changes the property value in the service, but the component is not aware of that change until it pulls that value. Luckily for us, if the getter property is bound in the template, change detection re-evaluates the binding and calls the getter, which re-gets the value appropriately, but that is only true if the value is bound. Instead of waiting for a component to ask for new state, especially if the value is not bound, the service can broadcast a notification. This basically pushes the change to any component subscribing to those notifications. Let's walk through how a notification would work. Here we have our list of products. The user performs an action, such as clicking a button to select an item. The template uses event binding to catch that event and calls a method in the component. Instead of setting a service property as we did in the prior module, the component calls a method in the service notifying it of the change. The service then broadcasts a notification. Any component or service can listen for that notification and respond accordingly. So, how does the service broadcast this notification? The first technique that may come to mind is to use an EventEmitter because we basically want to emit an event. Earlier in this course, we used an EventEmitter with an Output decorator in a child component so it could send notifications to its parent. In this example, we send out a notification every time the user changes the Filter by value. But the Output decorator technique shown here only allows communication between a child and its parent. We could set up something similar that doesn't use the Output decorator, but best practices discourage use of the EventEmitter except with an output property, as shown here. And it's recommended that no code subscribe to an EventEmitter, meaning there is no way to get event notifications except through event binding in a template. So, using an EventEmitter to broadcast notifications from our service is not a recommended option. A better choice for service notifications is to use a subject or a variant of a subject, such as BehaviorSubject. What's that all about? Let's take a look, starting with a basic subject.
A key purpose of a subject is to send out notifications. We can use a subject anywhere in our applications, but often encapsulate them in a service. But what is a subject? A subject is a special type of observable that can multicast a value or event to multiple subscribers. Any component or service that subscribes to the subject's observable will receive notifications. A subject is also an observer. An observer allows us to push new data into an observable sequence. Any component or other service can feed new values into the subject using its next method. The new value is then multicast to all subscribers. This sounds like just what we need if we want to use a service as an intermediary to pass notifications between our components. But let's step back a moment and remember that our application works perfectly fine without a subject. Since the detail template is binding to the currentProduct property, Angular's change detection handles change notifications for us. Then why modify our code to use a subject? There may be situations where you can't use binding and you need explicit notifications from a service. So, even through a subject is not required in this particular example, it's important to understand how to use them. Let's see how to change our code to use a subject. Before we jump into a demo, let's outline what we need to do, filling in more of the details from our earlier discussion. Here is our list of products. When the user selects a product in our list of products, the template catches that selection and calls a method in its component. We added this code in the prior module. In the onSelected method, instead of setting a property, we'll call a method in the service and pass in the newly selected product. In that service method, we receive the newly selected product and call the next method on the subject to push the new value into the observable sequence. This broadcasts the notification, passing along the selected product. The Subject is declared here. We make it private so that no other code can access it. We define a second property and use the asObservable method of the Subject to expose its observable. Any component or service can subscribe to this observable to receive notifications. Note that we could instead define the selectedProductSource as public and allow subscribing to the Subject directly. But then any other code could modify the Subject or call its next method. By making it private, the actual Subject is encapsulated in this service. We only expose its read-only observable. Then in the detail component, we subscribe to the Subject's observable to receive a notification when the selected product changes and display the values for the new product. Are we ready to give this a try?
Even though our application works perfectly fine without it, in this demo, we'll change our code to use a subject and broadcast explicit change notifications. Our list component will push the user's selected product into the subject's observable sequence, which in turn will broadcast that selected product to the detail component. We are back in the APM sample application once again, looking at the product.service. Here we have the currentProduct property that is tracking the user's selected product. Let's delete that and use a subject instead. We want to encapsulate our subject to ensure that no other code can use it directly, so we'll make it private. We'll call the subject selectedProductSource since it retains the source of knowledge about our selected product. To immediately work with our subject, we initialize it as a new Subject. The generic parameter is the type of data we want to include in the notification, which in our example is a product. We'll specify IProduct union null to allow setting the selected product to null when there is no selection. Then we'll add the associated import for Subject. Since we made the Subject private, we need to expose a property that provides the associated observable. We'll call the property selectedProductChanges and postfix the property name with a dollar sign. By convention, this indicates that it is an observable and not a simple property. We initialize this property to this.selectedProductSource.asObservable(). This exposes the read only observable from our Subject. We now have the observable part of our Subject implemented. Any component or service can subscribe to this observable to receive notifications. But they won't receive any notifications until we push data into the Subject's observable sequence. Let's do that next. Every time the selected product changes, we want to push that selection onto the observable. Where should this code go? It would make sense to add it to the list component so it can be called every time the user selects a new product. But since Subject is private, we can only access its methods from within this service. Let's build a publicly facing method that wraps the Subject method we need. I'll paste the code, and we can talk through it. We take into this method the selectedProduct. The type of this parameter can be IProduct when passing a product or null if there is no currently selected product. In this method, we call the Subject's next method to push the selectedProduct into its observable sequence. The observable then broadcasts the notification, passing along the selectedProduct. We've now set up the Subject's observable so any component can subscribe to its notifications and wrapped its observer so any component can push a new value to the Subject, which is then broadcast to all subscribers. But we have a few syntax errors that we need to fix. The code in our deleteProduct method cleared the currentProduct property. We no longer have that property. We'll instead call our new method and pass in null. The null value is then pushed onto the Observable sequence and broadcast to all subscribers. This makes sense because when the selected product is deleted, there is no selected product. The code in our createProduct method, sets the currentProduct to the created product. We'll also change this to call our new method as well, passing in the newly created product. The new product is then pushed onto the observable sequence and broadcast to all subscribers. This also makes sense because when a new product is created, we want it set as the selectedProduct. What's left to do? Well, we don't currently have any subscribers, and we aren't yet pushing the user selectedProduct on the observable sequence. Let's do this second task first. In the product-shell-list.component, when the user selects a product from the list, we need to call our new service method and push that selection to the observable. So, what do we change here? If you said the onSelected method, you are correct. In the onSelected method, instead of setting our original currentProduct property, we call our service method and pass in the newly selected product. The service then pushes the selected product onto the observable and broadcasts it to all subscribers. Speaking of subscribers, we don't have any yet. Our product-shell-detail.component wants to be notified every time the selectedProduct changes so it can update its display. It currently uses a getter to ensure that it has the most recently selected product. But we have a syntax error here because the service no longer has this property. Instead of pulling new values with this getter, let's subscribe to receive notifications when the selectedProduct changes. First, we replace the getter with a basic property declaration. Then we need to subscribe to receive change notifications. Where should we subscribe? If we want to start receiving notifications as soon as the component is initialized, we'll add it to the ngOnInit method. I'll paste the code, and we can talk through it. We access the selectedProductChanges$ observable and call its subscribe method. Within the subscribe, we'll get the selectedProduct as part of the notification. We then set our local product property to the selectedProduct. Since the template is bound to this product property, the new product will display. We now have one subscriber that responds each time the user selects a product. Let's check it out in the browser. If we made all of these changes correctly, it should work as before. And it does, whoosh. But to really show off the multicasting, we need more than one subscriber.
Demo: More on the Subject
Subject vs. BehaviorSubject
When the user navigates between the product shell page and the product edit page, the product shell page, along with the product shell list and product shell detail are destroyed. The edit page is initialized and displayed. When the user completes or cancels the edit, the edit page is destroyed, and the product shell page again appears, but with no product detail. Throughout this process, our service Subject still retains the last selected product. When the product shell and product shell detail components are initialized, they again subscribe to the selectedProductChanges$ observable. The problem is they don't receive a notification of the last set value, so they won't know about a selected product until the user picks another one. How do we fix that? How about a different kind of subject? We've just gone through the code for using a subject. We used it to provide notifications from our service to any component that subscribes, but what if we need slightly different functionality? Luckily for us, there are several variants of Subject available to us. One such variant is a BehaviorSubject. A BehaviorSubject works like a subject except for two key features. It requires an initial value, and it always provides the current value on any new subscription. What does that mean for us? Let's walk through our edit scenario again, but this time with a BehaviorSubject. When the user navigates between the product shell page and the product edit page, the product shell page, along with its children, are destroyed. The edit page is initialized and displayed. When the user completes or cancels the edit, the edit page is destroyed, and the product shell page again appears. Throughout this process, our service still retains the last selected product. When the product shell and product shell detail components are initialized, they again subscribe to the selectedProductChanges$ observable. Only this time, since we are using a BehaviorSubject, it provides the last selected product on each new subscription so the components can react accordingly. That's what we want. How do we change our code to use a BehaviorSubject instead?
Angular Component Communication
Let's jump back to a demo and try out this same scenario, but with a BehaviorSubject. Changing our code from one variant of subject to another is easy. In the product.service, we change the Subject to BehaviorSubject and add the needed import. Unlike the Subject, a BehaviorSubject expects an initial value. We pass in a value of null to the constructor since no product is initially selected. That's it. Let's see how that little change affects our application. Bringing up the browser, select a product, and we see the details on the right and the number of months here at the top. It's working as before. Now let's select a product for edit and Cancel. Notice now that when we return, the last value of the selected product was provided. We see the current product details here and the correct number of months on the market here. It now works, even when the components are destroyed and reinitialized. Such a small change, but a big impact on the usability of the application. One other fix we may want to consider here. Notice that when we return, the detail appears with the correct product, but the list itself doesn't show that the product was selected. What to do? The list can also subscribe to the subject's observable and be notified of changes. So, these subject techniques presented here can also be used for a component to communicate with itself. Going back to the code, let's open the product-shell-list and its template to the side. Let's start by adding a property to the product-shell-list to hold the selected product. Then in the template, we'll use ngClass to set the active style class. We set it when the ID of the product displayed on a button matches the ID of the selected product. And notice the safe navigation operators here in case the values are null. What's next? The code to subscribe here is similar to what we added to the product shell and product shell detail components. Want to give it a try yourself? If so, pause the video now. How did you do? Here are the changes I made to the product-shell-list.component. I added the subscribe in the ngOnInit and set the selectedProduct within the subscribe method. Does yours look similar? Viewing it in the browser, we can see that it works as before, but now the selected product has a more obvious style. And when we click to edit, and return, the product shell detail appears correctly on the right, the number of months is shown at the top, and the appropriate product is shown selected in the product shell list on the left.
Demo: Cleaning up a Subscription
We are now subscribing from three components. Before moving on, let's clean up a bit and unsubscribe when we're done. We are looking at the product-shell-detail.component. Let's clean it up just a bit. To prevent memory leaks, we should turn off the subscription when we are finished with it. We normally do that in the OnDestroy lifecycle hook. We begin by implementing OnDestroy and add the associated import. Then we create a property to hold the subscription. It is of type Subscription, and we again add the import. When we subscribe, we store the subscription in this new property, and in the ngOnDestroy method we unsubscribe to clean up. I'll leave it to you to clean up our other two subscriptions using similar code. Let's finish up this module with some guidelines for using the communication mechanisms covered in this module.
Guidelines and Summary
As we've seen throughout this course, guidelines are helpful when evaluating communication techniques. The first question we should ask ourselves is whether a service needs a subject or one of its variants at all. It doesn't need a subject if notifications are not required. If the service is simply a bag of properties or shares entity data, it may not need to provide notifications. And you don't need a subject if the notifications are for changes to bound properties. Prior to this module, our components used change detections and getters, and all of the features we added in this module can be accomplished with similar techniques. No need for a subject. You do need a subject if the service needs to provide notifications and those notifications are more than just changes to bound properties. As a side note, subjects also have purposes beyond component communication. For example, you can use them to sync multiple observables, but those uses are beyond the scope of this course. Once you determine that your service does need a subject, the next question is which variant? Use a plain Subject if there is no initial value and the service only broadcasts items as they are pushed to the observable. Use a BehaviorSubject if you want an initial value and if the service needs to provide the most recent item upon subscription, and then broadcast items as they are pushed. If you have slightly different requirements, such as always providing all prior values upon subscription, check out additional variants of Subject here. This module covered how to broadcast service notifications. We looked at why we should not use an EventEmitter and then examined how to use a Subject and a BehaviorSubject. Here once again is our diagram of increasingly complex techniques for managing state as a communication mechanism. In this module, we added notifications to our state management service, but we could've added them to any service. Our components can then subscribe to these notifications and react as needed. If you are interested in taking the complexity up further, check out the Play by Play Angular and ngrx course in the Pluralsight library. With that, we are finished using services as an intermediary for communication between our components. Up next, let's look at routing.
Communicating Using the Router
Angular's router provides several unique ways to communicate information between components. Welcome back to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and in this module, we communicate between our components using the router. Sometimes we just want to pass a little information from one route to the next. The router provides easy to use features to do just that. Looking at our component communication diagram, we've covered how a component communicates with its template, including child components on that template. And we've examined techniques for components to communicate using a service as an intermediary. We can leverage these techniques to communicate between our routed components. But if we just want to pass a bit of data from one route to the next, the router provides an easy way to do that without the need for a service. In this module, we start with a quick review of routing basics. Then we take a high-level look at passing data to a routed component using several kinds of route parameters, required, optional, and query parameters. Let's get started.
The primary purpose of routing is to provide a mechanism for the user to navigate through an application. Let's think back to our original user interface design for a moment and do a quick review of how basic routing works to ensure we are all on the same page with the terminology. Most applications provide some way for the user to navigate between features, such as a menu or a toolbar. In the HTML for the menu or toolbar, we use the routerLink directive. The routerLink defines the route to activate when the user selects the specified option. In this example, we activate the products route. The address bar is then modified to include the specified route. The router sees the change to the address bar and sequentially examines the list of route configuration paths for a match. It selects the first match it finds. It then loads the specified component. In this example, it's the ProductListComponent. The router looks for a router-outlet directive specifying where to put the routed view. It then displays that view in the location defined by the router-outlet. That's how basic routing works. We can expand on these basics and define multiple router outlets for child routes or secondary routes, often called auxiliary routes. In our original sample application, we have a router outlet specified in our app component. Notice that the app component's template has no menu, header, footer, or any other display elements, just the router outlet. We route to this router outlet to display a page without any accessories, such as for a login page. We also use this router outlet to display our application shell component. This app shell component includes our heading and menu. The app shell component contains a child router outlet. It is here that we route any views that we want to display with a menu, such as our original Product List page. To better demonstrate communication between non-routed components, we modified the product's route to instead display a product shell component. Inside the product shell component, we define both the product shell list and product shell detail components as child components. So, we no longer route between these two components. We do still use routing to navigate to the product shell component and to the product edit component. Currently in our sample application, our route configuration in the product-module looks something like this. The default product's path routes to our ProductShellComponent, and we aren't using the path to the ProductDetailComponent since the detail is defined as a child component in this design. If you want to examine the routing features provided in the original user interface design, simply change the default product's route configuration to route to the ProductListComponent instead. If routing is new for you, or if you want to learn more about the many features of routing, check out the Pluralsight course Angular Routing or the book Angular Router by one of the original members of the Angular team. In this module, we'll take a high-level look at using route parameters to communicate between routed components. For more information on route parameters, including detailed demos, see the Angular Routing course.
With route parameters, we can pass a bit of data from one component to another on the route. Let's walk through a route parameters example using our original user interface design. When the user clicks on one of the products, we want to route to the Product Detail. We need to provide the ID of the product so the detail knows which product to display. It seems like overkill to create a service just for this purpose. Instead, we can pass data between routes using route parameters, such as the ID of 5 shown here. The routed component can then read the parameter from the route and display the correct product. Angular provides three different types of route parameters with slightly different syntax for each, required, optional, and query parameters. Let's look at each one and when you would want to use it. Like its name suggests, use a required parameters when the route requires a parameter. For example, the detail and edit pages cannot display the appropriate content without knowing the ID of the item to display. When using required parameters, the parameters must be defined as part of the route configuration. Each required parameter is denoted with a colon and a placeholder name as shown here. We can add any number of parameters, separated with slashes. To activate a route with a required route parameter, use the routerLink. Specify the parameters as additional elements in the associated array. To activate a route in code, use router.navigate and pass in the same array. The resulting URL will look something like this. The routed component can then read the parameter from the route using syntax like this. The parameter passed into the get method here must exactly match the placeholder name here. So, this is how we set up and use required parameters. But there are other types of route parameters we can use. Sometimes we want to communicate optional information on a route. We do that using optional or query parameters. For example, here is a Product Search page we could add to our sample application. Using this page, the user can specify any desired search criteria. We then pass the entered criteria along to the Product List page. We don't want to make each of these elements required, so using optional or query parameters is a better choice. When defining optional parameters, we don't specify them as part of the route configuration. We instead specify the parameters when activating the route, either in the template or in code. The optional parameters are specified as a set of key and value pairs in a single element of the array. The resulting URL looks a bit odd with the key and value pairs separated with semicolons. The routed component can then read the parameters using syntax like this. The argument to the get method is the key from the key and value pair. Just like optional parameters, query parameters are not specified as part of the route configuration. We instead specify them when activating the route, either in the template or in code. With query parameters, we have an additional directive, queryParams. We assign queryParams to the set of key and value pairs defining the query parameters. The resulting URL may look more familiar as it follows the standard syntax for query parameters. Reading the route uses a different property. Here we use queryParamMap instead of paramMap here. The argument to the get method is again the key from the key and value pair. So, the optional and query parameters are different in syntax and generate different URLs, but query parameters also support an additional feature. We can use query or optional parameters to communicate a set of key and value pairs to another component using the route, but only the query parameters can automatically retain those parameters and pass them back. In this example, the parameters passed back from the Product List could be used to set defaults when the user returns to the Product Search page. Use route parameters any time you want to communicate a bit of data from one route to the next. Just be sure to select the type suitable for your needs and use the syntax appropriate for your selected type. To walk through complete code examples of each of these, check out the Angular Routing course in the Pluralsight library.
Guidelines and Summary
How about some guidelines for using route parameters? Route parameters are often the best way to pass a bit of data from one component to another on the route. Parameters are preferred over services or other techniques because they are simple, straightforward, and the resulting route URLs are bookmarkable and shareable. For example, if a user wants to share the detail for product seven with another user, they can simply email the URL. The recipients can click on the URL and be taken directly to the detail for product seven. But keep in mind that any specified parameters appear in the URL, so it's not a good candidate for information that you don't want the user to see. It's also not good for large amounts of data. For example, you would not normally use a route parameter to pass a complex object, such as a product, from one route to the next. Bottom line, use route parameters to pass small amounts of data between routed components. Use a service instead if you want to share objects or need to share data between components that are not routed. Once you've determined that you want to use a route parameter, the next question is which kind? Use required parameters when specific data must be provided to the routed component. Routing from the product list to the product detail or product edit are examples. The product detail and product edit must have the parameter to know which product's detail to display or edit. Use optional parameters when the data is optionally provided to the routed component. For example, a product search page with optional criteria selections. Use query parameters instead of optional parameters when the data is optionally provided to the routed component, but a query style URL is desired or the data should be retained across routes. This module took a quick look at using the router for communication between components. We reviewed router basics and then briefly examined how to use each of the three types of route parameters, required, optional, and query parameters. We've now looked at communication between a component and its template using a service as an intermediary for communication between components and using route parameters to pass data between routed components. Only one short module left.
As you have seen throughout this course, Angular provides numerous ways for components to communicate. Welcome back to Angular Component Communication from Pluralsight. My name is Deborah Kurata, and the final words in this course include a recap along with a few pointers to additional information. Let's jump right in to this short module.
The components and other parts of our application need to work together to get things done. And to work together successfully, they need to communicate clearly. We started with communication between a component and its template. We reviewed how to communicate values from the component to the template using binding and how to react to user's changes using the long form of two-way binding, getters and setters or the valueChanges observable. We examined how to use ViewChild or ViewChildren to get a reference to a native element so we can access its HTML properties or call its HTML methods, such as its focus method, or use ViewChild or ViewChildren to reference the NgForm or NgModel directive on an element and access its data structures. Those data structures provide state information, such as whether a form is dirty or a control is valid. There are additional techniques available when communicating between a parent component and a child component. Recall that a component is a child, or nested, component only if the component resides directly in the template of the parent component. We saw how a parent can pass a value to the child component using a property marked with the Input decorator. We examined how a parent component can get a reference to the child component to access its properties or perform an action by calling its methods. If a child component wants to notify the parent component of an action or pass it some data, use an EventEmitter property with the Output decorator and emit an event to send a notification to the parent component's template. Next, we discovered how services are a great intermediary for communication between components. We started with a simple property bag service that retained property values for a component to communicate with itself, though we could also use that technique to communicate with other components. We examined how a state management service can retrieve, manage, store, and share entity data. We then looked at techniques for sending notifications between components using a service with a Subject or a BehaviorSubject. Lastly, we examined how to use routing to pass a required value from one component to the next via the route and discussed how to use optional or query parameters to pass optional values on the route. Wow! We've covered a lot in this course. For your reference, here is a summary. First is communication between a component and its template, including techniques for notifying the component when the user makes a change in the view, then the additional mechanisms when communicating between a parent and child component. Here are the techniques used for the parent to communicate with the child, including ways for the child to react to changes made by the parent. And here is how a child component communicates with its parent. A service provides a great intermediary for a component to communicate with itself or other components. We looked at several ways for using a service. Lastly, we looked at how to use route parameters as an additional option when communicating between routed components. Want to know more? Let's look at some resources.
This course covered many techniques, but there is always more to learn. Here are some related Pluralsight courses. Play by Play Angular and ngrx introduces and demonstrates ngrx, the powerful state management library inspired by Redux. If you are new to forms, check out Angular Forms or Angular Reactive Forms. And for more information on routing, check out the Angular Routing course. It includes additional routing techniques, such as route resolvers and route guards.
Congratulations! You've made it through the key techniques for component communication. Yay! We covered lots of different techniques you can use as you build your own applications, and I'm sure you'll find more as you continue learning. I'd love to hear about them. Reach out to me on Twitter or on the discussion page for this course. Thanks for listening, and I hope you enjoyed component communication with Angular.
Deborah Kurata is a software developer, consultant, Pluralsight author, Google Developer Expert (GDE) and Microsoft Most Valuable Professional (MVP). Follow her on twitter: @deborahkurata
Released30 Jan 2018