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

Angular tips n tricks Gist

Started a gist with Angular tips-n-tricks cheatseat, as I find them in my path. Sharing it with you.

Tips and Tricks can be found here:
Killerchip’s Angular notes Gist

Here’s the opening tips:

Simple input without complex forms

The following is a quick example of how to simply grab input from user, without using Angular forms and form builder.
Of course it is for very simple cases.

HTML Template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<form>

<label for="firstName">First Name:</title>
<input name="firstName" #firstName> <!-- create a template variable that binds to input element -->

<br>

<label for="lastName">Last Name</title>
<input name="lastName" #lastName>

<button (click)="submitName(firstName,lastName)"> <!-- pass the input element to click event -->
Submit
</button>
</form>

In Component Class

1
2
3
4
5
submitName(firstName: HTMLInputElement, lastName: HTMLInputElement) {
// Then we can directly refer to elements as objects.
console.log(`Submiting: ${firstName.value} ${lastName.value}`);
return false; //returning false will stop event propagation, thus page does not reload.
}

Component adds class to itself

When defining a component you can define a class (and other attribute) on its own host element.

For example if you define the <my-component></my-component component, you can define it to have a class like <my-component class="my-component-class"> directly from compoent code. So you are not dependent on the “user” of this component to define the class.

This can be done with the @HostBinding

1
2
3
4
5
export class MyComponent {

@HostBinding('attr.class') cssClass='my-component-class'

}

Use a link as button

Sometimes is more ‘elegant’ to use an linke (href) as button instead of a button, even if we don’t want actually to navigate away from the page. But even with no URL in the href element the page ‘refreshes’. This is how to avoid this.

If the click-handler function returns false value, then this stops propagation of the click further.

HTML Template

1
<a href (click)="clickMe()">Click Me!</a>

Component code

1
2
3
4
public clickMe(): boolean {
console.log ('I will do something with the click');
return false; // returning false will stop click event from propagating
}

Captain Out…

ngx-translate-extract-csv v1.1.1

I’m happy to announce that v1.1.1 of ngx-translate-extract-csv is now out.

In this version you can define a custom separator for the CSV files.

See: https://github.com/killerchip/ngx-translate-extract-csv/blob/master/CHANGELOG.md

About ngx-translate-extract-csv

ngx-translate-extract-csv is a tool that becomes handy when doing Localization of Angular projects using ngx-translate library and the ngx-translate-extract tool. It will gather up the language .json files created into a single CSV file that can be easily imported to XLS.

Installation

1
npm install ngx-translate-extract-csv --save-dev

More info at:
https://www.npmjs.com/package/ngx-translate-extract-csv

Captain Out…

ngx-translate-extract-csv v1.0.0

I’m happy to announce that v1.0.0 of ngx-translate-extract-csv is now out.

ngx-translate-extract-csv is a tool that becomes handy when doing Localization of Angular projects using ngx-translate library and the ngx-translate-extract tool. It will gather up the language .json files created into a single CSV file that can be easily imported to XLS.

In v1.0.0 it now has the reverse operation. Split the “translated” CSV files into separate .json files to be used by the Angular app.

Installation

1
npm install ngx-translate-extract-csv --save-dev

More info at:
https://www.npmjs.com/package/ngx-translate-extract-csv

Captain Out…

Practice Building Android Layouts / Udacity course Lesson 4 notes

Sharing my draft notes from Udacity’s Android Basics - Lesson 4 online course

The course actually gave instructions and a challenge of building your own card as android app using Android Studio and deploying in a real Android device.

Here are my notes which can be found also at My GistHub:

Preparing to work with Android

From Design to Code

  1. Identify the Views to use (TextView? ImageView?)
  2. Position the Views (ViewGroups and positioning)
  3. Style the views (Fonts, colors, sizes, etc.)

Referencing an image

Images are saved in app/drawable folder. E.g androidparty.jpg.

Then they can be referenced as source from the ImageView.

1
2
3
<ImageView
...
android:src="@drawable/androidparty" />

Note: No need to add the extension of the image file.

A Sample ImageView

Use it as a quick reference for ImageView properties:

1
2
3
4
5
6
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/myImage"
/>

A Sample TextView

Use it as a quick reference for TextView properties:

1
2
3
4
5
6
7
8
9
10
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginLeft="16dp"
android:text="Hello World"
android:fontFamily="sans-serif-light"
android:textColor="@android:color/white"
android:textSize="32sp"
/>

Draw Your Own Card challenge

Here’s the reference to my own card that I created as part of the practical challenge of this lesson:
https://discussions.udacity.com/t/make-your-own-card/19643/6766

I actually created a Memory card for my son Orestis to remember the trip we took with “Mountzouris” Locomotive train last summer (2017).

Captain Out…