Featured resource
2026 Tech Forecast
2026 Tech Forecast

1,500+ tech insiders, business leaders, and Pluralsight Authors share their predictions on what’s shifting fastest and how to stay ahead.

Download the forecast
  • Lab
    • Libraries: If you want this lab, consider one of these libraries.
    • Core Tech
Labs

Angular Shop Reactive API Data

You are building 'Globomantics Shop', a lightweight Angular storefront that displays products fetched from a development JSON API. Customers can browse a product list, add items to a cart, and view their cart on a separate page. The application must be zoneless, use standalone components, manage cart state through an injectable service, and proxy API calls through the Angular dev server to avoid CORS errors during development.

Lab platform
Lab Info
Level
Beginner
Last updated
Oct 23, 2026
Duration
45m

Contact sales

By clicking submit, you agree to our Privacy Policy and Terms of Use, and consent to receive marketing emails from Pluralsight.
Table of Contents
  1. Challenge

    Step 1: Tour the Globomantics Shop workspace

    Globomantics Shop: a zoneless Angular storefront

    You are joining the build of Globomantics Shop, a small storefront that lists products, lets a shopper add them to a cart, and shows that cart on its own page. The workspace already has a working shell — nothing renders yet, but the wiring that boots Angular is in place, and every file you will touch already exists with a // TODO marking the exact line you own.

    By the end of this lab the shop will: model products and cart lines with plain TypeScript interfaces, boot with zoneless change detection instead of zone.js, render a reusable product card built from signal-based input()/output(), share cart state through an injectable service, route between a product list and a cart page, and fetch its product catalog from a proxied JSON API with HttpClient. ## The entry point: src/main.ts and src/App.ts

    src/main.ts is owned by the toolchain — you will not edit it — and it does exactly one thing: it calls bootstrapApplication(App, appConfig), pulling in the root component from src/App.ts and the provider list from src/app.config.ts. Angular 22 has no @NgModule and no BrowserModule; a standalone component simply lists what it needs in its own imports: [], and the root providers — including provideZonelessChangeDetection() — live in one config object instead of a module tree.

    src/App.ts currently renders a placeholder. You will give it a signal that reports whether the app has finished booting, which is the first piece of observable behaviour you will make it produce. ## Where the rest of the shop will live

    As you move through the steps, you will fill in files already scaffolded under src/: models/product.ts for the Product and CartItem shapes, product-card/ProductCard.ts for the reusable card, cart/CartService.ts for shared cart state, pages/ProductListPage.ts and pages/CartPage.ts for the two screens, app.routes.ts for the router configuration, and services/ProductService.ts for the HTTP call that fetches real product data. Each file already compiles — the // TODO comments mark the single behaviour you add next.

    info> If you get stuck, you can refer to the provided solution code for each task, available in the solution folder.

  2. Challenge

    Step 2: Model product data and bootstrap a zoneless app

    Modelling the data first

    Every other part of the shop — the card, the cart, the API call — passes around the same two shapes: a Product and a CartItem. src/models/product.ts already declares both interfaces; what is missing is a small factory function, createCartItem, that turns a product and a quantity into a properly-shaped CartItem. Building it now means CartService can reuse it later instead of assembling the object by hand in more than one place. ## Turning on zoneless change detection

    Angular's classic change detection patches browser APIs through zone.js to know when to re-render. Globomantics Shop skips that entirely: provideZonelessChangeDetection() from @angular/core tells Angular to rely on signals instead, and it is registered once, in the provider list bootstrapApplication reads from src/app.config.ts. That file already has the shape of the config in place — coreProviders is the one function you need to fill in. ## Proving the shell boots

    With the providers registered, the last piece of Step 2 is to make the root component itself observable: src/App.ts holds a bootPhase signal and a statusMessage computed from it, but nothing ever moves bootPhase out of 'booting'. That is the job of markReady.

  3. Challenge

    Step 3: Build the reusable ProductCard component

    A card that takes its data as an input

    ProductCard is the one component every product on the page will render through, so it takes a Product via input.required<Product>() rather than reaching out for one itself — that keeps it reusable anywhere a product needs showing. src/product-card/ProductCard.ts already declares that input and a formattedPrice computed signal meant to turn its raw price number into a display string; you just need to fill in the computation. ## Telling the parent about a click

    A card should not know what "adding to cart" means for the rest of the app — it just needs to announce that its button was clicked. That is what the addToCart output, declared with output<Product>(), is for: the parent component that renders the card decides what to do with the emitted product. The template already wires the button's click to emitAddToCart; you complete what that method does. ## Noticing when a card mounts

    ngOnInit runs once, right after Angular has resolved a component's inputs, which makes it the right place to do anything that depends on the data already being there — logging which product just rendered, for instance. ProductCard declares a rendered signal for exactly this, still left at false.

  4. Challenge

    Step 4: Render the product list and wire cart state to a service

    One shared cart, not one per page

    Both the product list and the cart page need to see the same cart, so it lives in an injectable, CartService, rather than inside either page component. src/cart/CartService.ts already exposes a read-only items signal and a total computed from it; addItem is the one gap, and it has to replace the array on every change rather than mutate it, since signals only notify readers when they receive a new reference. ## Rendering the list, one card per product

    ProductListPage renders one ProductCard per entry in its products signal with @for (product of products(); track product.id), and binds each card's (addToCart) output to a single handler. That handler, handleAddToCart, is the only place the page needs to touch CartService — the card itself never has to know the service exists. ## Showing the cart, or saying it is empty

    CartPage reads the same items and total straight from CartService, so any change made on the list page appears here too. Its template uses @if (items().length === 0) to switch between an empty-state message and the list of lines — emptyMessage is the computed signal that decides what that message says.

  5. Challenge

    Step 5: Route pages and fetch products from the proxied API

    Giving the shop more than one page

    Up to now every page has existed in isolation; provideRouter() is what turns ProductListPage and CartPage into destinations a shopper can navigate between. The route table itself lives in src/app.routes.ts, in a function, buildRoutes, that the exported routes constant below it calls once and hands to provideRouter. ## Fetching real products over HTTP

    So far products has started out empty. src/services/ProductService.ts injects HttpClient — imported from @angular/common/http, provided by @angular/common — so it can ask the Angular dev-server's proxy for /api/products instead of hard-coding data. The workspace already includes that proxy (proxy.conf.mjs) and a small mock catalog API (mock-api.mjs) that starts together with the dev server, so the only code you write is the call itself. loadProducts is the one method that needs to make that call and hand back the resulting observable. ## Wiring the fetch into the page

    ProductListPage's ngOnInit already calls loadProductList as soon as the page mounts; the method itself still needs to ask ProductService for data and put whatever comes back into the products signal so the @for loop has something real to render.

  6. Challenge

    Step 6: Run the application

    Every task is done, and each one was graded the moment you completed it. What you have not done yet is watch the whole application run at once -- which is the only place the pieces you wrote separately become a single working program.

    Start the application

    Open the Terminal tab and start the dev server:

    npm run dev
    

    It serves the application on port 4200. Switch to the Web Browser tab to use it: the page is running the functions you implemented in the previous steps, so what you see is your own code rather than a demonstration of it.

    The Products page now lists the catalog from the mock API that npm run dev starts alongside the dev server, so you can add items and open the cart to see the whole shop working.

    Leave the server running while you look around. It reloads on save, so you can change a file and see the result without restarting anything. ### Run the checks yourself

    The lab ran each task's test for you as you went, but they are ordinary tests and you can run them whenever you like. The whole suite:

    npx --no-install ng test --no-watch --reporters=dot
    

    Or one task at a time:

    bash runTest.sh "task 2.1"
    

    Each failure names the task it belongs to, so the output tells you which step to go back to rather than which line to stare at. Two things worth trying before you finish: break something on purpose and watch the matching check fail, then put it back. Knowing what a failure looks like is worth as much as knowing what a pass looks like, and it costs you nothing here.

    When you are done, everything you wrote is still in the editor tabs -- the lab is yours to keep reading.

About the author

Pluralsight’s AI authoring technology is designed to accelerate the creation of hands-on, technical learning experiences. Serving as a first-pass content generator, it produces structured lab drafts aligned to learning objectives defined by Pluralsight’s Curriculum team. Each lab is then enhanced by our Content team, who configure the environments, refine instructions, and conduct rigorous technical and quality reviews. The result is a collaboration between artificial intelligence and human expertise, where AI supports scale and efficiency, and Pluralsight experts ensure accuracy, relevance, and instructional quality, helping learners build practical skills with confidence.

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.

Get started with Pluralsight