- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Guided: Angular Components and Templates
Imagine your team has begun developing an e-commerce website that sells robot parts. The site is mostly functional, your team has stopped short of implementing some more advanced component features to solve some existing challenges. Your mission? To dive into the codebase and make strategic improvements to enhance the user experience—all while mastering essential concepts related to Angular component communication and templates. In this hands-on lab, you’ll work with a pre-existing application consisting of a catalog page and cart page. Along the way, you'll practice communicating between components with input and output properties, passing template content to be rendered by child components using content projection, utilizing control flow variables within a @for loop, and conditionally rendering content with @if and @else control flow blocks, reinforcing topics related building Angular components in a practical, real-world scenario. By the end of the lab, you will have the practical experience working with and communicating between components to help you start building your own dynamic web applications! Your robot parts store awaits its transformation.
Lab Info
Table of Contents
-
Challenge
Introduction
In this Angular guided lab, you’ll work with the Joe’s Robot Shop application. It’s already functional, but you’ll notice it has a few quirks.
To see its current state:
- Click the Run button in the Terminal tab in the bottom-right.
- Then click the Web Browser tab to the right, or open the app in a new browser tab using this link: {{localhost:4200}}.
You’ll see Catalog and Cart pages where you can purchase items and view them in your cart.
However, each product currently displays both Add and Remove buttons. The developers intended to use a single
ProductDetailscomponent that conditionally shows these buttons depending on whether the component is rendered in the Catalog or Cart page—but they didn’t finish implementing that logic.
What You'll Work On:
Your job in this lab will be to:
- Solve the issue with the buttons using
inputproperties and@if…@elseblocks, - Explore child-to-parent communication using
outputproperties and events by delegating the handling of theBuyandRemovebutton clicks to the parent components, - Experiment with solving this problem using Content Projection and slots instead of
inputandoutputproperties, and, - Use
@forloop control flow variables to apply alternating styles to the products
Solution Directory and Validation
As you work through each step, tasks can be validated by clicking on the Validate button. If you're stuck or would like to compare solutions, a
solutionfolder has been provided for you.
Before You Get Started
If you haven't already, click the Run button in the Terminal tab, then continue to the next step.
-
Challenge
Creating and Using Input Properties
The Catalog and Cart pages each use the
ProductDetailscomponent which currently displays bothAddandRemovebuttons on both pages.Update the
ProductDetailscomponent so that its parent component can tell it which "mode" to operate in (shop mode or cart mode).info> Reminder: You can create a signal-based input property with a default value on a component like this:
myProperty = input('value');. You can importinputfrom@angular/core. Nice work! Now use your newmodeinput property to show and hide the appropriateBuyorRemovebutton using@if...@elseblocks, based on the passed inmodevalue. Now that you have defined a mode property that controls the Add and Remove buttons, update theCatalogandCartcomponents to pass the appropriate values.info> Remember: You pass values to input properties using attributes. This can be a binding, for example,
<child-component [myProperty]="boundValue" />. Or, if just working with string values, you can just using a simple attribute value, for example,myProperty="value". -
Challenge
Creating Output Properties
Currently, the
ProductDetailscomponent handles what happens when the user clicks the Buy or Remove buttons. That approach works, but there are times when you want the parent component (such asCatalogorCart) to control what happens when these actions occur.Create two output properties named
addedToCartandremovedFromCarton theProductDetailscomponent which each output an event when the respective Add and Remove buttons are clicked. That will set you up to handle these events on their parent components.Keep in Mind
-
You can define output properties like this:
myEvent = output<MyType>();(
outputcan be imported from@angular/core) -
You emit events via the output properties like this:
this.myEvent.emit(); -
And you pass data/objects along with the event by passing the data as an argument:
this.myEvent.emit(myObject); ``` Great! You now have the `ProductDetails` component outputting events when the **Add** and **Remove** buttons are clicked.
Next, you can start handling those events on the parent components!
-
-
Challenge
Binding to Output Property Events
Your
ProductDetailscomponent no longer updates theCartServicewhen the buttons are clicked to add or remove products from the cart.Instead, it now emits an event that parent components can listen to to handle those events themselves.
Update the
CatalogandCartcomponents to listen to your newaddedToCartandremovedFromCartevents and call theCartService.addToCart()andCartService.removeFromCart()methods, respectively.info> Remember: You can bind a child-component's output event to a method in the parent component's class using an attribute binding like this:
[myEvent]="handleMyEvent($event)"In this example, passing$eventto thehandleMyEventmethod passes along any data emitted by themyEventoutput property. Nice! You're now using the parentCatalogcomponent to add items to the cart based on the event and event data emitted from theProductDetailscomponent.Now apply the same pattern in the
Cartcomponent to handle theremovedFromCartoutput event. -
Challenge
Using Content Projection
Taking a Different Approach
There's another way to handle the Add and Remove buttons in the
ProductDetailscomponent.Instead of passing in a flag that tells the component which buttons to render, you could pass the buttons themselves from the parent component. This makes
ProductDetailsmore reusable and extensible because the parent component can define any button or set of buttons it wants to display. This approach uses content projection to achieve this.Content Projection
Content projection is a mechanism that allows a parent component to pass and render its own markup inside a designated area of a child component’s template. The child component chooses where projected content will appear using an
<ng-content />element.For example, consider this template for a child component with the selector
<my-child-component />:<h1>Click here:</h1> <ng-content /> <div>For magic!</div>A parent component could render a button inside this child component, where the
<ng-content />tag is, with a template such as this:<my-child-component> <button>Click me!</button> </my-child-component>Notice that the content to be projected, or inserted, into the child component is rendered inside the opening and closing tags for the child component.
So, in this case, the parent component's "Click me!" button will be rendered between the
<h1>and<div>of the child component, since that's where the<ng-content />tag is.
Try It Out!
Right now the
ProductDetailscomponent renders either a Buy or Remove button based on itsmodeinput property.Instead of this approach, allow parent components to pass in the button to be rendered using content projection. ### Looking Good Excellent, you're now using content projection to allow the parent components to control what buttons are displayed on the
ProductDetailscomponent.Check it out in the browser!
-
Challenge
Using @for Loop Variables
Using
@forLoop VariablesAngular's
@forcontrol flow syntax allows you to access helpful loop variables, such as the index of each item, whether the item’s index is odd or even, or if it is the first or last item.Use the
$even@forloop variable to style even rows on the catalog page so that the rows have an alternating background style. If the current item index is even, apply abg-grayclass to the list item that gives it a gray background.Keep in Mind
You can access
@forloop variables by defining them in the@forloop like this:@for (item of items; track item.id; let i = $index; let even = $even) {You can optionally apply a CSS class based on an expression like this:
<li [class.bg-gray]="even">What You Learned
Congratulations, you've finished this lab and now have an improved Robot Shop application!
Throughout this lab you:
- Created and used input properties to pass data to child components
- Used
@if...@elseblocks to conditionally render content - Created and bound to output properties to handle events from child components
- Used content projection to pass content to child components, making them more extensible
- Used the
@forloop$evenvariable to style alternating rows inside a repeating list of elements
Keep Learning
Now check out the Pluralsight Angular: Services and Dependency Injection course to learn lots of great, and in-depth information about how to use services and dependency injection in your Angular applications!
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.