GIF Guide to Kendo UI for Angular_870x220

Interested in building a web application with Kendo UI for Angular? Learn how in this step-by-step GIF guide.

This GIF guide demonstrates the steps necessary to integrate Kendo UI for Angular into a web app. This is going to be a demo store app that we are building and each new GIF guide will walk you through a different Kendo UI Component. This particular guide walks you through the process of using the Button component as well as setting up a store app and adding products to a "cart." Let's dive in!

Getting Started: Setup

We are starting this GIF guide out with an already begun app. If you need a bit of help in creating your first app, we have a Getting Started Guide! It outlines the steps necessary for setting up your machine to use Kendo UI for Angular. It also provides step-by-step instructions on how to build your first app.

I went ahead and did some styling and created a header, so to follow along, you should clone the beginning seed of the project here.

Quick Note on Service Workers

I did start our project using the Service Worker and --style=scss flag (more on this in a later GIF guide):

ng new KUI-buttons --style=scss --service-worker
The --service-worker flag takes care of configuring your app to use service workers by adding the service-worker package along with setting up the necessary files to support service workers. For information on the details, see the following section which covers the process in detail as it shows you how to add a service worker manually to an existing app. — Angular.io Guide

Set View Encapsulation to None for Root Component

I also went ahead and set view encapsulation to none on our root component. This is going to allow us to import a styles variable file and all the children components of the root app.component will inherit these styles. Yay cascading styles! From app.component.ts:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class AppComponent {
  title = 'app';
}

Create the Variable Stylesheet

If you check out the app.component.sass, you will see that I created and imported a variable stylesheet. This is a place for us to store global style variables, like the ones already there. From app.component.sass:

$kendo-orange: #ff6358
$kendo-white: #f8f8f8
$kendo-light-grey: #ededed
$kendo-black: #4b4e52

Now that you have cloned the starter seed to this GIF guide, cd to that project in your terminal and npm install all the things. Then, let's run the project using ng serve. You should see this at http://localhost:4200/ in your browser:

Install the Kendo UI Default Theme

Now we are going to install the Kendo UI default theme:

And then we will include the theme in our styles.scss file!

@import '~@progress/kendo-theme-default/scss/all'

Generate the Shirt and Sticker Components

Now before we start using some Kendo UI components, let's go ahead and get our navigation working. We'll start by generating the two components we're missing; T-shirts and Stickers.

ng g c t-shirts
ng g c stickers

Create the Navigation Routes

Import the Angular Router Service into app.module.ts

import { RouterModule, Routes } from '@angular/router';

Create Routes

const appRoutes: Routes = [];

Configure Routes

Next, we need to configure our appRoutes with routerModule.forRoot(). This goes inside our app.module.ts imports array:

RouterModule.forRoot(
  appRoutes
)

Establish Route Paths

Now to create a couple of routes! Our stickers path needs to point to our StickersComponent:

const appRoutes: Routes = [
  { path: 'stickers', component: StickersComponent },
  { path: '', redirectTo: '/stickers', pathMatch: 'full' }
];

The empty path in the second route represents the default path for the application, the place to go when the path in the URL is empty, as it typically is at the start.

Create Route for Shirts Component

Remember to leave the most generic routes for last. Order does matter! In this case, we are leaving the empty route until the very end, for our "catch all" route:

Add Navigation in app.component.html

At the top, we'll add a routerLink attribute with the route for each <a> element:

<nav>
  <a routerLink="/t-shirts">T-Shirts</a>
  <a routerLink="/stickers">Stickers</a>
</nav>

Include the router-outlet at the bottom of our app.component.html:

<router-outlet></router-outlet>

Our routes are working now!

However, we don't have active styles applying to the links when each route in turn is selected. I've already added .active styles to the app.component.sass file:

a, a:focus, a:active
  color: $kendo-black
  text-decoration: none
  margin: 14px
  &:first-child
    margin-left: 0

a.active
  font-weight: bold
  cursor: default
  color: $kendo-orange

We just need to set a routerLinkActive attribute to the active <a> element:

<a routerLink="/t-shirts" routerLinkActive="active">T-Shirts</a>
<a routerLink="/stickers" routerLinkActive="active">Stickers</a>

This is going to add a class of .active to each <a> element when the routerLink route is selected.

Watch the magic happen:

Install the Button Component and Dependencies

Let's install the Button component so we can use it in our app. It's contained in the package, @progress/kendo-angular-buttons. It has a peer dependency for the Localization package, @progress/kendo-angular-l10n, which enables you to translate the components into different languages:

npm install --save @progress/kendo-angular-buttons @progress/kendo-angular-l10n

Import Button and Animation Component into app.module.ts

Animations are a dependency of our Button component. So, we'll need to include both!

import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { ButtonsModule } from "@progress/kendo-angular-buttons";

Be sure to add them to the imports array as well:

@NgModule({
  declarations: [
    AppComponent,
    TShirtsComponent,
    StickersComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    ButtonsModule,
    ...
  ],

I went ahead and populated the stickers template for us:

Include Kendo UI Buttons in Stickers Component

Now, let's add our buttons into the stickers componet. So each sticker for sale will have a button to add that sticker to the cart!

<section>
  <div class="product">
    <div class="product image">
      <img src="assets/images/stickers/angular_sticker.jpeg" />
    </div>
    <button kendoButton (click)="onButtonClick()" [primary]="true">Angular Sticker $5</button>
  </div>
  <div class="product">
    <div class="product image">
      <img src="assets/images/stickers/angularjs_sticker.jpeg" />
    </div>
    <button kendoButton (click)="onButtonClick()" [primary]="true">AngularJS Sticker $5</button>
  </div>
  <div class="product">
    <div class="product image">
      <img src="assets/images/stickers/nativescript_sticker.jpeg" />
    </div>
    <button kendoButton (click)="onButtonClick()" [primary]="true">NativeScript Sticker $5</button>
  </div>
  <div class="product">
    <div class="product image">
      <img src="assets/images/stickers/react_sticker.jpeg" />
    </div>
    <button kendoButton (click)="onButtonClick()" [primary]="true">React Sticker $5</button>
  </div>
  <div class="product">
    <div class="product image">
      <img src="assets/images/stickers/vuejs_sticker.jpeg" />
    </div>
    <button kendoButton (click)="onButtonClick()" [primary]="true">VueJS Sticker $5</button>
  </div>
</section>

Add Button Functionality

We need each of these buttons to add their product to the cart. The rest of our game plan will look something like this:

  • Generate Cart service
  • Import and Include Cart Service inside app.module.ts Provider Array
  • Create Product Class
  • Create CartItem Class

Generate Cart Service

We are going to need a cart service to give us access to our cart to add/remove items. To generate our cart service, I used the CLI command:

ng g s cart

Import and Include Cart Service inside app.module.ts provider array

import { CartService } from './cart.service';

...

providers: [
  CartService
]

Create Classes for product and cartItem

In order to add thing to our cart, we need to create a couple of classes, cartItems that will consist of products.

Create Product Class

We would like our products to consist of an ID, type, name, and price (in cents) in ./product.ts:

export class Product {
  id: number;
  type: string;
  name: string;
  price: number;
}

Create Cart Item Class

We want all of our cart items to have not only the product info (from above) but also the quantity and the size if applicable in ./cartItem.ts:

import { Product } from './product';

export class CartItem {
  product: Product;
  quantity: number;
  size?: string | null;
}

Populate Cart Service

Now, inside our cart service, we will import the cartItem and product classes in cart.service.ts:

import { Injectable } from '@angular/core';
import { CartItem } from './cartItem';
import { Product } from './product';

@Injectable()

Then, we'll create a hard coded productList for now, with all the stickers:

export class CartService {

  // hard coded data, FOR NOW! MUHAHA
  productList: Product[] = [
    {
      id: 0,
      type: 'sticker',
      name: 'Angular Sticker',
      price: 500
    },
    {
      id: 1,
      type: 'sticker',
      name: 'AngularJS Sticker',
      price: 500
    },
    {
      id: 2,
      type: 'sticker',
      name: 'NativeScript Sticker',
      price: 500
    },
    {
      id: 3,
      type: 'sticker',
      name: 'React Sticker',
      price: 500
    },
    {
      id: 4,
      type: 'sticker',
      name: 'VueJS Sticker',
      price: 500
    }
  ];

Next, we need to create a cart that is an array of cartItem objects:

cart: CartItem[] = [];
constructor() {}

Now for the fun part! We need three functions, one to return the products in the cart (getCart()), one to return all the available products (getProducts()) and one to add items into our cart for shopping fun (addToCart)! Here, we could import and use Observable and of from RxJS, but for now I chose to keep it simple:

// Could use Observables if we wanted
// getCart(): Observable<CartItem[]> {
//   return of(this.cart);
// }
//
// getProducts(): Observable<Product[]> {
//   return of(this.productList);
// }

getCart(): CartItem[] {
  return this.cart;
}

getProducts(): Product[] {
  return this.productList;
}

Our addToCart() method needs to be a bit more complex, so let's break it down. We could do something like this:

addToCart(productId): void {
  let item = this.productList.find( (product)=>{
    return product.id == productId;
  });

  let cartItem: CartItem = {
    product: item,
    quantity: 1
  };

  this.cart.push(cartItem);
  console.log('CART:', this.cart);
}

In this implementation, we take the productId passed in and set item to the product with a matching id. Then, we take that item and put it into a cartItem with a default quantity of 1. Then simply push the cartItem into the cart. This works of course, but isn't super flexible. If the shopper chooses to buy two of the same sticker, this way would push that same sticker into the cart twice instead of simply updating the quantity of the first sticker. What we'd rather have happen is first check if that product exists in the cart, if it does update the quantity, else push the new product into the cart.

addToCart(productId): void {
  let item = this.productList.find( (product)=>{
    return product.id == productId;
  });

  let cartItem: CartItem = {
    product: item,
    quantity: 1
  };

  for (let thingInCart of this.cart) {
    if (thingInCart.product.id == item.id) {
      thingInCart.quantity++;
      console.log('CART:', this.cart);
      return;
    }
  };

  this.cart.push(cartItem);
  console.log('CART:', this.cart);
}

Now that all this cool cart functionality has been created, we can go into our stickers component and use it! For a quick test, let's connect each of the buttons (again, hard coded, I know) and call an addToCart() method that we need to create in the stickers component. We'll pass in a product ID for each product.

<button kendoButton (click)="addToCart(0)" [primary]="true">Angular Sticker $5</button>

So each of our buttons will have this nifty call on click (click)="addToCart(0)".

Finish addToCart Functionality in Stickers Component

Let's create the addToCart functionality inside our stickers.component.ts by importing the CartService in stickers.component.ts:

import { Component, OnInit } from '@angular/core';
import { CartService } from '../cart.service';

@Component({
  selector: 'app-stickers',
  templateUrl: './stickers.component.html',
  styleUrls: ['./stickers.component.sass']
})

Then, we'll go ahead and inject our cartService in the constructor params. We need to do it here, because there are methods on the cartService which we'd like to use:

export class StickersComponent implements OnInit {
  constructor(private cartService: CartService) {}

  ngOnInit() {}
}

Create addToCart Function

This function will pass the productId to the Service and let it handle all the logic:

export class StickersComponent implements OnInit {
  constructor(private cartService: CartService) {}

  addToCart(productId): void {
    this.cartService.addToCart(productId);
  }

  ngOnInit() {}
}

Cart is Populated

Now when we click the stickers buttons, each sticker is added to the cart!

And if we selected the same sticker multiple times, we see that it just updates the quantity for that product in the cart!

We have much more to do in the way of cleaning up, but for now we will leave that for the next GIF guide! We hope you have enjoyed this first one and look forward to publishing more that will build on where we left off. Happy coding!


AlyssaNicoll
About the Author

Alyssa Nicoll

Alyssa is an Angular Developer Advocate & GDE. Her two degrees (Web Design & Development and Psychology) feed her speaking career. She has spoken at over 20 conferences internationally, specializing in motivational soft talks, enjoys gaming and scuba diving, and has a toothless dog named Gummy. Her DM is always open, come talk sometime.

Related Posts

Comments

Comments are disabled in preview mode.