Javascript placement in HTML5 document

An HTML Page example showcasing how you can insert/link javascript scripts inside an HTML document.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Javascript placement in HTML document</title>
<script>
console.log("Script in HTML > head");
</script>
<script src="head.js"></script>
</head>
<body>
<script>
console.log("Script in HTML > body");
</script>
<script src="body.js"></script>
</body>
</html>

You can have javascript directly inside the document (head / body) and/or import it from external files (head/body).

You can find the file as a project at Github: Javascript-Cheatsheet / js-placement.

Moodle editathon - what a fun event...

On 5/5/2018 I participated in a 4h editathon event, hosted at Smartbox facilities and organized for the city of Larissa by my friend John Gialamas. What a fun… !

Moodle is an open-source learning platform, for hosting online courses, etc…

Our mission was to translate as much content as we could into Greek language. So this way we contribute to bringing the platform closer to the Greek education community. And I think we did a great job…

Regardless of the result, it was great fun and met interesting people !

Until next time…

Captain out

Immutable Class in Typescript/Angular

Please refer to my Github project for details.

This is a class structure for creating immutable objects in typescript / angular. An immutable object is one that you do not update its contents. Instead you create a new object with altered content and then replace the original with a new one. The initial intention is to be used with ngx-model.

In this example we are creating a class that models a person .

Define an interface

First define an interface to be applied to the constructor of the class:

src/app/model/person.ts

1
2
3
4
5
export interface IPerson {
name: string;
surname: string;
age?: number; //optional property
}

Define the class

Define the class with its constructor and the properties:

src/app/model/person.ts

1
2
3
4
5
6
7
8
9
10
11
12
export class Person {
//Define the class Person
name: string;
surname: string;
age?: number;

constructor(newPerson: IPerson) {
this.name = newPerson.name;
this.surname = newPerson.surname;
this.age = newPerson.age || 0; //Default value in optional property
}
}

Define a json method

A .json() method will allow you extracting the original data that the class was created with:

src/app/model/person.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export class Person {
...

// Allows extraction of the original properties of the class
json(): IPerson {

// Return the properties
return {
name: this.name,
surname: this.surname,
age: this.age
};
}

...
}

Define a clone method

A .clone() method will allow you easily obtaining a clone of the object when needed:

src/app/model/person.ts

1
2
3
4
5
6
7
8
9
10
export class Person {
...

// Allows cloning of the object
clone(): Person {
return new Person( this.json() );
}

...
}

Define an update method

Although the name .update() refers to a mutable object, in practice this method will allow you easily to get a copy of the original object with updated properties.

src/app/model/person.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export class Person {
...

// Returns a new clone of the object with updated specific properties
update(data: Partial<IPerson>): Person {
let result: IPerson = this.json();
for (let key in data) {
result[key] = data[key];
}
return new Person(result);
}

...
}

Define any additional methods

You define any additional methods and/or derived data as methods.

src/app/model/person.ts

1
2
3
4
5
6
7
8
9
10
export class Person {
...

// A sample class method
getDescription(): string {
return this.age >= 18 ? 'adult' : 'kid';
}

...
}

Conclusion

You are now having a class which can produce immutable objects. Its basic methods allows to easily manipulate the object and still keep it immutable.

A Home-made Redux Store

I created a code template/guide on how to create a simple Redux-store for Angular. You can find it in my Gist: Home Made Redux Store

Captain out…


Home-made Redux Store

This is a simple/small Redux Store implementation for small angular apps.
Use in case you don’t want to use the full-blown Redux libraries out there, for the sake of simplicity.

This template code is based and inspired on Ultimate Angular - NGRX Store + Effects Online Training (Free).
Highly recommended.

The example below is based on a super simple fictional ‘Agenda’ app where we can add contacts and to-do tasks.

Step 1: Copy the Home-Made Redux Store code

Copy into your application the following files, from this Gist that form the ‘Home-Made Redux Store’ class.

Step 2: Define your state

Now define your AppState interface and its substates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export interface IContact {

name: string;
email: string;

}

export interface ITask {

description: string;
done: boolean;

}


export interface IAppState {

contacts: IContact[];
tasks: ITask[];

}

Step 3: Define the initial state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export const initialState: IAppState = {
contacts: [
{
name: "Me",
email: "me@example.com"
}
],

tasks: [
{
description: "Start populating this list",
done: false
}
]
}

Step 4: Define your action types

1
2
3
4
export enum ActionTypes {
ADD_CONTACT,
ADD_TASK
}

Step 5: Define your actions and their payloads

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export class TaskAction implements IAction {
public readonly type = ActionTypes.ADD_TASK

constructor (
public payload: ITask
){}
}

export class ContactAction implements IAction {

constructor(
public payload: IContact
) {

}

public readonly type = ActionTypes.ADD_CONTACT;

}

Step 6: Define your reducers (functions)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const contactsReducer: IReducer = function (state: IContact[], action: ContactAction) {
switch (action.type) {
case ActionTypes.ADD_CONTACT: {
const contacts = [...state, action.payload];
return contacts;
}
}
return state;
};

const tasksReducer: IReducer = function (state: ITask[], action: TaskAction) {
switch (action.type) {
case ActionTypes.ADD_TASK: {
const tasks = [...state, action.payload];
return tasks;
}
}
return state;
}

export const reducers: IReducers = {
contacts: contactsReducer,
tasks: tasksReducer
};

Step 7: Instanciate the store as your own service

In this example, the State Service extends the HomeMadeReduxStore class. Alternatively you could build a wrapper service
that utilizes the HomeMadeReduxStore class.

1
2
3
4
5
6
7
8
9
10
11
@Injectable()
export class AppStateService extends HomeMadeReduxStore<IAppState> {

constructor(
reducers: IReducers = {},
initialState: IAppState = <IAppState>{}
) {
super(reducers, initialState);
}

}

Step 8: Inject your service into your module

In this example we instanciate the service with our specific state and reducers.

app.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [
{provide: AppStateService, useFactory(){
return new AppStateService(reducers,initialState);
}}
],
bootstrap: [AppComponent]
})
export class AppModule { }

Step 9: Start using the store by dispatching actions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { TaskAction } from './app-state/actions/task-action';
import { AppStateService } from './app-state/app-state.service';
import { Component } from '@angular/core';
import { ContactAction } from './app-state/actions/contact-action';

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

constructor(
private appState: AppStateService
){

console.log('Intial State:', this.appState.value);

this.appState.dispatch(new ContactAction ({
name: "John Doe",
email: "johndoe@example.com"
}));
console.log('Adding contact', this.appState.value);

this.appState.dispatch(new TaskAction ({
description: 'Buy groceries',
done: false
}));
console.log('Added task', this.appState.value);

}

}

Angular Dependency Injection Cheatsheet

Here’s is a cheatsheet on using Angular’s DI framework.

You can find it also in my Gist.


How to Inject Dependencies

When we want to use a dependent class/value, instead of creating them ourselves,
Angular’s DI framework will do it for us. In order to do this, we must define for the class/value a Token.
Once Angular sees that we want to inject the specific token, it will create the class/value and provide it to our class.

Injecting a Class

Create a class with Injectable decorator

1
2
3
4
5
6
import { Injectable } from '@angular/core';

@Injectable()
export class MyService {
...
}

Declare the class in a NgModule

1
2
3
4
5
NgModule ({
...
providers: [MyService],
...
})

Note: The above is a shortcut for

1
providers: [{ provide: MyService, useClass: MyService }]  // provide: <token>, useClass: <the class to instanciate>

Request injection

1
2
3
4
5
6
export class MyClientClass {

constructor (
private myService: MyService // Angular identifies the MyService token and provides an instance of the class.
){}
}

Injecting a Value

Declare the value in a NgModule

1
2
3
4
5
NgModule ({
...
providers: [{provide: 'API', useValue: 'https://api.example.com/v1.0'}],
...
})

Inject the value in a class

1
2
3
4
5
6
7
8
9
10
export myAPIClient {
constructor (
@Inject('API') apiHome: string
){}

//use the injected value
public getData(){
console.log(`Will fetch data from ${this.apiHome}`);
}
}

useFactory: Inject anything

You can inject a service that requires parameters in its constructor, or anything that can be returned from a function.

Injecting a parametrized service

Create your service as normal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Injectable } from '@angular/core';

@Injectable()
export class RandomProvider {

public modulo = 1;

constructor(modulo: number) { //Instanciating the class requires parameter
this.modulo = modulo
}

getRandomNumber(): number {
return this.modulo*Math.random();
}

}

Define Injection with useFactory

1
2
3
4
5
6
7
8
9
10
11
NgModule ({
...
providers: [
{
provide: RandomProvider,
useFactory(){
return new RandomProvider(4);
}
}]
...
})

Use the injection as normal

1
2
3
4
5
6
7
8
export class Randomizer {

constructor( private randomService: RandomService ) {}

public roll(): number {
return this.randomService.getRandomNumber();
}
}

Inject a constructed value

Let’s say that we want to inject an array of value that is constructed on start. We use useFactory to construct the value.

Construct the injectable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NgModule ({
...
providers: [
{
provide: 'RANDOM_ARRAY',
useFactory(){
return [
Math.random(),
Math.random(),
Math.random()
];
}
}]
...
})

Inject the value as normal

1
2
3
4
5
6
7
8
export class ConsumeInjectedValue {

constructor( @Inject('RANDOM_ARRAY') private threeRandomValues: number[] ) {}

public consoleLog() {
console.log(this.threeRandomValues);
}
}

useFactory Dependencies

If the factory function itself has dependencies, they can be declared in the deps property.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { HttpClientModule, HttpClient } from '@angular/core/http'

NgModule ({
...
providers: [{
provide: 'TRUE_RANDOM_SOURCE',
deps: [HttpClient], //here we declare dependencies of our factory
useFactory(httpClient: HttpClient) { // our factory needs an injectable (HttpClient)
return this.httpClient.get('http://api.example.com/randomval'); //fake URL - does not really work
}
}]
...
})

Angular Forms, Angular Directives

My Angular Tips-n-Tricks Gist got an update with notes on Directives and Angular Forms

You can find it here.

Here follow the changes:


Directives examples

NgForn

In this example we also demonstrate how to print an array of strings with a comma separator in between them.

in component

1
public values = ['one','two','three'];

in HTML template

1
2
3
4
<span *ngFor="let value of values; let i = index">
{{value}}
<span>{{i < (values.length-1) ? "," : ""}}</span>
</span>

NgSwitch

ngSwitchDefault is optional.

1
2
3
4
5
6
<div [ngSwitch] = "position">
<div *ngSwitchCase = "1">You are 1st!</div>
<div *ngSwitchCase = "2">You are 2nd!</div>
<div *ngSwitchCase = "3">You are 3rd!</div>
<div *ngSwitchDefault>You are not worth mentioning!</div>
</div>

NgStyle

Single property

1
<div [style.background-color]="'black'"></div>

Multiple properties at once

1
<div [ngStyle]="{color: 'white', 'background-color': 'blue'}"></div>

With units

1
2
3
<div [style.font-size.px]="13"></div>
<div [style.font-size.em]="1.5"></div>
<div [style.font-size.%]="45.5555"></div>

NgClass

1
2
<div [ngClass]="{bordered: true, 'white-background': false}"></div>
<div [ngClass]="['class1','class2','class3']"></div>

tip: You can use a classes object in your code and bind it to template

component code

1
public classesObj = { /* implement logic that build classes in the object*/ }; // e.g. {bordered: true; strongly: true}

HTML template

1
<div [ngClass]="classesObj"></div>

NgNonBindable

Helps you ‘disable’ bidding in HTML so you can use the

My Angular Tips-n-Tricks Gist got an update with notes on Directives and Angular Forms

You can find it here.

Here follow the changes:


Directives examples

NgForn

In this example we also demonstrate how to print an array of strings with a comma separator in between them.

in component

1
public values = ['one','two','three'];

in HTML template

1
2
3
4
<span *ngFor="let value of values; let i = index">
{{value}}
<span>{{i < (values.length-1) ? "," : ""}}</span>
</span>

NgSwitch

ngSwitchDefault is optional.

1
2
3
4
5
6
<div [ngSwitch] = "position">
<div *ngSwitchCase = "1">You are 1st!</div>
<div *ngSwitchCase = "2">You are 2nd!</div>
<div *ngSwitchCase = "3">You are 3rd!</div>
<div *ngSwitchDefault>You are not worth mentioning!</div>
</div>

NgStyle

Single property

1
<div [style.background-color]="'black'"></div>

Multiple properties at once

1
<div [ngStyle]="{color: 'white', 'background-color': 'blue'}"></div>

With units

1
2
3
<div [style.font-size.px]="13"></div>
<div [style.font-size.em]="1.5"></div>
<div [style.font-size.%]="45.5555"></div>

NgClass

1
2
<div [ngClass]="{bordered: true, 'white-background': false}"></div>
<div [ngClass]="['class1','class2','class3']"></div>

tip: You can use a classes object in your code and bind it to template

component code

1
public classesObj = { /* implement logic that build classes in the object*/ }; // e.g. {bordered: true; strongly: true}

HTML template

1
<div [ngClass]="classesObj"></div>

NgNonBindable

Helps you ‘disable’ bidding in HTML so you can use the {{ content }} double brackets as is.

1
2
3
<div ngNonBindable>
{{ this should be rendered as is and not interpolated. }}
</div>

Enums

Defining an Enum

1
2
3
4
5
6
7
8
// Simple Enum
enum Role = {Employee, Manager, Admin}

// Custom start value
enum RolesCustomStart = {Employee = 3, Manager, Admin}

// Custom values
enum RoleCustomValues = {Employee = 3, Manager = 5, Admin = 7}

Using Enum

1
const role = Role.Manager;

Finding Enum description by value

1
console.log ( Role[1]); // 'Manager'


Template Driven Forms

How to setup

import FormsModule

1
2
3
4
5
6
7
8
9
import { FormsModule } from '@angular/forms';

@NgModule ({
...
imports: [
FormsModule,
...
]
})

Add Form and submit handling
HTML Template:

1
2
3
4
5
<form #myForm="ngForm"
(submit)="handleSubmit(myForm.value)"
>
<button type="submit">Submit</button>
</form>

Component:

1
2
3
public handleSubmit(formValue: any) {
console.log(formValue);
}

Add Fields

1
2
3
4
5
6
7
8
9
<div>
<label for="usernameInput">Username</label>
<input type="text"
id="usernameInput"
name="username"
ngModel
placeholder="enter username"
>
</div>

Available form properties

  • myForm.errors
  • myForm.dirty
  • myForm.valid

Notes and Explanations

  • NgForm automatically binds to a <form> element and creates a FormGroup object called ngForm.
    With #myForm="ngForm" we actually grab this FormGroup object and bind it to a template variable.

  • NgForm automatically creates the (submit) event emitter.

  • NgModel directive creates automatically a FormControl named with the name value.

  • NgModel creates a one-way data bind (model -> view)


Reactive Forms

A quick reference guide on how to setup.

How to setup

import ReactiveFormsModule

1
2
3
4
5
6
7
8
import { ReactiveFormsModule } from '@angular/forms';
@NgModule ({
...
imports: [
ReactiveFormsModule,
...
]
})

define a FormGroup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
...

export FormComponent {

public userForm: FormGroup;
public

constructor(
private fb: FormBuilder
){
this.userForm = this.fb.group({
username: ['user1', Validators.required] //default value = 'user1', Optional validator = required
});

this.username = this.userForm.controls['username']; //optional. Allows easier reference in HTML template

}

public onSubmit(formValue: any) {
console.log('You submitted: ', formValue);
}


}

Create form and bind controls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<form [formGroup]="userForm"
(ngSubmit)="onSubmit(userForm.value)"
>

<label for="usernameElm">Username</label>
<input type="text"
id="usernameElm"
placeholder="enter username here"
[formControl]="userForm.controls['username']"
>

<!--
optionally you could bing the element to the Control property:
[formControl]="username"
-->

<button type="submit">Submit</button>

</form>

Handling errors and validation

Checking whole form validity example

1
2
<button [disabled]="!userForm.valid"
type="submit>Submit</button>

Detecting errors in controls

1
2
<input [class.error]="!userForm.controls['username'].valid"
...

Handling specific validation errors

1
2
3
<div *ngIf="userForm.controls['username'].hasError('required')">
You must enter a username
</div>

Good practice: see if control is touched

1
2
3
4
5
<!-- we are using the reference variable for leaner code -->

<input [class.error]="!username.valid && username.touched"

<div *ngIf="username.hasError('required') && username.touched"

Other validation options

Errors from FormGroup level

You can check for errors of specific field even in FormGroup level:

1
console.log (userForm.hasError('required','username');  // FormGroup.hasError(errorKey, fieldKey)

Custom Validators

Create a function that accepts FormControl object as parameter and returns an error string map {errorKey: boolean} if the error is valid.

Define Validator
Example: Not allow ‘admin’ as username.

1
2
3
4
5
private reservedUsernames (control: FormControl): { [string]: boolean } {
if (control.value === 'admin') {
return {reserved: true};
}
}

Apply Validator

1
2
3
4
5
this.userForm = this.fb.group({
username: ['', Validators.compose(
[Validators.required, this.reservedUsernames]
)]
});

Check in HTML template

1
2
3
<div *ngIf="userForm.controls['username'].hasError('reserved')">
Username reserved and cannot be used.
</div>

Monitor changes

There is an available Observable valueChanges in both FormGroup and FormControl classes.

1
2
3
4
5
6
7
this.userForm.valueChanges.subscribe(
(value: any) => console.log('Form Value changed to', value)
);

this.userForm.controls['usernames'].valueChanges.subscribe(
(value: string) => console.log('Current username: ', value);
);

2-way data bind

If you need to… you can bind an input control value to an external variable.

Component class:

1
2
3
4
5
6
public currentUsername: string;

//example of setting the value of currentUsername
public suggest() {
this.currentUsername = 'user'+Math.floor((Math.random() * 10) + 1);
}

HTML template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
<!-- ngModel binds the input element value to variable
<input type="text"
placeholder="enter username here"
id="usernameElem"
[formControl]="userForm.controls['username']"
[(ngModel)]="currentUsername"
>
...
<!-- the currentUsername reflects the value of input element -->
<div> Current Value: {{currentUser}}</div>

<!-- setting the currentUsername reflects the value back to input element -->
<button (click)="suggest()">Suggest a username</button>

Note: ngModel binds the input element value to variable. We still have to use [formControl] to bind the input element with the rest of our FormGroup and make it part of it (validation, etc).

Notes

  • When Angular sees [formGroup] in a <form> element, it does not automatically bind the ngForm FormGroup.

Captain out…

double brackets as is.

1
2
3
<div ngNonBindable>
{{ this should be rendered as is and not interpolated. }}
</div>

Enums

Defining an Enum

1
2
3
4
5
6
7
8
// Simple Enum
enum Role = {Employee, Manager, Admin}

// Custom start value
enum RolesCustomStart = {Employee = 3, Manager, Admin}

// Custom values
enum RoleCustomValues = {Employee = 3, Manager = 5, Admin = 7}

Using Enum

1
const role = Role.Manager;

Finding Enum description by value

1
console.log ( Role[1]); // 'Manager'


Template Driven Forms

How to setup

import FormsModule

1
2
3
4
5
6
7
8
9
import { FormsModule } from '@angular/forms';

@NgModule ({
...
imports: [
FormsModule,
...
]
})

Add Form and submit handling
HTML Template:

1
2
3
4
5
<form #myForm="ngForm"
(submit)="handleSubmit(myForm.value)"
>
<button type="submit">Submit</button>
</form>

Component:

1
2
3
public handleSubmit(formValue: any) {
console.log(formValue);
}

Add Fields

1
2
3
4
5
6
7
8
9
<div>
<label for="usernameInput">Username</label>
<input type="text"
id="usernameInput"
name="username"
ngModel
placeholder="enter username"
>
</div>

Available form properties

  • myForm.errors
  • myForm.dirty
  • myForm.valid

Notes and Explanations

  • NgForm automatically binds to a <form> element and creates a FormGroup object called ngForm.
    With #myForm="ngForm" we actually grab this FormGroup object and bind it to a template variable.

  • NgForm automatically creates the (submit) event emitter.

  • NgModel directive creates automatically a FormControl named with the name value.

  • NgModel creates a one-way data bind (model -> view)


Reactive Forms

A quick reference guide on how to setup.

How to setup

import ReactiveFormsModule

1
2
3
4
5
6
7
8
import { ReactiveFormsModule } from '@angular/forms';
@NgModule ({
...
imports: [
ReactiveFormsModule,
...
]
})

define a FormGroup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
...

export FormComponent {

public userForm: FormGroup;
public

constructor(
private fb: FormBuilder
){
this.userForm = this.fb.group({
username: ['user1', Validators.required] //default value = 'user1', Optional validator = required
});

this.username = this.userForm.controls['username']; //optional. Allows easier reference in HTML template

}

public onSubmit(formValue: any) {
console.log('You submitted: ', formValue);
}


}

Create form and bind controls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<form [formGroup]="userForm"
(ngSubmit)="onSubmit(userForm.value)"
>

<label for="usernameElm">Username</label>
<input type="text"
id="usernameElm"
placeholder="enter username here"
[formControl]="userForm.controls['username']"
>

<!--
optionally you could bing the element to the Control property:
[formControl]="username"
-->

<button type="submit">Submit</button>

</form>

Handling errors and validation

Checking whole form validity example

1
2
<button [disabled]="!userForm.valid"
type="submit>Submit</button>

Detecting errors in controls

1
2
<input [class.error]="!userForm.controls['username'].valid"
...

Handling specific validation errors

1
2
3
<div *ngIf="userForm.controls['username'].hasError('required')">
You must enter a username
</div>

Good practice: see if control is touched

1
2
3
4
5
<!-- we are using the reference variable for leaner code -->

<input [class.error]="!username.valid && username.touched"

<div *ngIf="username.hasError('required') && username.touched"

Other validation options

Errors from FormGroup level

You can check for errors of specific field even in FormGroup level:

1
console.log (userForm.hasError('required','username');  // FormGroup.hasError(errorKey, fieldKey)

Custom Validators

Create a function that accepts FormControl object as parameter and returns an error string map {errorKey: boolean} if the error is valid.

Define Validator
Example: Not allow ‘admin’ as username.

1
2
3
4
5
private reservedUsernames (control: FormControl): { [string]: boolean } {
if (control.value === 'admin') {
return {reserved: true};
}
}

Apply Validator

1
2
3
4
5
this.userForm = this.fb.group({
username: ['', Validators.compose(
[Validators.required, this.reservedUsernames]
)]
});

Check in HTML template

1
2
3
<div *ngIf="userForm.controls['username'].hasError('reserved')">
Username reserved and cannot be used.
</div>

Monitor changes

There is an available Observable valueChanges in both FormGroup and FormControl classes.

1
2
3
4
5
6
7
this.userForm.valueChanges.subscribe(
(value: any) => console.log('Form Value changed to', value)
);

this.userForm.controls['usernames'].valueChanges.subscribe(
(value: string) => console.log('Current username: ', value);
);

2-way data bind

If you need to… you can bind an input control value to an external variable.

Component class:

1
2
3
4
5
6
public currentUsername: string;

//example of setting the value of currentUsername
public suggest() {
this.currentUsername = 'user'+Math.floor((Math.random() * 10) + 1);
}

HTML template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
<!-- ngModel binds the input element value to variable
<input type="text"
placeholder="enter username here"
id="usernameElem"
[formControl]="userForm.controls['username']"
[(ngModel)]="currentUsername"
>
...
<!-- the currentUsername reflects the value of input element -->
<div> Current Value: {{currentUser}}</div>

<!-- setting the currentUsername reflects the value back to input element -->
<button (click)="suggest()">Suggest a username</button>

Note: ngModel binds the input element value to variable. We still have to use [formControl] to bind the input element with the rest of our FormGroup and make it part of it (validation, etc).

Notes

  • When Angular sees [formGroup] in a <form> element, it does not automatically bind the ngForm FormGroup.

Captain out…

Setup Bootstrap in Angular Project

Here follow instructions on how to setup Bootstrap in your Angular project.

The instructions below can be found in my Angular Gist.


The instructions here are based on this post…
https://medium.com/codingthesmartway-com-blog/using-bootstrap-with-angular-c83c3cee3f4a

The method here is using Bootstrap 4 (beta.2) with ng-bootstrap package.

Install ng-bootstrap

1
npm install --save @ng-bootstrap/ng-bootstrap

Install bootstrap 4

1
npm install --save bootstrap@4.0.0-beta-2

Add files to angular-cli.json

1
2
3
4
5
6
7
"styles": [
"styles.css",
"../node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
"../node_modules/bootstrap/dist/js/bootstrap.min.js"
],

Import NgbModule
Import NgbModule in app.component

1
2
3
4
5
6
7
8
9
10
11
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

...

@NgModule({
declarations: [AppComponent, ...],
imports: [NgbModule.forRoot(), ...],
bootstrap: [AppComponent]
})
export class AppModule {
}

(Optionally) Import in other modules
If you need to import in other modules, import without the .forRoot() method:

1
2
3
4
5
6
@NgModule({
declarations: [OtherComponent, ...],
imports: [NgbModule, ...]
})
export class OtherModule {
}

Captain out