Youtube channel !

Be sure to visit my youtube channel

Tuesday, December 31, 2019

Angular 8 - Working with reactive forms

Here is a fully functional component featuring dynamic creation of form elements and filling them with server data. For more information on modern development with Angular you can visit this course.


import { Component, OnInit } from '@angular/core';
import { FormGroup, FormArray, Validators, FormBuilder } from '@angular/forms';
let emailRegex = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$";


// initial server data mockup
let serverData =
{
"users": [
{
"firstName": "server:John",
"lastName": "Bernard",
"email": "john@test.com"
},
{
"firstName": "server:Anna",
"lastName": "Parker",
"email": "anna@test.com"
}
],
"date": 1577788750829
};

@Component({
selector: 'app-root',
styleUrls: ['./app.component.scss'],
template: `
<form [formGroup]="usersForm">
<!-- digg deeper to get the users controls (they are also controls of usersForm-->
<!-- usersForm['controls'].users['controls']; -->
{{ usersForm.get('date').value | date}}
<ng-container *ngFor="let userFromGroup of usersForm.get('users')['controls']; let i = index" >
<div class="formgroup" [formGroup]="userFromGroup"> <!-- binds formgroup from the loop -->
{{i}}
<!-- get values inside the fg object to bind to controls -->
<label>First name:<input type="text" formControlName="firstName"></label>
<label>Last name:<input type="text" formControlName="lastName"></label>
<label>Email:<input type="text" formControlName="email"></label>
<button type="button" class="remove" (click)="removeControl(i)">x</button>
</div>
</ng-container>
</form>
<button type="button" (click)="addNew()">Add new</button>
<button type="button" (click)="setValues()">Set values</button>
<button type="button" (click)="patchValues()">Patch values</button>
<button type="button" (click)="getServerData()">Get from server</button>
{{usersForm.value|json}}
`
})


export class AppComponent implements OnInit {
constructor(private fb: FormBuilder) { }

public usersForm: FormGroup;

ngOnInit() {
this.usersForm = this.fb.group({ // get inside the main form group
users: this.fb.array([ // get inside the array of form control groups
this.fb.group({ // the controls array[0]
firstName: ['user 1', { validators: Validators.required }],
lastName: ['', { validators: Validators.required }],
email: ['', { validators: Validators.pattern(emailRegex) }]
}),
this.fb.group({ // the controls array[1]
firstName: ['user 2', { validators: Validators.required }],
lastName: ['', { validators: Validators.required }],
email: ['', { validators: Validators.pattern(emailRegex) }]
})
]),
date: Date.now()
});
}

removeControl(i) {
let uForm = this.usersForm.get('users') as FormArray;
// we cast to FormArray because of:
// Property 'removeAt' does not exist on type 'AbstractControl
uForm.removeAt(1);
}

patchValues() {
let uForm = this.usersForm.get('users') as FormArray;
uForm.patchValue([
{ email: 'mock@mail.com' }
]);
uForm.controls[0].patchValue(
{ email: 'mock@mail.com' }
);
}

setValues() { // strictly setting data
let formUsers = this.usersForm.get('users') as FormArray;
// foreach of the users
formUsers.setValue([
{
firstName: 'John',
lastName: 'Bernard',
email: 'john@test.com'
},
{
firstName: 'Anna',
lastName: 'Parker',
email: 'anna@test.com'
}
]);
}


getServerData() {
this.usersForm = this.fb.group({
users: this.fb.array([]),
// you can also set initial formgroup inside if you like
/*
users: this.fb.array(
serverData.users.map(u =>
this.fb.group({ // the users from the server
firstName: [u.firstName, { validators: Validators.required }],
lastName: [u.lastName, { validators: Validators.required }],
email: [u.email, { validators: Validators.pattern(emailRegex) }]
})
)),
*/
date: Date.now()
})

let usersArray = this.usersForm.get('users') as FormArray;

serverData.users.map(u => { // for each of the users from the server
usersArray.push( // push new controls into the usersArray
this.fb.group({ // set the controls
firstName: [u.firstName, { validators: Validators.required }],
lastName: [u.lastName, { validators: Validators.required }],
email: [u.email, { validators: Validators.pattern(emailRegex) }]
})
);
});
}

getInfo() {
let uForm = this.usersForm.get('users') as FormArray;
let fcontrol = uForm.controls[0].get('firstName').value;
console.log(fcontrol);
}

addNew() {
let formsArray = this.usersForm.get('users') as FormArray;
let arrayLen = formsArray.length;
let newarrayGroup =
this.fb.group({ // the controls
firstName: ['user 3', { validators: Validators.required }],
lastName: ['', { validators: Validators.required }],
email: ['', { validators: Validators.pattern(emailRegex) }]
});
formsArray.insert(arrayLen, newarrayGroup);
}
}

Congratulations! You can further take a look at more examples in the course!

Sunday, December 29, 2019

Star rating script - JavaScript and CSS

There is a whole course on how to achieve star rating using PHP and JavaScript, and here is a simple way consisting of CSS and JavaScript only:


<style>
.rating {
display: flex;
padding: 0;
margin: 0;
}

.rating li {
list-style-type: none
}

.rating-item {
border: 1px solid #fff;
cursor: pointer;
font-size:2em;
color: yellow;
}

/* initial: make all stars full */
.rating-item::before {
content: "\2605";
}

/* make until the clicked star (the rest) empty */
.rating-item.active ~ .rating-item::before {
content: "\2606";
}

/* on hover make all full */
.rating:hover .rating-item::before {
content: "\2605";
}

/* make until the hovered (the rest) empty */
.rating-item:hover ~ .rating-item::before {
content: "\2606";
}

</style>

&lt!--html markup-->

<ul class="rating">
<li class="rating-item" data-rate="1"></li>
<li class="rating-item active" data-rate="2"></li>
<li class="rating-item" data-rate="3"></li>
<li class="rating-item" data-rate="4"></li>
<li class="rating-item" data-rate="5"></li>
</ul>


<script>
const container = document.querySelector('.rating');
const items = container.querySelectorAll('.rating-item')
container.onclick = e => {
const elClass = e.target.classList;
// change the rating if the user clicks on a different star
if (!elClass.contains('active')) {
items.forEach( // reset the active class on the star
item => item.classList.remove('active')
);
console.log(e.target.getAttribute("data-rate"));
elClass.add('active'); // add active class to the clicked star
}
};
</script>

Congratulations !

Wednesday, December 25, 2019

Sorted to do list in JavaScript

JavaScript is a wonderful language as you may discover in the JavaScript for beginners - learn by doing course.

Here is a short working example on how we can easily sort todos saved inside an array:

The initial HTML markup:
<input id="myInput" type="text" /><button id="Add"> Add new LI </button>
<ul class="todoList"></ul>

<script>
// initial array to hold the todos
const todos = [];

// we have helper function for doing the actual sorting
const doSort = (todos) => {
return todos
// .map(todo => todo.toLowerCase()) // make all the items lowercase
.sort((a, b) => {
// compare 2 words letter by letter
if (a.value > b.value) { return 1; } // when the first letter is after the second
if (a.value < b.value) { return -1; } // when the second letter is before the second
return 0; // if both letters are the same
 }) 
} // attach event listener to the Add button document.querySelector('#Add').addEventListener('click', () => {

// get what's inside the input
const data = document.querySelector('#myInput');

// push the new todo into the todos array
todos.push(data.value);

// create additional helper array with object values and indexes
var mapped = todos.map(
(el, i) => ({ index: i, value: el.toLowerCase() })
);

// sort the todos
const sortedTodos = doSort(mapped)
// restore the originals from the todos array
.map(el => todos[el.index]);

// display the sorted totos
todoList.innerHTML = sortedTodos.map(todo => '<li>' + todo + '</li>').join('');

// clear the value of the input
data.value = '';
});

//get reference to the todoList
const todoList = document.querySelector('.todoList');

</script>

Congratulations!

Resources:

JavaScript for beginners - learn by doing

Thursday, December 19, 2019

To do list in Angular

Here is how to create a simple todo-list in Angular. We will be also using extensively RxJs BehaviorSubject as well as Observables. If you want more information on the techniques used, I advise you to practice with the full Angular for beginners course.




First install angular with: sudo npm i -g @angular/cli
Then create a new todo project: ng new todo
Inside the todo directory create 2 new components and service using the Angular CLI:
ng g c todos
ng g c form
ng g s services/todos

Next, modify the following files:
app.component.html
// to include the new component tags
<div class="container">
<h1>{{appTitle}}</h1>
<app-todos [limit]="3"></app-todos>
<app-form></app-form>
</div>
...
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { TodosComponent } from './todos/todos.component';

// import httpclientmodule to be able to perform http requests
import { HttpClientModule } from '@angular/common/http';

// import forms module to be able to use 2-way [(binding)]
import { FormsModule } from '@angular/forms';
import { FormComponent } from './form/form.component';

@NgModule({
declarations: [
AppComponent,
TodosComponent,
FormComponent
],
// and place it (FormsModule) inside modules to be visible by all derived services and componenets
imports: [
BrowserModule,
HttpClientModule,
FormsModule //  add into imports the formsmodule to be accessible from the components
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

...
form.component.html

<div>
<input type="text" placeholder="Add todo..."
<!-- bind the title input field of the form to the model this.title -->
[(ngModel)]="title"
<!-- allow with enter to be able to run addTodo() -->
(keydown.enter)="addTodo()" />
<button (click)="addTodo()">Add todo</button>
</div>

...
form.component.ts

import { Component } from '@angular/core';
// import both the interface Todo as well as the TodoService
import { Todo, TodosService } from '../services/todos.service';

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

export class FormComponent implements OnInit {
// title will be bound to the title input of the form
title: string = '';

// use dependency injection to inject the TodosService
constructor(private todosService: TodosService) { }

addTodo() {
// construct todo object and add it using the service
const todo: Todo = {
id: Date.now(),
title: this.title,
complete: false,
}
this.todosService.addTodo(todo);
}
}
...


todos.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { TodosService, Todo } from '../services/todos.service';
import { Observable } from 'rxjs';

@Component({
selector: 'app-todos',
templateUrl: './todos.component.html',
styleUrls: ['./todos.component.scss']
})
export class TodosComponent implements OnInit {

constructor(private todosService: TodosService) { }
private fetchData$: Observable<boolean>;
private todos$: Observable<Todo[]>;

// receive the limit from the parent(appcomponent) via input
@Input() limit: number;

ngOnInit() {
// initialize the both observables
this.fetchData$ = this.todosService.getTodos(this.limit);
this.todos$ = this.todosService.todos$;
}

// call the service
onChange(id: number) {
this.todosService.onToggle(id);
}

// call the service
removeTodo(id: number) {
this.todosService.removeTodo(id);
}

}

...

todos.component.html

<!-- mainly to fetch the http json data & setup this.todos -->
<div *ngIf="(fetchData$ | async ); else errorFetch"></div>

<!-- get the observable data and spread it on the page -->
<ul *ngIf="(todos$ | async ) as todos; else loading">
<li *ngFor="let todo of todos; let i = index">
<span [class.done]="todo.complete">
<input type="checkbox" [checked]="todo.complete" (change)="onChange(todo.id)">
<!--  bind [checked] to the state complete of todo
 pass todo.id on(change) event,
and run onChange function from the .ts file
-->
{{i + 1}} {{todo.title}}
</span>
<small>{{todo.date | date}}</small>
<!-- use pipe date to format the data: you can see other pipes from API/datepipe on angular website -->
<button class="remove" (click)="removeTodo(todo.id)">×</button>
</li>
</ul>

<ng-template #loading> Loading, please wait ...</ng-template>
<ng-template #errorFetch> There is an error while loading the data, please try again ...</ng-template>

...

todos.service.ts

import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject, of } from 'rxjs';
import { tap, map, catchError } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';

// create interface with the structure of a simple todo item
export interface Todo {
id: number
title: string
complete: boolean
}

@Injectable({
providedIn: 'root'
})
export class TodosService {

constructor(private http: HttpClient) { }
public todos: Todo[] = [];
private subject = new BehaviorSubject<Todo[]>([]); // to be able to retain the last emitted version !
public todos$ = this.subject.asObservable();

getTodos(limit: number): Observable<boolean> {

return this.http.get<Todo[]>(`https://jsonplaceholder.typicode.com/todos?_limit=${limit}`)
//  just return observable of true or false
.pipe(
map((fetchedTodos: Todo[]) => {
// setup the local this.todos member
this.todos = fetchedTodos;
// propagade the todos to subscribers via subject
this.subject.next(this.todos);
return true;
}),
catchError(err => {
alert(err.message);
return of(false);
})
);
}

onToggle(id: number) {
// get the todo index by the provided ID
const idx = this.todos.findIndex(todo => todo.id === id);
this.todos[idx].complete = !this.todos[idx].complete;
}

removeTodo(id: number) {
this.todos = this.todos.filter(todo => { return todo.id !== id }
);

// propagade the updated data back to all observables
this.subject.next(this.todos);
}

addTodo(todo: Todo) {
this.todos = [...this.todos, todo];
this.subject.next(this.todos);
}
}

Now you can run: ng serve
and browse: http://127.0.0.1:4200

Congratulations and enjoy learning Angular.

Sunday, December 15, 2019

Immutable JavaScript / ES6 to do list

Here is my interpretation of how to create a simple JavaScript ToDo list based on immutability and states. You may also enjoy the JavaScript for beginners - learn by doing course.

So far there are only add and delete actions created:
<input id="myInput" type="text" /><button id="Add">Add new Todo </button>
<div id="app"></div>

<script>
// state object
var state = {
name: 'todo list 1',
todos: [{ id: 1, data: 'first todo <button class="delete" data-id="1">X</button>' }]
};

//we pass the todo as object({with id, and the todo_info})
function applyAction(todo, action) {
switch (action) {
case 'add':
return [...state.todos, todo];
break
case 'remove':
//here remove it from index using todo.id
return state.todos
.filter(dbTodo => dbTodo.id !== todo.id);
break
case 'update':
// return [
// ...state.todos
// .filter(dbTodo => dbTodo.id !== todo.id), // first we remove the soon to be updated todo
// todo // then we add the updated version to the end of the array
// ];
return state.todos.map(
dbTodo =>
// whe check when we have match on id, we use todo, otherwise we leave the original dbTodo
dbTodo.id === todo.id ? todo : dbTodo
);
break
}
}


let renderTodos = todos => {
return `
<ul>${state.todos.map(todo => `
<li>${todo.data}</li>
`)} `;
}


// The state-based UI rendering
var render = state => {
var html = `
<h1>
${state.name}</h1>
${ state.todos.length < 1 ? 'Please add a todo' : ''} ${renderTodos(state.todos)} `; app.innerHTML = html; }; let setState = obj => {
// update the state with passed object
Object.keys(obj).forEach(key => {
state[key] = obj[key];
});
//update the render
render(state);
}

// Add todo event listener
document.querySelector('#Add').addEventListener('click', () => {
// get what's inside the input
const inputData = document.querySelector('#myInput');
if (inputData.value == '') return;
// just mapping into new array only the ids, then doing max on them
let lastId = (state.todos.length > 0) ? Math.max(...state.todos.map(todo => todo.id)) : 0;
current_todo = { id: ++lastId, data: `${inputData.value} <button class="delete" data-id="${lastId}">X</button>` }
setState({ //save the modified state and show the result
todos: applyAction(current_todo, 'add') // apply the add modification to the state
});
// clear the value inside the input
inputData.value = '';
});

// Remove todo event listener
document.querySelector('#app').addEventListener('click', (e) => {
if (e.target.classList.contains('delete')) {
const id = +e.target.getAttribute('data-id'); // convert the string data-id to number
const currentTodo = { id };
setState({ // save the modified state and show the result
todos: applyAction(currentTodo, 'remove') // apply the add modification to the state
});
}
});

// Render the initial UI
var app = document.querySelector('#app'); render(state);
</script>

Congratulations ! 

Resources:

JavaScript for beginners - learn by doing

Subscribe To My Channel for updates

Things to do after install Fedora 43

--- ##### SYSTEM UPDATES & FIRMWARE   sudo dnf upgrade --refresh -y   ##### Check Firmware (Only if supported hardware is found)   fwupd...