Angular - Routes and Forms

Angular - Routes and Forms

In this course Angular - Routes and Forms,you will learn to

  • manage dependencies of your class through Dependency Injection talk to servers through APIs
  • build Single Page Apps using routers
  • take user inputs through forms
  • By the end of this course, you should be in a position to create your Single Page Application and interact with users and servers through forms and APIs.

What Is Dependency Injection?

  • When Object 'X' needs Object 'Y' to run, then Y is dependency of X or we can say in simple terms X is dependent on Y.
  • Here in Angular, dependencies are defined as Services, which will be injected into those Objects which asks for it.

Note: Services / Dependencies are always defined inside "constructor" of the class.
Let us consider the following example:

export class SmartPhone {
    constructor (bat: Battery, disp: Display){}
}

Here SmartPhone is dependent on Services, that supply power(Battery) and takes input and shows output(Display), for its creation. So SmartPhone dependencies are: Battery and Display.

DI as a design pattern
DI is a coding pattern in which a class receives its dependencies from the external source rather that creating them itself.

Without DI
class Car{
   engine;
   tires;
   constrcutor(){
     this.engine = new Engine();
     this.tires = new Tires();
   }
}
With DI

class Car{
   engine;
   tires;
   constructor(engine, tires){
       this.engine = engine;
       this.tires = tires;
   }
}

DI  as a design pattern contd.
var myEngine = new Engine();
var myTires = new Tires();
var myCar = new Car(myEngine, myTires);

var myEngine = new Engine(parameter);
var myTires = new Tires();
var myCar = new Car(myEngine, myTires);

var myEngine = new Engine(parameter);
var myTires = new Tires(parameter);
var myCar = new Car(myEngine, myTires);

How to Inject Dependencies?
The whole technique of Injecting Dependencies can be summed up as:

Creating a Service
Registering Service with Providers.
Defining Service inside constructor of the class that needs the Service. (Injecting)
You might wonder, what is a provider?
Well, It is a meta-data that generates instances of services that the injector injects into components and other services..
Everything looks fine, but what exactly is Service and how do you define it?

What is a Service?

  • Service is a class that has a specific purpose. Services are mainly used to provide data to components that ask for it.
  • Components are used only to provide view to user. The Service class is Decorated with @Injectable().

import { Injectable } from `@angular/core`

// @Injectable() defines class "EmpService" as a "Service Class"
@Injectable({ providedIn: 'root', })    // updated in angular-v6+
export class EmpService {
    // do something
}

To Summarize: Components use Service, fetches data and generates view ( view is defined in template of component).

Difference between @Inject and @Injectable
@Inject() :

  • It is a parameter decorator
  • It explicitly tells what are the dependencies of the class inside constructor parameters

export class SmartPhone {
    constructor (@Inject(Battery) bat){}
}
The same functionality can be easily achieved using TypeScript.

export class SmartPhone {
    constructor ( bat: Battery ){}
}

@Injectable()

  • It is a class decorator
  • This tells Angular that this class can be used with Dependency Injector. So whenever a component is dependent on this class, Injector can create the instance of this class.

@Injectable()
export class SmartPhone{}

Remember to add the parenthesis () in @Inject() and @Injectable()

  • Define the EmployeeService class.
    •    cmd : ng g s employeeSerive
  •  Register with injector.
  •  Declare as dependency in EmpList and EmpDetails.


Hands-On 1 : Dependency Injection in Angular :
File : contact_service.ts
import { Injectable } from '@angular/core';
import { of, Observable } from 'rxjs';
import { Contacts } from '../models/contacts';
export class ContactService {
  contacts = {
    'contactsList': [
        {'id': 1, 'name': 'Rajesh', 'city': 'bangalore'},
        {'id': 2, 'name': 'Aarjith', 'city': 'london'},
        {'id': 3, 'name': 'Anjan', 'city': 'california'},
        {'id': 4, 'name': 'David', 'city': 'delhi'}
    ]
  };
  constructor(
  ) { }
  getContacts(): Observable<Contacts> {
    // send contacts to subscriber
    return of(this.contacts);
  }
}

File : contact-list.component.ts
import { Component, OnInit } from '@angular/core';
import { ContactService } from 'src/app/services/contact.service';
@Component({
  selector: 'app-contact-list',
  templateUrl: './contact-list.component.html',
  styleUrls: ['./contact-list.component.css']
})
export class ContactListComponent implements OnInit {
  contacts;
  constructor(
    private contactService: ContactService
  ) { }
  ngOnInit() {
    // get contacts from service and assign it to contacts
    this.contactService.getContacts().subscribe(d=>{
      this.contacts = d ? d.contactsList :[];

    });
  }
}
HTTP in Angular
Till now you had your data inside Service Class. But in reality, the data is present in a remote server, which you can get using HTTP calls to APIs.
In Angular you can use HttpClient API to make HTTP requests to backend services.
So let us understand how to fetch data from server.

  • The component will call its service
  • Service will invoke http get method. This will send request from your web browser to server, asking server to send data.
  • The server will send the data to your web browser as a HTTP Response
  • The response that the "service" will get from HTTP service, is an Observable. So the component has to subscribe the service to receive the data.

Observables
Observables are a sequence of values that arrive in asynchronous mode over time. You can say it works like an array. It is similar to promises in Angular1.x, but the major difference between Observables and Promises are:
Promise returns only a single value when called once whereas Observables return multiple values
Observable's can be cancelled whereas Promises cannot

Observable's Sample
Lets have a look at this sample code to understand how you can create an Observable in Angular:
import {Observable} from 'rxjs/Observable';
export class MyApp {

    private data: Observable<Array<number>>;

        init() {
this.data = new Observable(observer => {
        
setTimeout(  () => observer.next(42), 1000  );

    }); // this will emit value 42, every 1000ms
       let subscription = this.data.subscribe( res => console.log(res) );
    }
 }

You need to import Observable into component from rxjs/Observable
You create a new Observable inside constructor
Then call subscribe on this Observable which allows you to listen to any data that is coming through
Remember to Unsubscribe from observables in ngOnDestroy() lifecycle, else your application might have memory leaks

Observables In Angular

  • In case of HTTP request, Observable is a single value (and not sequence of values) called HTTP Response Object.
  • To make use of Observables, you use RxJS ( Reactive Extensions for JavaScript). RxJS is just an external Library to work with Observable.
  • Few areas in Angular where reactive programming (RxJS) and Observables are used: EventEmitter, HTTP, Forms.

Hands-on 2 : Http In Angular
File : contact-list.component.ts
import { Component, OnInit } from '@angular/core';
import { ContactService } from 'src/app/services/contact.service';
import { Contacts } from 'src/app/models/contacts';
@Component({
  selector: 'app-contact-list',
  templateUrl: './contact-list.component.html',
  styleUrls: ['./contact-list.component.css']
})
export class ContactListComponent implements OnInit {

  contacts;
  constructor(
    private _ContactService : ContactService
  ) { }

  ngOnInit() {
    // call your service, and assign its response to contacts variable
    this._ContactService.getContacts().subscribe(d => {this.contacts = d ? d.contactsList:[];})
  }
}
File : contact.service.ts
import { Component, OnInit } from '@angular/core';
import { ContactService } from 'src/app/services/contact.service';
import { Contacts } from 'src/app/models/contacts';
@Component({
  selector: 'app-contact-list',
  templateUrl: './contact-list.component.html',
  styleUrls: ['./contact-list.component.css']
})
export class ContactListComponent implements OnInit {

  contacts;
  constructor(
    private _ContactService : ContactService
  ) { }

  ngOnInit() {
    // call your service, and assign its response to contacts variable
    this._ContactService.getContacts().subscribe(d => {this.contacts = d ? d.contactsList:[];})
  }
}
What are Routers?

  • Router in Angular defines route to navigate from one view to another, as user performs task on the application.
  • Routers are used to develop 'Single Page Application'
  • Traditionally websites used to load the whole webpage, on cliking any of the links. This increases page load time, bandwidth consumption affecting user-experience
  • Now you can just load the required view, instead of sending the request to server to send a whole new webpage
  • This saves bandwidth, page load time and improves user-experience
  • The good thing is, you can trace back the history of navigations, and bookmark paticular view. This is achieved using path, which is visible in the URL

Routing and Navigation
For defining Routers, you first need to import RouterModule and other required component in app.module.ts, that will be loaded/routed in the webpage as shown in the sample code below.
import { RouterModule, Routes } from '@angular/router';
const appRoutes: Routes = [
   {path: '/my-cources', component: MyCourcesComponent },
   {path: '/explore', component: ExploreComponent }
];
@NgModule({
  imports: [ RouterModule.forRoot(appRoutes) ],
})

path: this defines the URL path in your browser address bar
component: this defines which component should be loaded.

Routing and Navigation

  • Let us now define routerLink and router-outlet in template.
  • routerLink defines the "path" of the "anchor tag". (i.e), when a link is clicked, which "path" should be displayed in URL and which "component" is to be loaded.
  • router-outlet defines the location on webpage where the component is to be loaded.

 <nav>
   <a routerLink= "/my-cources" > My Cources </a>
   <a routerLink= "/explore"> Explore </a>
 </nav>
 <router-outlet></router-outlet>


Parameterized Routing
In Parameterized Routing, you have a variable in path (url), that accepts "parameter".
For example, (in Fresco Play) if you click any category, say, Data Science in Explore, it will show the details of that category and the path can be ---.com/explore/data-science. So you can write routes to look like:
 const routes: Routes = [
     { path: 'explore/data-science', component: DataScienceComponent },
     { path: 'explore/devops', component: DevopsComponent }
     and so on.....
 ];

But this could get messy, if we have a lot of categories in Explore. A better approach can be:

 const routes: Routes = [
  { path: 'explore/:category', component: ExploreDetailsComponent }
 ];

Short and Sweet, is it not?

:category is a variable, as it starts with : (colon)
:category is placeholder for PARAMETER that will have category name.
You can have any number of variables in path, as long as they begin with : and have different name.

Non-Parameterized Routing
Non-parameterized Routing always has higher priority over "Parameterized" Routing. Let us consider the example:

 const routes: Routes = [
  { path: 'explore/:category', component: ExploreDetailsComponent },
  { path: 'explore/settings', component: ExploreSettingsComponent }
 ];

settings does not start with :, hence is non-parameterised.
Even if ExploreSettingsComponent "path" matches with explore/:category, precedence is given to explore/settings (non-parameterised). So, Explore Settings Component is loaded instead of Explore Details Component.

Reading Parameterized Routing
The Explore Details Component should first read the parameter, then load the details based on the :category given in the parameter.

This is achieved using Activated Route service.
To use it, you first need to import it. Then inject it into the constructor of ExploreDetailsComponent.
It provides a params Observable which you can subscribe to receive the route parameters.
 import {ActivatedRoute} from "@angular/router";

 constructor(private route: ActivatedRoute) {
     let category = this.route.snapshot.paramMap.get('category');
 }


 Child Routes

  • In certain scenarios, certain routes should be available only when other routes are present.
  • For example you would want SpecficationComponent to load only when a mobile is selected (--.com/mobiles/423134/spec).
  • In such scenarios, its better to create them as child routes. The following video will explain you more about ChildRoutes

Handson 3 : Routing in Angular :
File : app.component.html
<p class="title"> Contact Applications </p>
<!-- redirect the following links to appropriate routes -->
<p class="menus">
  <a routerLink="/contacts"><span class="menu-item" routerLinkActive="menu-item-active" id="contact-link">Contacts</span></a>
  <a routerLink="/cities"><span class="menu-item" routerLinkActive="menu-item-active" id="cities-link">Cities</span></a>
</p>
<div *ngFor = "let con of Contacts">
  <h3>{{con.city}}</h3>
  <h3>{{con.name}}</h3>
</div>
<router-outlet></router-outlet>

File 2 : app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContactsComponent } from './components/contacts/contacts.component';
import { CityComponent } from './components/city/city.component';

export const routes: Routes = [
  {path:'contacts', component:ContactsComponent},
  {path:'cities', component:CityComponent}
];

@NgModule({
  imports: [RouterModule.forRoot([])],
  exports: [RouterModule]
})
export class AppRoutingModule { }

File 3 : contacts.component.ts


import { Component, OnInit } from '@angular/core';
import {  ContactService } from '../../services/contact.service';
import { Contacts, Contact } from 'src/app/models/contacts';

@Component({
  selector: 'app-contacts',
  templateUrl: './contacts.component.html',
  styleUrls: ['./contacts.component.css']
})
export class ContactsComponent implements OnInit {
  contacts: Contact[];
  constructor( private CS : ContactService
  ) { }

  ngOnInit() {
    // call your service and assign response to contacts
    this.CS.getContacts().subscribe(d => {this.contacts = d ? d.contactsList:[];})
  }
}
File 4 : city.component.ts

import { Component, OnInit } from '@angular/core';
import { ContactService } from 'src/app/services/contact.service';
import { Contact, Contacts } from 'src/app/models/contacts';

@Component({
  selector: 'app-city',
  templateUrl: './city.component.html',
  styleUrls: ['./city.component.css']
})
export class CityComponent implements OnInit {
  contacts: Contact[] = [];
  constructor( private CS : ContactService
  ) { }
  ngOnInit() {
    // call your service and assign response to contacts
    this.CS.getContacts().subscribe(d => {this.contacts= d ? d.contactsList:[];})
  }
}
File 5 : contact.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Contacts } from '../models/contacts';
import { Observable, of } from 'rxjs';
// @Injectable({
//   providedIn: 'root'
// })
export class ContactService {

  url = `http://www.mocky.io/v2/5c5d880f3200000e11220880`;
  constructor( private http:HttpClient
  ) { }
  getContacts(): Observable<Contacts> {
    // return this.http.get<Contacts>(this.url);
    return this.http.get<Contacts>(this.url);
    // get contacts from the above url
    return of();
  }
}

Types of Forms in Angular-II

Forms are vital part of any real application.
There are two Types of Forms in Angular:

  • Template Driven Forms (TDF)
  • Reactive (or Model Driven) Forms

Model Driven Forms (MDF) - with FormGroup and FormControl
Model Driven Forms (MDF) - with FormBuilder

Creating TDF- Inside AppModule

Let us understand how to create Template Driven Forms
In app.module.ts
import FormsModule - @angular/forms
add FormsModule into import metadata
import {FormsModule}               
@NgModule({
    imports: [BrowserModule, FormsModule],
    .....
    .....
})
Creating TDF- Inside AppComponent

In app.component.html
Create a form, provide link in app.component.ts under templateUrl                 
    <form #form="ngForm" (ngSubmit)="onSubmit(form.value)">  //ngForm,ngSubmit
            <label>Name:</label>
            <input type="text" name="name" ngModel>    //ngModel
            ......
            ......
            ......
            <div ngModelgroup="address">     //ngModelgroup
                Street: <input type="text" name="name" ngModel>
                City:
                ....
                ....
            </div>
    </form>

  • ngForm directive gives value of Form Fields. And tells you if the form is in valid/invalid state
  • ngSubmit event is fired when a user clicks on Submit button.
  • ngModel directive creates the FormControl instance to manage the template form control. It also lets you setup two-way data binding between variable in component and form-control in template. <input type="text" name="name" [(ngModel)]="someName">
  • ngModelgroup directive logically group the form fields under one name. For example: Street, City, Country, Zip etc can be grouped in Address

Validating TDF
The attributes you use to validate form controls are mostly parts of standard HTML5 and not angular. For example: required, minlength etc.
Angular provides many classes that are automatically attached to form controls, but are hidden. You can view them using interpolation {{}} as shown
    Name: <input type="text" #refName name="name" ngModel>
    {{refName.className}} //this will show all the classes applied to #refName

The classes provided by Angular are:

  • ng-valid: field value is valid
  • ng-invalid: field value is invalid
  • ng-untouched: field is not yet clicked
  • ng-touched: field is visited by user atleast once
  • ng-pristine: field value is the default value
  • ng-dirty: field value is changed from the default value

Validating TDF- Pairing Classes
You can use these pairs in many combinations, to change style in CSS or provide feedback to user.
ng-valid / ng-invalid
ng-untouched / ng-touched
ng-pristine / ng-dirty
    Name: <input type="text" #refName="ngModel" required name="name" ngModel>
    <div [hidden]="refName.valid || refName.pristine">
        Please enter a name
    </div>

Creating MDF-Inside AppModule

  • A Model Driven Form is pretty much like Template Driven. But here you need to import a different module ReactiveFormsModule instead of FormsModule.
  • In app.module.ts, import ReactiveFormsModule and add it in "import" metadata

import { ReactiveFormsModule } from '@angular/forms';   
@NgModule({
    imports: [ ReactiveFormsModule ],


Creating MDF-Inside AppComponent
In app.component.ts
import FormGroup, FormControl
define FormGroups and FormComponents inside class
export class AppComponent {
    form= new FormGroup({
        name: new FormControl(),
        .....
        address: new FormGroup({
          street: new FormControl(),
          city: new FormControl()
        })
    });
}

Creating MDF-Inside Template
In app.component.html
Create a form with formGroup, formGroup, formComponentName and formComponent and provide link in templateUrl
<form [formGroup]="form" (ngSubmit)="onSubmit()">
    <label>Name:</label>
    <input type="text" formControl="name">
    ......
    ......
    <div formGroupName="address">
      Street: <input type="text" formControl="street">
      City:   <input type="text" formControl="city">
      ....
      ....
    </div>
</form>

MDF – With FormBuilder

  • In Model Driven Form, you might have felt a lot of coding, typing and wording. You had to call new FormGroup() and new FormControl() frequently. Now you can give a little rest to your hand, as we now have a laboratory called FormBuilder, which creates FormGroup and FormControl.
  • All you have to do is import FormBuilder, FormGroup and call .group() method as shown

In app.component.ts
export class AppComponent {
  form: FormGroup;
  constructor(fb: FormBuilder){
    this.form = this._fb.group({
      name: ['', [
          Validators.required,
          Validators.minLength(4),
          Validators.maxLength(10)] ],
      .....
      address = this._fb.group({
        street: [],
        .....
      })
    });
  }
}

Validating MDF-Inside Component
In Model Driven Form, you need to import Validators to validate the form.

In app.component.ts
export class AppComponent {
  form= new FormGroup({
      name: new FormControl([
            Validators.required,
            Validators.minLength(4),
            Validators.maxLength(10)
          ]),
  });
}

Validating MDF-Inside Template
In app.component.html
Name: <input type="text" #refName formControlName="name">
<div *ngIf="name.invalid && (name.dirty || name.touched)">
    <div *ngIf= " name.errors.required  "> Please enter a name </div>
    <div *ngIf= " name.errors.minlength "> Minimum Length: 4 </div>
</div>

Hands-on 4 : Angular reactive forms question
File : app.component.ts
import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  /** create a form (contactForm) with following controls/groups and  validations
   *    - name: control,    valiations: required
   *    - phone: control,   validations: required, number of 10 digits
   *    - address: group
   *      - street: control
   *      - city: control
   *      - zip: number of 6 digits
   */
 contactForm = new FormGroup({
    name : new FormControl(null,[Validators.required]),
    phone : new FormControl(null,[Validators.required, Validators.pattern("^[0-9]*$"),Validators.minLength(10),
Validators.maxLength(10)]),
    address : new FormGroup({
      street : new FormControl(null),
      city : new FormControl(null),
      zip : new FormControl(null, [Validators.minLength(6), Validators.maxLength(6), Validators.pattern("^[0-6]*$")]),
    })
  });
onSubmit() {
    console.log('form value =>', this.contactForm.value);
  }
  get name() { return this.contactForm.get('name'); }
  get phone() { return this.contactForm.get('phone'); }
  get zip() { return this.contactForm.controls['address'].get('zip'); }

}

Best Practices

  • Use _(underscore) before a private member and $ after an observable, improves readability. Example: _courseService , actionSubjects$
  • One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control.
  • Use dashes to separate words in the descriptive name.
  • Use conventional type names including .service, .component, .pipe, .module, and .directive.
  • Use dots to separate the descriptive name from the type. Example: file-name.component.ts, ----.service.ts etc.
  • Put bootstrapping and platform logic for the app in a file named main.ts.
  • Avoid putting app logic in main.ts. Instead, consider placing it in a component or service.

Angular 2 Routes and Forms - Course Summary
So you came to the end of the course. Hope you enjoyed learning.
In this course you have learnt:

  • What dependencies are and how to inject them
  • Defining services that send data to components
  • Using HTTP request to send the data to component
  • You understood the importance of routers and how they can help building Single Page Applications
  • Finally you learnt Template Driven and Model Driven approach to create forms

-----------------------------------------------------------------------------------------------------------------------------

#Mini-project - Angular8 Kanban Board

credit 0.5
course - 1101

Angular8 - Kanban Board
KanBan is a popular workflow used in task management, project management, issue tracking, and
so on. The workflow is usually visualized using the Kanban Board.
Implements a kanban Board with the cards where each card contains only title.
Certain core Angular functionalities have already been implements. Complete the given single Page Angular application thats simuntakest a Kanban Board app, to pass the unit tests.

Specifications
Kanban Board
Kanban Controls

  • The New Task filed is the input area for the name of a new task.
  • The Add Button, with the name specified in the input, adds the task as the last task to the first stage. This button must be disabled when the input for the name of the new task is empty
  • The Selected Task filed shows the name of selected task, if any.
  • The Move bake button moves the selected task to the previous stages. This button be disabled if either no task is selected or the selected task is in first stage.
  • The Move forward button moves the selected task, if any to the next stage if any This button be disable if either no task is selected or the selected task is in the last stage.
  • The Delete button deletes the selected task, if any button must be disabled if no task selected.


Note - Here in this hands-on We need to write code only below files rest of the Same 
File : Kanban-viwe.component.ts
import { Component, OnInit } from '@angular/core';
import { element } from 'protractor';

@Component({
  selector: 'app-kanban-view',
  templateUrl: './kanban-view.component.html',
  styleUrls: ['./kanban-view.component.css']
})
export class KanbanViewComponent implements OnInit {
  stages = [{
    id: 1,
    name: 'Backlog',
    cards: [],
  }, {
    id: 2,
    name: 'To Do',
    cards: [],
  }, {
    id: 3,
    name: 'Ongoing',
    cards: [],
  }, {
    id: 4,
    name: 'Done',
    cards: [],
  }];
  newCardName = '';
  task='';
  selectedCardName = '';
  selectedStageIndex = -1;
  selectedCardId;
  constructor() { }

  ngOnInit() {
  }

  onAddCard() {
    this.stages[0].cards.push({
    name: this.task,
    id : this.stages[0].cards.length + 1
    });
    this.task = '';

  }
  onCardselect(data) {
      this.selectedStageIndex = data.stageId -1;
     this.selectedCardId = data.cardId;
    this.stages[this.selectedStageIndex].cards.forEach(i => {
         if(i.id === data.cardId){
           this.selectedCardName = i.name;
         }
    });

  }
  onMoveBackCard() {
    const realIndex = this.selectedStageIndex;
    this.stages[realIndex].cards = this.stages[realIndex].cards.filter(i => {
      return (i.id !== this.selectedCardId)
    });
    this.stages[realIndex -1 ].cards.push({
      name: this.selectedCardName,
      id: this.selectedCardId
    });
    this.selectedStageIndex = realIndex -1;

  }
  onMoveForwardCard() {
     const realIndex = this.selectedStageIndex;
    this.stages[realIndex].cards = this.stages[realIndex].cards.filter(i => {
      return (i.id !== this.selectedCardId)
    });
    this.stages[realIndex + 1 ].cards.push({
      name: this.selectedCardName,
      id: this.selectedCardId
    });
    this.selectedStageIndex = realIndex + 1;

  }
  onCardDelete(){
    this.stages[this.selectedStageIndex].cards  =      this.stages[this.selectedStageIndex].cards.filter(i => {
      return (i.id !== this.selectedCardId)
    });
    this.selectedCardName = '';
    this.selectedStageIndex = -1;
  }


}

File : kanban-view.component.html
<div class='kanban_view'>
  <div class='kanban_controls'>
   <div class='add_card'>
       <input class='add_card_input' [(ngModel)]="task" type="text" placeholder="New Task Name">
       <button class="add_card_btn" (click)="onAddCard()">Add</button>
   </div>
    <div class='edit_card'>
        <input class='selected_card_input' [(ngModel)]="selectedCardName" type="text" placeholder="Selected Task will appear">
        <button class='card_move_back_btn' [disabled]="selectedStageIndex=== -1 || selectedStageIndex === 0" (click)="onMoveBackCard()">Move Back</button>
        <button class='card_forward_back_btn' [disabled]="selectedStageIndex=== -1 || selectedStageIndex === 3"  (click)="onMoveForwardCard()">Move Forward</button>
        <button class='card_delete_back_btn' [disabled]="selectedStageIndex === -1" (click)="onCardDelete()">Delete</button>
      </div>

  </div>
  <div class='kanban_stage_container'>
    <app-kanban-stage *ngFor="let stage of stages" [stage]='stage' (onCardselect)='onCardselect($event)'></app-kanban-stage>
  </div>
</div>

File : kanban-stage.component.ts
import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
@Component({
  selector: 'app-kanban-stage',
  templateUrl: './kanban-stage.component.html',
  styleUrls: ['./kanban-stage.component.css']
})
export class KanbanStageComponent implements OnInit {
  @Input() stage;
  @Output() onCardselect: EventEmitter<Object> = new EventEmitter<Object>();

  constructor() {}

  ngOnInit() {
  }
  onCardSelected(data) {
    this.onCardselect.emit({
      cardId : data.cardId,
      stageId : this.stage.id
    });

  }
}
File : kanban-stage.component.html
<div class='kanban_stage'>
  <h2 class='kanban_header'>{{ stage.name }}</h2>
  <div class='kanban_card_container'>
    <app-kanban-card (onCardSelected)="onCardSelected($event)" *ngFor="let card of stage.cards" [card]='card'></app-kanban-card>
  </div>
</div>
File : kanban-card.component.html
<div class='kanban_card' (click)="onCardSelect(card['id'])">
  <div class='kanban_card_content'>{{ card['name'] }}</div>
</div>
File : kanban-card.component.ts
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
@Component({
  selector: 'app-kanban-card',
  templateUrl: './kanban-card.component.html',
  styleUrls: ['./kanban-card.component.css']
})
export class KanbanCardComponent implements OnInit {
  @Input() card: object;
  @Output() onCardSelected: EventEmitter<Object> = new EventEmitter<Object>();
  constructor() { }
  ngOnInit() {
  }
  onCardSelect(id) {
    console.log(id);
    this.onCardSelected.emit({
      cardId : id
    });

  }
}
---------------------------------------------------------------------------------------------------------------------------

Comments

Post a Comment

If you any doubts, please let me know Telegram Id: @Coding_Helperr
Instagram Id : codingsolution75

More Related

For Any Help Related to coding exams follow me on Instagram : codingsolution75

Popular Posts

WIPRO Latest Coding Questions with Solutions : 2023

EPAM Latest Coding Questions with Solutions 2023

MindTree Latest Coding Questions with Solutions

Fresco Play Hands-on Latest Solutions

TCS Digital Exam | DCA | Direct Capability Assessment

TCS Wings 1 All Prerequisite

Infosys | InfytTq | Infosys Certification Exam Latest Coding Questions with Solutions

RAKUTEN Latest Coding Questions with Solutions

Cognizant

TypeScript Complete Course With Completed Hands-on