3. Include jQWidgets Component

In this chapter, we will create a new component, class and also, integration of the jqxInput component. Also, we will integrate an option to edit the component.

How to create a component

We will use the internal Angular CLI command to start creating a new component named - "heroes".

ng generate component heroes

This command automatically generates a new folder into the app/ folder. The folder src/app/heroes have the same name - "heroes". It collects three main files - heroes.component.html, heroes.component.ts, and heroes.component.css.
(there have the fourth "heroes.component.spec.ts" file but will not use it)

In the heroes.component.ts file has a class named HeroesComponent with the following structure:

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

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

export class HeroesComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

It is possible to create a new component from scratches if save the main pointers. You always need to import the Component from the Angular core. After that start to describe it with its metadata properties and annotate it as you start with @Component. The example above generated from the CLI has three metadata properties:

  1. selector - the component's CSS element selector
  2. templateUrl - the location of the component's template file.
  3. styleUrls - the location of the component's private CSS styles.
    Also, there has and other properties part of the Component
  4. template - directly set template
    If you want to create a simple component the (4.) property is suitable for this purpose.

The first point (1.) is a CSS element selector which is used to identify the 'Component' with same named HTML element - <app-heroes></app-heroes>

The ngOnInit is a lifecycle hook Angular calls ngOnInit shortly after creating a component. It's a good place to put initialization logic.

If you prefer you can create the component from scratches but you should be careful to have the main parts, like this:

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

@Component({
  selector: 'stylized-app-heroes',
  template: '<h2>Stylized Heroes Component</h2>'
})

export class StylizedHeroesComponent { }

Start editing the component

We will add a property named hero to the HeroesComponent. Set it with value "Windstorm". It is not a final state, it will be changed during the tutorial's creation.

Open the heroes.component.ts file if you haven't and add the new property - hero = 'Windstorm';

Your file should look like this:

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

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

export class HeroesComponent implements OnInit {

  hero = 'Windstorm';

  constructor() { }

  ngOnInit() {
  }
}

Represent the property in the template

Now it is time to open the heroes.component.html file and change the template with {{hero}} as follows:

{{hero}}

We will replace the whole content with this {{hero}} but this does not reflect on the main component. We should include the HeroesComponent with its selector app-heroes as a tag to the AppComponent template file - <app-heroes>.

Return to the main application folder app.component.html and do the mentioned:

<h1>{{title}}</h1>
<app-heroes></app-heroes>

Almost at the same moment after you save these changes in the relevant files, they will reflect in the browser view as the following two rows:

HEROES
Windstorm

The CLI ng serve command should be still running otherwise you cannot see these changes.

'ng-serve'

Create a new class

In the real project, the component will contain more complex structure. The hero property can be more complicated. If we have a class which we could inherit to this property, that will be more useful.

Create a Hero class in its own file in the src/app folder. We should create a new file hero.ts which will have definitions of this class. Give it an id and name properties. It should look like that:

export class Hero {
    id: number;
    name: string;
}

Return to the HeroesComponent class and import the Hero class.

Refactor the component's hero property to be of type Hero. Initialize it with an id of 1 and the name Windstorm.

The revised HeroesComponent class file should look like this:

import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };

    constructor() { }

    ngOnInit() {
  }
}

The page no longer displays properly because you changed the hero from a string to an object.

The new representation of the hero as the object

We should update our HeroesComponent template. Let's update the heroes.component.html:

<h2>{{ hero.name }} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div><span>name: </span>{{hero.name}}</div>

The browser should update itself automatically.

'hero'

Using Pipe

The name of the hero stay a seamlessly but we can add accent to it with "pipe" formatting. In the heroes.component.html update the hero.name binding with this:

<h2>{{ hero.name | uppercase }} Details</h2>

The hero's name is displayed in capital letters after the browser has been refreshed.

'pipe'

The word uppercase in the interpolation binding, right after the pipe operator ( | ), activates the built-in UppercasePipe.

Pipes are a good way to format strings, currency amounts, dates and other display data. Angular ships with several built-in pipes and you can create your own.

How to add jQWidgets Component

It is time to add the jqxInput. For this purpose, we should refer to the jqxInputComponent from our library and include the component to the "declarations" member of the @NgModule metadata directive. Assuming you already installed our library with one of the available options: "jqwidgets-scripts" or "jqwidgets-framework". Depends on this if you use one of both libraries you should refer to that way.

( We will use "jqwidgets-scripts" in this tutorial. )

Your app.module.ts file should look as follow:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';

import { jqxInputComponent } from 'jqwidgets-scripts/jqwidgets-ts/angular_jqxinput';

@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent,
    jqxInputComponent
  ],
  imports: [
    BrowserModule,
    CommonModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

After saving this file it will throw an error like this:
"ERROR in ./node_modules/jqwidgets-scripts/jqwidgets-ts/angular_jqxinput.ts"

To handle this error we should include the angular_jqxinput file in files property (and also, the "include": ["src/**/*"]) in tsconfig.json file. We will add these changes which are on the same level as the compilerOptions property:

{
    "compileOnSave": false,
    "compilerOptions": {
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "declaration": false,
        "moduleResolution": "node",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "es5",
        "typeRoots": [
            "node_modules/@types"
        ],
        "lib": [
            "es2017",
            "dom"
        ]    
    },
    "include": [
        "src/**/*"
    ],
    "files": [
        "node_modules/jqwidgets-scripts/jqwidgets-ts/angular_jqxinput.ts"
    ]
}

There is one more thing we should change in the root folder. Open the .angular-cli.json file and add the path to the base CSS file in the style section:
"../node_modules/jqwidgets-scripts/jqwidgets/styles/jqx.base.css" Your property should include this reference next to the "styles.css" and it will look as this below:

"styles": [
    "styles.css",
    "../node_modules/jqwidgets-scripts/jqwidgets/styles/jqx.base.css"
],

We will use jqxInput to edit the hero's name. For this purpose we should use it from the component class to the screen and from the screen back to the class. To achieve this functionality we should use the Two-Way data binding between the <jqxInput> and the hero.name.

Two-Way data binding

Let's first add a simple input into the template in the heroes.component.html file:

<div>
    <label>name:
    <input [(ngModel)]="hero.name" placeholder="name">
    </label>
</div>

FormsModule is missing

The next problem we will meet is in the browser's console. We will see this error message:
Uncaught Error: Template parse errors: Can't bind to 'ngModel' since it isn't a known property of 'input'.

The ngModule is an optional directive and it belongs to the FormsModule

AppModule

Angular has internal attributes that are used to describe different structural components. Angular needs to know how the pieces of your application fit together and what other files and libraries the app requires. This information is called metadata.

Some of the metadata is in the @Component decorators that you added to your component classes. Other critical metadata is in @NgModule decorators.

The most important @NgModule decorator annotates the top-level AppModule class.

The Angular CLI generated an AppModule class in src/app/app.module.ts when it created the project. This is where you opt-in to the FormsModule.

Import FormsModule

Include to the app.module.ts file that row:
import { FormsModule } from '@angular/forms';

Also, it should be added to the @NgModule metadata's imports array: imports: [ BrowserModule, FormsModule ],

Now your app.module.ts file should look on that way:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';

import { jqxInputComponent } from 'jqwidgets-scripts/jqwidgets-ts/angular_jqxinput';

@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent,
    jqxInputComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})

export class AppModule { }

[(ngModel)] is Angular's two-way data binding syntax. The flow will work in both directions and the result from the jqxInput will show in the template structure where hero.name property is set. The text typed in the textbox will flow in both directions from the hero.name property to the textbox, and from the textbox back to the hero.name.

Now we can replace the <input /> with the jqxInput component. Change your heroes.component.html file as follows:

<h2>{{ hero.name | uppercase }} Details</h2>
<div>
    <span>id: </span>{{hero.id}}</div>
<div>
    <label>name:
        <jqxInput [(ngModel)]="hero.name" [placeHolder]="'hero name'"></jqxInput>
    </label>
</div>

In conclusion