PRA09ENG.rst

Programming Laboratory

Front End

Attention! The code for this class is on the branch Angular in the repository https://github.com/WitMar/PRA2018-2019/tree/Angular and final code at https://github.com/WitMar/PRA2018-2019/tree/AngularFinal.

Author of the code : Michał Kurkowski

Angular

AngularJS - an open JavaScript framework library, supported and endorsed by Google, supporting the creation and development of web applications on a single page (single page application). The library's task is to implement the Model-View-Controller (MVC) pattern for web applications to facilitate their development and testing.

The main difference between the Single Page Application (SPA) and standard web applications is a one-page interface and the transfer of logic from the server to the client. The entire application logic is written on the client side in JavaScript and executed in the browser. The HTML, JavaScript and CSS code is downloaded once when the application is started, while the remaining required resources will be downloaded dynamically when needed at the moment.

The easiest wat to create a new project is to use Angular CLI (command line interface) following the instructions given on the Angular4 QuickStart page:

For this you will need NodeJS in the minimum version 6.9.x and npm minimum 3.x.x.

Node.js is a programming environment designed to create highly scalable web applications, especially web servers written in JavaScript. Node.js allows you to create event-driven applications that use an asynchronous I/O system.

On Linux systems, search if you have already installed nodejs and npm packages, eg. the installation for Ubuntu looks like this:

sudo apt-get install nodejs npm

The work environment with javascript can be any text editor or a more advanced environment such as very similar to IntelliJ (it is very helpful in searching for syntax and syntax errors in the code).

Typescript

The Angular4 is written in TypeScript. TypeScript is a complex JavaScript, transpilated (compilation of source code into different source code) to JavaScript. It contains the same features as JavaScript and some additions, including strong typing, interfaces and object-based programming based on classes (constructors, inheritance). Its popularity grows with the increasing complexity of web applications. You can get to know the TypeScript action starting from the TypeScript Playground page:

examples in Polish:

What is strong typing? It is an indication of what type of data should be stored under a given variable. Thanks to this, basic errors can be detected already at the compilation stage. In JavaScript, the following types exist: string, number, boolean, object, function, undefined. It is particularly important to indicate the types of parameters in the function declarations. Remember that to reference class attributes every time you have to use

this.variable_name

Variables are defined by the keyword let. If we do not want to allow changes in the variable value, we can use the keyword const instead of let. Variables defined by let are visible within the block in which we defined them. Code examples:

//variables
let name : string = 'Jan';
let decimal: number = 61;
let cyfry: number[] = [1, 2, 3];
let t: [string, number];
t = ['Jan', 24]; // Poprawne

//interfaces
interface Message {
    text: string;
}
function showAlert(msg: Message) {
    console.log(msg.text);
}
showAlert({text : 'No data!'});

//classes
class Employee {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    fire() {
        alert('You are fired, ' + this.name);
    }
}

let employee = new Employee("Jan");
employee.fire();

Installation at home

The application requires the installation of a node on the computer: https://nodejs.org/en/

A set of modules and tools

Then, from the terminal level, in the project directory the following commands shall be invoked:

npm install @angular/cli

npm install json-server

npm install

Errors

If I see the error:

npm ERR! ENOTFOUND code
npm ERR! errno ENOTFOUND
npm ERR! network request to https://registry.npmjs.org/inline-process-browser/-/inline-process-browser-1.0.0.tgz failed, reason: getaddrinfo ENOTFOUND registry.npmjs.org registry.npmjs.org:443
npm ERR! network This is a problem related to network connectivity.
npm ERR! network In most cases you are behind or have bad network settings.
npm ERR! network
npm ERR! network If you are behind a proxy, please make sure that the
npm ERR! network 'proxy' config is set properly. See: 'npm help config'

Try one of the following:

Temporary workaround: add 104.16.20.35 registry.npmjs.org to /etc/hosts file

or

npm config set registry http://registry.npmjs.org

Installation - windows

You need to install packages (preferably on polygon).

P:

mkdir s[numer_indeksu]

cd s[numer_indeksu]

Download the code from Git repository to created directory and run

npm install @angular/cli

npm install json-server

cd node_modules/.bin/

ng serve

Copy file db.json from main directory to node_modules/.bin. Run in separate terminal

json-server --watch db.json

The application will be available under localhost: 4200, server under localhost: 3000.

The application is built live, during each change in source files. All you have to do is save the file and then changes are immediately applied.

The structure of the project

The main project directory, major files:

angular.json - the main configuration file of the project,
gitignore - tells you which files should not land in the repository (you do not want to even have node_modules or configuration files for IDE, if everyone can use another one),
package.json - a set of all packages, dependencies used in the project.

Catalogs and files:

src - source files for our project,
index.html - default file with HTML page,
main.ts - initialization of typescript,
styles.css - the first style file for our initial application,
favicon.ico - application icon, which will appear in the tab (tab) of the browser (next to the name),
polyfills.ts - browsers differ from each other. The same function used in one may not work in another or act differently. Polyfills are codes that implement "shortcomings" in browsers.

The heart of the project can be found in the subdirectory:

app/ - here you will find our application, all components, views. We will only be interested in this catalog.
app.module.ts - the most important file of our application, here you include all components, modules, websites that make up the application (in declarations, imports and providers respectively). Using Angular CLI when creating them - they are automatically added to this file.

Newly created services and components are not normally attached to any module. However, this can be easily done, for example, by registering application module, in the file src/app/app.module.ts.

Model

It is defined in the catalog src/app/models.

The question mark at the parameter means that it is optional. We check in the code whether the object has been transferred and whether it has id, name, finished fields.

The rest of the syntax is similar to Java with the difference that the types are defined after the colon and not before the variable / method name.

constructor (included? any) {
    this.id = (obj && obj.id) || Math.floor (Math.random () * 1000);
    this.name = (obj && obj.name) || '';
    this.finished = (obj && obj.finished) || false;
}

We must define the same model as in the backend server.

View

The view is defined in the html class. The main file is index.html, in which we embed the app-root component defined in app.component.

If in our class in the ts file we define a variable, then we can refer to its value in the html file by using

{{title}}

As mentioned above, we build pages from HTML tags and component tags (names from component selector - see below).

Service

The service defined in the file /service/todo.service.ts is responsible for downloading data from the server and for processing them (eg. sorting).

It refers directly to the server's REST API methods.

The service class is singleton (ie. one instance of it is initialized). Service is passed to components through the dependency injection mechanism, by definition in the component's constructor. From the service side we have the annotation @Injectable () before the definition of the class.

@Injectable ()
export class TodoService {
    constructor (private http: HttpClient) {}
}

In our case, we define the service by adding a module from the @angular/common/http library for http queries. On such an object we can execute HTTP requests, specifying the method and the path to be called.

this.http.delete(`${apiUrl}/todos/${todo.getId()}`);

As the second query parameter, we can pass the body of an http query.

this.http.post(`${apiUrl}/todos`, todo);

The next parameters depend on the query and include such things as the type of response, parameters, etc. We can add a curly bracket and specify only selected parameters.

For example, forwarding the http header:

let headers = new HttpHeaders();
headers = headers.set('h1', 'v1').set('h2','v2');

http.get('someurl',{
    headers: {'header1':'value1','header2':'value2'}
});

On the query, we can perform subsequent operations on the principle of "stream" processing. HTTP query returns type Observable serving as a collection for storing values ​​in the Observer pattern (ie. dynamically updated). To recover data from the observable type, we have to "register" in it, i.e. execute the subscribe() method on it. In this method, we define the behavior of values. The result of the query is available in the variable res.

this.http.post(`${apiUrl}/todos`, todo).subscribe(res => {
  console.log(res);

We can rewrite the code from the repository which is adding an item to the list (replacing the streaming function map with a loop):

private getTodos() {
    this.todoService.getAllTodos().subscribe(res => {
        for (let i = 0; i < res.length; i++) {
            this.todos.push(new Todo(res[i]));
        }
    });
}

At subscribe we have a paremeter res - answer and parameter with error error - if the query fails. We can write the second one to the console to see errors (or to bend them in some other way).

private getTodos() {
    this.todoService.getAllTodos().subscribe(res => {
        for (let i = 0; i < res.length; i++) {
            this.todos.push(new Todo(res[i]));
        }
    },
        error => {
            console.log(error);
            window.alert('Błąd');
        }
    );
}

It's a good idea to share services between various functions that you want to fulfill, i.e. a separate login service, employee management, plan management, etc.

Components

The basic elements of Angular4 project are components. Component controls a part of the page. After creating the project by Angular CLI we create the main component: app.component, which controls and contains all other components. Each component has its own class, html page and css style.

A single component will consist of:

app.component.ts - responsible for the component's logic, its operation,
app.component.html - stores its structure,
app.component.css - and here we style it.
app.component.specs.ts - file with component tests (optional).

In the *. ts ** file, the upper part of the component is called the **component decorator. It is here that the place (element of the DOM tree) of the component rendering (selector), the appearance of the component (templateUrl) and its styles (styleUrls) are indicated.

The name from the selector is also used when referring to a given component from a different component. For example, look at the file index.html that contains the reference to the app-root component. App-root is the main component of our Single Page Application.

An example of a class is defined below, constructor is optional

export class AddTodoComponent implements OnInit {

    newTodoName: String;

    constructor(private todoService: TodoService) {}

    ngOnInit() {
        this.newTodoName = '';
}

The List component in our project have Service as a parameter in constructor, which is injected automatically.

The component List-Item accepts in the Service constructor, it also has two elements defined for the input type and return type (similar to the function). They are used for communication between components.

@Input() todo: Todo;
@Output() removeItem: EventEmitter<Todo> = new EventEmitter();

Since Angular 2, the parent component has the ability to pass data to the child, which may determine the behavior of the component or in general - allow its proper rendering. This is possible thanks to the annotation @Input(). We can define default values ​​if we initialize them in the definition.

The parameter is passed through annotations in HTML, as well as the values ​​of HTML tag parameters:

<component_name variable_name="value"></component_name>

Annotation @Output() allows us to achieve the opposite result - pass the values ​​from the child to the parent - using the event.

@Output() variable_name = new EventEmitter<string>();

EventEmitter is a generic class - a template. Above we have determined that the parameter that will be passed in the form of an argument will be of the string type. Now, to pass the method that will be triggered when the event is emitted, all you need to do is:

<component_name (variable_name)="parent_function_name($event)"></component_name>

To emit an event, we need to call the emit () method of the object variable_name:

this.variable_name.emit ( 'abc');

This will result in calling function child_function_name (that is, the component in which we define our subcomponent) and passing it in the form of an argument - the value 'abc'.

Modules

Modules allow us to divide the application into smaller ones that are easier to manage fragments (they wrap the services and the module just like packages in Java). We have only one module in our project.

Here we define the paths to components (routing), define new services and components.

const ROUTES: Routes = [
    { path: '', redirectTo: 'todo-list', pathMatch: 'full' },
    { path: 'todo-list', component: TodoListComponent },
    { path: 'add-todo', component: AddTodoComponent }
];

@NgModule({
    declarations: [AppComponent, TodoListComponent, TodoItemComponent, AddTodoComponent],
    imports: [BrowserModule, HttpClientModule, FormsModule, RouterModule.forRoot(ROUTES)],
    providers: [TodoService],
    bootstrap: [AppComponent]
})

export class AppModule {}

In const ROUTES we define map between components and domain paths in the application. The first one says to redirect when we do not enter anything on the page (empty path). We use the same path names to define redirections in html files.

Directive

Directives in Angular4 (Angular4 Directives) are a way to add dynamic behavior to HTML. Directives are the most frequently used element of AngularJS and they determine its strength and advantage over other JavaScript frameworks.

Types of directives:

component (Compontent) - is a template with a template.
structural - they change the appearance by adding, removing or changing HTML elements, eg: * ngFor,` * ngIf`
Attribute - change the appearance or behavior of an element, component or other directive

Directives can be created in several different ways. They also have a number of configuration parameters that are responsible for the operation and use of directives.

Angular needs to know where our application starts to use the ng-app directive. We put it as an attribute to the element that will be our application. In our case, it could be the <html> or <body> tag

The expression {{}} otherwise known ngBind is one of the Angular directives. Its purpose is to bind data.

Exercise

Add the ngModule directive to the input field of the form.

To add this directive you need to import a new module. Go to app.module.ts.

Add :

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

and add an entry in imports

imports: [BrowserModule, HttpClientModule, FormsModule, RouterModule.forRoot(ROUTES), ReactiveFormsModule],

The ng-model directive associates the contents of the text field with that in the component.

The relationship is two-way. This means that if I change the contents of the text field, I change the contents of the username. If I change the username value, I will also change the value of this text field.

Add the variable name in todo-list.components.ts

name: String;

Add in todo-list.components.html:

<div>
    Name: <input [(ngModel)]="name">
    <h1>You entered: {{name}}</h1>
</div>

Structural directives are responsible for the HTML view. They shape this view by adding, removing or manipulating DOM objects. Structural directives are easy to recognize - preceded by the * sign, as in the example.

ngIf is the simplest and easiest to understand structural directive. It takes some logical expression and makes the whole block of code on which it is used appear or disappear depending on the value of the expression.

   <p *ngIf="true">
       The expression gives true.
       The paragraph is visible.
   </p>
   <p *ngIf="false">
       The expression gives false.
       The paragraph is hidden.
   </p>

is used to iterate through the collection.
<li *ngFor="let item of items; index as i; even as isEven; odd as isOdd; first as isFirst; last as isLast; trackBy: trackByFn">
        ({{i}}) {{item.name}}
</li>

Where:

The keyword let declares a variable to which we can refer within the element.
When ngFor iterates over a list, sets and changes properties in its own context object. These properties are: index and odd and the special property $implicit
For variables and and odd angular inserts the property values ​​from the context object into them.
The let-item property has not been set. Angular sets the let-item value of $implicit that ngFor uses during iteration

For example, replace the code from the todo-list.component.html file with the following one:

<table class="table table-bordered table-striped">
    <tr>
    <tr *ngFor="let todoski of todos index as i; even as isEven; odd as isOdd">
        <td>
            <div [ngClass]="{'todo__name--finished' : todoski.isFinished() }"><font color="blue" *ngIf="isEven">{{i + 1}}
                {{todoski.getName()}} </font></div>
            <div [ngClass]="{'todo__name--finished' : todoski.isFinished() }"><font color="red" *ngIf="isOdd">{{i + 1}}
                {{todoski.getName()}} </font></div>
        </td>
        <td>
            <input [(ngModel)]="todoski.name">
        </td>
        <td>
            <button (click)=removeTodo(todoski)>Remove</button>
        </td>
        <td>
            <button (click)=save(todoski)>Save</button>
        </td>
        <button class="todo__btn todo__btn--status" (click)="toggleStatus(todoski)"> TOGGLE STATUS</button>
    </tr>
</table>

It allows you to edit field values and delete it without using todo-item elements.

Exercise

Program the "Save" button by copying the appropriate elements (functions) from the Remove button. Add the button in Html, add method which execute uptadeTodo from service.

Change updateTodo method to

updateTodo(todo: Todo) {
    return this.http.put(`${apiUrl}/todos/${todo.getId()}`, todo).subscribe(res => {
        console.log(res);
    });
}

Filters in Angular4

Filters in Angular4 (Angular4 Pipes) are used to convert input data into the desired output format. The following is an example of uppercase letter transformation:

<h1>{{title | uppercase}}</h1>

List of filters:

Refresh

Note that switching between ToDo List and Add Todo angular changes the view by reloading only the relevant components of the page.

Exercise

Add a new ToDo in Postman by doing POST on http: // localhost: 3000 / Todos and in the body passing

{
    "id": 111111222,
    "name": "NEW POST",
    "finished": false
}

Switch between the list view and the add item view, see if a new item appears in the list.

Exercise

Add the date display in the notes list view.

private date: Date;

constructor(obj?: any) {
    this.id = (obj && obj.id) || Math.floor(Math.random() * 1000);
    this.name = (obj && obj.name) || '';
    this.finished = (obj && obj.finished) || false;
    this.date =  new Date(Date.now());
}

Note that the dates are the same for different notes, because when you switch to the list view, notes are created each time.

*

Wykorzystano materiały z:

https://farmastron.pl/nauka-angular4-angular2-czesc-1/

http://10clouds.github.io/acodemy.io/intro/angular/

https://www.nettecode.com/angular-cli-struktura-projektu/

https://www.nafrontendzie.pl/podstawy-angularjs-niezbedne-minimum

http://eluzive.pl/2017/12/10/kurs-angular-14-strukturalne-dyrektywy/

https://kamilmysliwiec.com/kurs-angular-2-komunikacja-input-output