|
| 1 | +# Try it: Manage data |
| 2 | + |
| 3 | +At the end of [In-app Navigation](start/start-routing "Try it: In-app Navigation"), the online store application has a product catalog with two views: a product list and product details. |
| 4 | +Users can click on a product name from the list to see details in a new view, with a distinct URL, or route. |
| 5 | + |
| 6 | +This page guides you through creating the shopping cart in three phases: |
| 7 | + |
| 8 | +* Update the product details view to include a "Buy" button, which adds the current product to a list of products that a cart service manages. |
| 9 | +* Add a cart component, which displays the items in the cart. |
| 10 | +* Add a shipping component, which retrieves shipping prices for the items in the cart by using Angular's `HttpClient` to retrieve shipping data from a `.json` file. |
| 11 | + |
| 12 | +{@a services} |
| 13 | +## Services |
| 14 | + |
| 15 | +Services are an integral part of Angular applications. In Angular, a service is an instance of a class that you can make available to any part of your application using Angular's [dependency injection system](guide/glossary#dependency-injection "Dependency injection definition"). |
| 16 | + |
| 17 | +Services are the place where you share data between parts of your application. For the online store, the cart service is where you store your cart data and methods. |
| 18 | + |
| 19 | +{@a create-cart-service} |
| 20 | +## Create the shopping cart service |
| 21 | + |
| 22 | +Up to this point, users can view product information, and |
| 23 | +simulate sharing and being notified about product changes. |
| 24 | +They cannot, however, buy products. |
| 25 | + |
| 26 | +In this section, you add a "Buy" button to the product |
| 27 | +details view and set up a cart service to store information |
| 28 | +about products in the cart. |
| 29 | + |
| 30 | +<div class="alert is-helpful"> |
| 31 | + |
| 32 | +A later part of this tutorial, [Use forms for user input](start/start-forms "Try it: Forms for user input"), guides you through accessing this cart service from the view where the user checks out. |
| 33 | + |
| 34 | +</div> |
| 35 | + |
| 36 | +{@a generate-cart-service} |
| 37 | +### Define a cart service |
| 38 | + |
| 39 | +1. To generate a cart service, right click on the `app` folder, choose `Angular Generator`, and choose `Service`. Name the new service `cart`. |
| 40 | + |
| 41 | + <code-example header="src/app/cart.service.ts" path="getting-started/src/app/cart.service.1.ts"></code-example> |
| 42 | + |
| 43 | + <div class="alert is-helpful> |
| 44 | + |
| 45 | + The StackBlitz generator might provide the cart service in `app.module.ts` by default. That differs from the example, which uses a bundle-optimization technique, an `@Injectable()` decorator with the `{ providedIn: 'root' }` statement. |
| 46 | + For more information about services, see [Introduction to Services and Dependency Injection](guide/architecture-services "Concepts > Intro to Services and DI"). |
| 47 | + |
| 48 | + </div> |
| 49 | + |
| 50 | +1. In the `CartService` class, define an `items` property to store the array of the current products in the cart. |
| 51 | + |
| 52 | + <code-example path="getting-started/src/app/cart.service.ts" header="src/app/cart.service.ts" region="props"></code-example> |
| 53 | + |
| 54 | +1. Define methods to add items to the cart, return cart items, and clear the cart items: |
| 55 | + |
| 56 | + <code-example path="getting-started/src/app/cart.service.ts" header="src/app/cart.service.ts" region="methods"></code-example> |
| 57 | + |
| 58 | + * The `addToCart()` method appends a product to an array of `items`. |
| 59 | + |
| 60 | + * The `getItems()` method collects the items users add to the cart and returns each item with its associated quantity. |
| 61 | + |
| 62 | + * The `clearCart()` method returns an empty array of items. |
| 63 | + |
| 64 | +{@a product-details-use-cart-service} |
| 65 | +### Use the cart service |
| 66 | + |
| 67 | +This section walks you through using the cart service to add a product to the cart with a "Buy" button. |
| 68 | + |
| 69 | +1. Open `product-details.component.ts`. |
| 70 | + |
| 71 | +1. Configure the component to use the cart service. |
| 72 | + |
| 73 | + 1. Import the cart service. |
| 74 | + |
| 75 | + <code-example header="src/app/product-details/product-details.component.ts" path="getting-started/src/app/product-details/product-details.component.ts" region="cart-service"> |
| 76 | + </code-example> |
| 77 | + |
| 78 | + 1. Inject the cart service by adding it to the `constructor()`. |
| 79 | + |
| 80 | + <code-example path="getting-started/src/app/product-details/product-details.component.ts" header="src/app/product-details/product-details.component.ts" region="inject-cart-service"> |
| 81 | + </code-example> |
| 82 | + |
| 83 | + <!-- |
| 84 | + To do: Consider defining "inject" and describing the concept of "dependency injection" |
| 85 | + --> |
| 86 | + |
| 87 | +1. Define the `addToCart()` method, which adds the current product to the cart. |
| 88 | + |
| 89 | + The `addToCart()` method does the following three things: |
| 90 | + * Receives the current `product`. |
| 91 | + * Uses the cart service's `addToCart()` method to add the product the cart. |
| 92 | + * Displays a message that you've added a product to the cart. |
| 93 | + |
| 94 | + <code-example path="getting-started/src/app/product-details/product-details.component.ts" header="src/app/product-details/product-details.component.ts" region="add-to-cart"></code-example> |
| 95 | + |
| 96 | +1. Update the product details template with a "Buy" button that adds the current product to the cart. |
| 97 | + |
| 98 | + 1. Open `product-details.component.html`. |
| 99 | + |
| 100 | + 1. Add a button with the label "Buy", and bind the `click()` event to the `addToCart()` method: |
| 101 | + |
| 102 | + <code-example header="src/app/product-details/product-details.component.html" path="getting-started/src/app/product-details/product-details.component.html"> |
| 103 | + </code-example> |
| 104 | + |
| 105 | + <div class="alert is-helpful"> |
| 106 | + |
| 107 | + The line, `<h4>{{ product.price | currency }}</h4>` uses the `currency` pipe to transform `product.price` from a number to a currency string. A pipe is a way you can transform data in your HTML template. For more information about Angular pipes, see [Pipes](guide/pipes "Pipes"). |
| 108 | + |
| 109 | + </div> |
| 110 | + |
| 111 | +1. To see the new "Buy" button, refresh the application and click on a product's name to display its details. |
| 112 | + |
| 113 | + <div class="lightbox"> |
| 114 | + <img src='generated/images/guide/start/product-details-buy.png' alt="Display details for selected product with a Buy button"> |
| 115 | + </div> |
| 116 | + |
| 117 | + 1. Click the "Buy" button to add the product to the stored list of items in the cart and display a confirmation message. |
| 118 | + |
| 119 | + <div class="lightbox"> |
| 120 | + <img src='generated/images/guide/start/buy-alert.png' alt="Display details for selected product with a Buy button"> |
| 121 | + </div> |
| 122 | + |
| 123 | + |
| 124 | +## Create the cart view |
| 125 | + |
| 126 | +At this point, users can put items in the cart by clicking "Buy", but they can't yet see their cart. |
| 127 | + |
| 128 | +Create the cart view in two steps: |
| 129 | + |
| 130 | +1. Create a cart component and configure routing to the new component. At this point, the cart view has only default text. |
| 131 | +1. Display the cart items. |
| 132 | + |
| 133 | +### Set up the component |
| 134 | + |
| 135 | + To create the cart view, begin by following the same steps you did to create the product details component and configure routing for the new component. |
| 136 | + |
| 137 | +1. Generate a cart component, named `cart`. |
| 138 | + |
| 139 | + Reminder: In the file list, right-click the `app` folder, choose `Angular Generator` and `Component`. |
| 140 | + |
| 141 | + <code-example header="src/app/cart/cart.component.ts" path="getting-started/src/app/cart/cart.component.1.ts"></code-example> |
| 142 | + |
| 143 | +1. Add routing (a URL pattern) for the cart component. |
| 144 | + |
| 145 | + Open `app.module.ts` and add a route for the component `CartComponent`, with a `path` of `cart`: |
| 146 | + |
| 147 | + <code-example header="src/app/app.module.ts" path="getting-started/src/app/app.module.ts" region="cart-route"> |
| 148 | + </code-example> |
| 149 | + |
| 150 | +1. Update the "Checkout" button so that it routes to the `/cart` url. |
| 151 | + |
| 152 | + Open `top-bar.component.html` and add a `routerLink` directive pointing to `/cart`. |
| 153 | + |
| 154 | + <code-example |
| 155 | + header="src/app/top-bar/top-bar.component.html" |
| 156 | + path="getting-started/src/app/top-bar/top-bar.component.html" |
| 157 | + region="cart-route"> |
| 158 | + </code-example> |
| 159 | + |
| 160 | +1. To see the new cart component, click the "Checkout" button. You can see the "cart works!" default text, and the URL has the pattern `https://getting-started.stackblitz.io/cart`, where `getting-started.stackblitz.io` may be different for your StackBlitz project. |
| 161 | + |
| 162 | + <div class="lightbox"> |
| 163 | + <img src='generated/images/guide/start/cart-works.png' alt="Display cart view before customizing"> |
| 164 | + </div> |
| 165 | + |
| 166 | +### Display the cart items |
| 167 | + |
| 168 | +You can use services to share data across components: |
| 169 | + |
| 170 | +* The product details component already uses the cart service to add products to the cart. |
| 171 | +* This section shows you how to use the cart service to display the products in the cart. |
| 172 | + |
| 173 | + |
| 174 | +1. Open `cart.component.ts`. |
| 175 | + |
| 176 | +1. Configure the component to use the cart service. |
| 177 | + |
| 178 | + 1. Import the `CartService` from the `cart.service.ts` file. |
| 179 | + |
| 180 | + <code-example header="src/app/cart/cart.component.ts" path="getting-started/src/app/cart/cart.component.2.ts" region="imports"> |
| 181 | + </code-example> |
| 182 | + |
| 183 | + 1. Inject the `CartService` so that the cart component can use it. |
| 184 | + |
| 185 | + <code-example path="getting-started/src/app/cart/cart.component.2.ts" header="src/app/cart/cart.component.ts" region="inject-cart"> |
| 186 | + </code-example> |
| 187 | + |
| 188 | +1. Define the `items` property to store the products in the cart. |
| 189 | + |
| 190 | + <code-example path="getting-started/src/app/cart/cart.component.2.ts" header="src/app/cart/cart.component.ts" region="items"> |
| 191 | + </code-example> |
| 192 | + |
| 193 | +1. Set the items using the cart service's `getItems()` method. Recall that you defined this method [when you generated `cart.service.ts`](#generate-cart-service). |
| 194 | + |
| 195 | + The resulting `CartComponent` class is as follows: |
| 196 | + |
| 197 | + <code-example path="getting-started/src/app/cart/cart.component.3.ts" header="src/app/cart/cart.component.ts" region="props-services"> |
| 198 | + </code-example> |
| 199 | + |
| 200 | +1. Update the template with a header, and use a `<div>` with an `*ngFor` to display each of the cart items with its name and price. |
| 201 | + |
| 202 | + The resulting `CartComponent` template is as follows: |
| 203 | + |
| 204 | + <code-example header="src/app/cart/cart.component.html" path="getting-started/src/app/cart/cart.component.2.html" region="prices"> |
| 205 | + </code-example> |
| 206 | + |
| 207 | +1. Test your cart component. |
| 208 | + |
| 209 | + 1. Click on "My Store" to go to the product list view. |
| 210 | + 1. Click on a product name to display its details. |
| 211 | + 1. Click "Buy" to add the product to the cart. |
| 212 | + 1. Click "Checkout" to see the cart. |
| 213 | + 1. To add another product, click "My Store" to return to the product list. |
| 214 | + |
| 215 | + Repeat to add more items to the cart. |
| 216 | + |
| 217 | + <div class="lightbox"> |
| 218 | + <img src='generated/images/guide/start/cart-page-full.png' alt="Cart view with products added"> |
| 219 | + </div> |
| 220 | + |
| 221 | + |
| 222 | +<div class="alert is-helpful"> |
| 223 | + |
| 224 | +StackBlitz tip: Any time the preview refreshes, the cart is cleared. If you make changes to the app, the page refreshes, so you'll need to buy products again to populate the cart. |
| 225 | + |
| 226 | +</div> |
| 227 | + |
| 228 | +<div class="alert is-helpful"> |
| 229 | + |
| 230 | +For more information about services, see [Introduction to Services and Dependency Injection](guide/architecture-services "Concepts > Intro to Services and DI"). |
| 231 | + |
| 232 | +</div> |
| 233 | + |
| 234 | + |
| 235 | +## Retrieve shipping prices |
| 236 | +<!-- Accessing data with the HTTP client --> |
| 237 | + |
| 238 | +Servers often return data in the form of a stream. |
| 239 | +Streams are useful because they make it easy to transform the returned data and make modifications to the way you request that data. |
| 240 | +The Angular HTTP client, `HttpClient`, is a built-in way to fetch data from external APIs and provide them to your app as a stream. |
| 241 | + |
| 242 | +This section shows you how to use the HTTP client to retrieve shipping prices from an external file. |
| 243 | + |
| 244 | +### Predefined shipping data |
| 245 | + |
| 246 | +The application that StackBlitz generates for this guide comes with predefined shipping data in `assets/shipping.json`. |
| 247 | +Use this data to add shipping prices for items in the cart. |
| 248 | + |
| 249 | +<code-example header="src/assets/shipping.json" path="getting-started/src/assets/shipping.json"> |
| 250 | +</code-example> |
| 251 | + |
| 252 | + |
| 253 | +### Use `HttpClient` in the `AppModule` |
| 254 | + |
| 255 | +Before you can use Angular's HTTP client, you must configure your app to use `HttpClientModule`. |
| 256 | + |
| 257 | +Angular's `HttpClientModule` registers the providers your app needs to use a single instance of the `HttpClient` service throughout your app. |
| 258 | + |
| 259 | +1. Open `app.module.ts`. |
| 260 | + |
| 261 | + This file contains imports and functionality that is available to the entire app. |
| 262 | + |
| 263 | +1. Import `HttpClientModule` from the `@angular/common/http` package at the top of the file with the other imports. As there are a number of other imports, this code snippet omits them for brevity. Be sure to leave the existing imports in place. |
| 264 | + |
| 265 | + <code-example header="src/app/app.module.ts" path="getting-started/src/app/app.module.ts" region="http-client-module-import"> |
| 266 | + </code-example> |
| 267 | + |
| 268 | +1. Add `HttpClientModule` to the `AppModule` `@NgModule()` `imports` array to register Angular's `HttpClient` providers globally. |
| 269 | + |
| 270 | + <code-example path="getting-started/src/app/app.module.ts" header="src/app/app.module.ts" region="http-client-module"> |
| 271 | + </code-example> |
| 272 | + |
| 273 | +### Use `HttpClient` in the cart service |
| 274 | + |
| 275 | +Now that the `AppModule` imports the `HttpClientModule`, the next step is to inject the `HttpClient` service into your service so your app can fetch data and interact with external APIs and resources. |
| 276 | + |
| 277 | + |
| 278 | +1. Open `cart.service.ts`. |
| 279 | + |
| 280 | +1. Import `HttpClient` from the `@angular/common/http` package. |
| 281 | + |
| 282 | + <code-example header="src/app/cart.service.ts" path="getting-started/src/app/cart.service.ts" region="import-http"> |
| 283 | + </code-example> |
| 284 | + |
| 285 | +1. Inject `HttpClient` into the `CartService` constructor: |
| 286 | + |
| 287 | + <code-example path="getting-started/src/app/cart.service.ts" header="src/app/cart.service.ts" region="inject-http"> |
| 288 | + </code-example> |
| 289 | + |
| 290 | + |
| 291 | +### Define the `get()` method |
| 292 | + |
| 293 | +Multiple components can leverage the same service. |
| 294 | +Later in this tutorial, the shipping component uses the cart service to retrieve shipping data via HTTP from the `shipping.json` file. |
| 295 | +First, define a `get()` method. |
| 296 | + |
| 297 | +1. Continue working in `cart.service.ts`. |
| 298 | + |
| 299 | +1. Below the `clearCart()` method, define a new `getShippingPrices()` method that uses the `HttpClient` `get()` method to retrieve the shipping data. |
| 300 | + |
| 301 | + <code-example header="src/app/cart.service.ts" path="getting-started/src/app/cart.service.ts" region="get-shipping"></code-example> |
| 302 | + |
| 303 | + |
| 304 | +<div class="alert is-helpful"> |
| 305 | + |
| 306 | +For more information about Angular's `HttpClient`, see the [Client-Server Interaction](guide/http "Server interaction through HTTP") guide. |
| 307 | + |
| 308 | +</div> |
| 309 | + |
| 310 | +## Define the shipping view |
| 311 | + |
| 312 | +Now that your app can retrieve shipping data, create a shipping component and template. |
| 313 | + |
| 314 | +1. Generate a new component named `shipping`. |
| 315 | + |
| 316 | + Reminder: In the file list, right-click the `app` folder, choose `Angular Generator` and `Component`. |
| 317 | + |
| 318 | + <code-example header="src/app/shipping/shipping.component.ts" path="getting-started/src/app/shipping/shipping.component.1.ts"></code-example> |
| 319 | + |
| 320 | +1. In `app.module.ts`, add a route for shipping. Specify a `path` of `shipping` and a component of `ShippingComponent`. |
| 321 | + |
| 322 | + <code-example header="src/app/app.module.ts" path="getting-started/src/app/app.module.ts" region="shipping-route"></code-example> |
| 323 | + |
| 324 | + There's no link to the new shipping component yet, but you can see its template in the preview pane by entering the URL its route specifies. The URL has the pattern: `https://getting-started.stackblitz.io/shipping` where the `getting-started.stackblitz.io` part may be different for your StackBlitz project. |
| 325 | + |
| 326 | +1. Modify the shipping component so that it uses the cart service to retrieve shipping data via HTTP from the `shipping.json` file. |
| 327 | + |
| 328 | + 1. Import the cart service. |
| 329 | + |
| 330 | + <code-example header="src/app/shipping/shipping.component.ts" path="getting-started/src/app/shipping/shipping.component.ts" region="imports"></code-example> |
| 331 | + |
| 332 | + 1. Define a `shippingCosts` property. |
| 333 | + |
| 334 | + <code-example path="getting-started/src/app/shipping/shipping.component.ts" header="src/app/shipping/shipping.component.ts" region="props"></code-example> |
| 335 | + |
| 336 | + 1. Inject the cart service in the `ShippingComponent` constructor: |
| 337 | + |
| 338 | + <code-example path="getting-started/src/app/shipping/shipping.component.ts" header="src/app/shipping/shipping.component.ts" region="inject-cart-service"></code-example> |
| 339 | + |
| 340 | + 1. Set the `shippingCosts` property using the `getShippingPrices()` method from the cart service. |
| 341 | + |
| 342 | + <code-example path="getting-started/src/app/shipping/shipping.component.ts" header="src/app/shipping/shipping.component.ts" region="ctor"></code-example> |
| 343 | + |
| 344 | +1. Update the shipping component's template to display the shipping types and prices using the `async` pipe: |
| 345 | + |
| 346 | + <code-example header="src/app/shipping/shipping.component.html" path="getting-started/src/app/shipping/shipping.component.html"></code-example> |
| 347 | + |
| 348 | + The `async` pipe returns the latest value from a stream of data and continues to do so for the life of a given component. When Angular destroys that component, the `async` pipe automatically stops. For detailed information about the `async` pipe, see the [AsyncPipe API documentation](/api/common/AsyncPipe). |
| 349 | + |
| 350 | +1. Add a link from the cart view to the shipping view: |
| 351 | + |
| 352 | + <code-example header="src/app/cart/cart.component.html" path="getting-started/src/app/cart/cart.component.2.html"></code-example> |
| 353 | + |
| 354 | +1. Test your shipping prices feature: |
| 355 | + |
| 356 | + Click the "Checkout" button to see the updated cart. Remember that changing the app causes the preview to refresh, which empties the cart. |
| 357 | + |
| 358 | + <div class="lightbox"> |
| 359 | + <img src='generated/images/guide/start/cart-empty-with-shipping-prices.png' alt="Cart with link to shipping prices"> |
| 360 | + </div> |
| 361 | + |
| 362 | + Click on the link to navigate to the shipping prices. |
| 363 | + |
| 364 | + <div class="lightbox"> |
| 365 | + <img src='generated/images/guide/start/shipping-prices.png' alt="Display shipping prices"> |
| 366 | + </div> |
| 367 | + |
| 368 | + |
| 369 | +## Next steps |
| 370 | + |
| 371 | +Congratulations! You have an online store application with a product catalog and shopping cart. You can also look up and display shipping prices. |
| 372 | + |
| 373 | +To continue exploring Angular, choose either of the following options: |
| 374 | +* [Continue to the "Forms" section](start/start-forms "Try it: Forms for User Input") to finish the app by adding the shopping cart view and a checkout form. |
| 375 | +* [Skip ahead to the "Deployment" section](start/start-deployment "Try it: Deployment") to move to local development, or deploy your app to Firebase or your own server. |
0 commit comments