Categories
Angular Material

Angular Material — Badges, Bottom Sheets, and Buttons

Angular Material is a popular UI framework based on Material Design for Angular.

In this article, we’ll look at how to use Angular Material into our Angular project.

Badges

We can add badges into our app with Angular Material.

To add it, we write:

app.module.ts

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatBadgeModule } from '@angular/material/badge';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatBadgeModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

<div>
  <span matBadge="4" matBadgeOverlap="false">Text with a badge</span>
</div>

We add the matBadge and matBadgeOverlap properties to add a badge.

Also, we can make it raised:

app.component.html

<div>
  <span mat-raised matBadge="4" matBadgeOverlap="false">Text with a badge</span>
</div>

Bottom Sheet

A bottom sheet adds a panel to the bottom of the screen.

For example, we can use it by writing:

app.module.ts

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatBottomSheetModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { Component } from '@angular/core';
import { MatBottomSheet, MatBottomSheetRef } from '@angular/material/bottom-sheet';

@Component({
  selector: 'bottom-sheet-overview-example-sheet',
  template: 'bottom sheet',
})
export class BottomSheetOverviewExampleSheet {
  constructor(private _bottomSheetRef: MatBottomSheetRef<BottomSheetOverviewExampleSheet>) {}

  openLink(event: MouseEvent): void {
    this._bottomSheetRef.dismiss();
    event.preventDefault();
  }
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private _bottomSheet: MatBottomSheet) { }

  openBottomSheet(): void {
    this._bottomSheet.open(BottomSheetOverviewExampleSheet);
  }
}

app.component.html

<div>
  <button mat-raised-button (click)="openBottomSheet()">Open file</button>
</div>

We added a button to that calls the openBottomSheet method to open the bottom sheet.

In app.component.ts , we added a BottomSheetOverviewExampleSheet component to show the sheet.

We have the openBottomSheet method to show the sheet.

We inject the _bottomSheet service so that we can open the bottom sheet.

Button

We can add buttons with the MatButtonModule .

For instance, we can write:

app.module.ts

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatButtonModule } from '@angular/material/button';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatButtonModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html

<div>
  <button mat-button color="primary">Primary</button>
  <button mat-raised-button color="primary">Primary</button>
</div>

We add the mat-button and mat-raised-button directives to add Material design buttons.

Conclusion

We can add badges, bottom sheets, and buttons with Angular Material easily.

Categories
Angular Material

Angular Material — Getting Started

Angular Material is a popular UI framework based on Material Design for Angular.

In this article, we’ll look at how to use Angular Material into our Angular project.

Getting Started

We can install Angular Material into an existing project by running:

ng add @angular/material

Then we run:

ng serve

to serve our app.

Autocomplete

Angular comes with an autocomplete component.

To use it, we write:

app.module.ts

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    MatAutocompleteModule,
    MatFormFieldModule,
    ReactiveFormsModule,
    FormsModule,
    MatInputModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  myControl = new FormControl();
  options = [
    { name: 'apple' },
    { name: 'orange' },
    { name: 'grape' }
  ];
  filteredOptions: Observable<any[]>;

  ngOnInit() {
    this.filteredOptions = this.myControl && this.myControl.valueChanges
      .pipe(
        startWith(''),
        map(value => typeof value === 'string' ? value : value.name),
        map(name => name ? this._filter(name) : this.options.slice())
      );
  }

  displayFn(user): string {
    return user && user.name ? user.name : '';
  }

  private _filter(name: string) {
   const filterValue = name.toLowerCase();

   return this.options.filter(option => option.name.toLowerCase().indexOf(filterValue) === 0);
  }
}

app.component.html

<div>
  <form class="example-form">
    <mat-form-field class="example-full-width">
      <mat-label>Assignee</mat-label>
      <input type="text" matInput [formControl]="myControl"
        [matAutocomplete]="auto">
      <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
        <mat-option *ngFor="let option of filteredOptions | async"
          [value]="option">
          {{option.name}}
        </mat-option>
      </mat-autocomplete>
    </mat-form-field>
  </form>
</div>

We add the input and the mat-autocomplete component into the mat-form-field to show the input and the autocomplete.

In app.component.ts , we watch the value of myControl and then do the filtering with the map operator and the filter method.

The displayFn method is bused to display the values for the autocomplete.

We also have to import all the required modules in app.module.ts .

We can also add a custom input into the mat-form-field component.

Also, we defined a FormControl and set it to the input to let us watch the input value.

For instance, we write:

app.component.ts

import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  control = new FormControl();
  fruits: string[] = ['apple', 'orange', 'grape'];
  filteredFruits: Observable<string[]>;

ngOnInit() {
    this.filteredFruits = this.control.valueChanges.pipe(
      startWith(''),
      map(value => this._filter(value))
    );
  }

  private _filter(value: string): string[] {
    const filterValue = this._normalizeValue(value);
    return this.fruits.filter(street => this._normalizeValue(street).includes(filterValue));
  }

  private _normalizeValue(value: string): string {
    return value.toLowerCase().replace(/s/g, '');
  }
}

app.component.html

<div>
  <form class="example-form">
    <input type="text" placeholder="Search a fruit" [formControl]="control"
      [matAutocomplete]="auto">
    <mat-autocomplete #auto="matAutocomplete">
      <mat-option *ngFor="let street of filteredFruits | async"
        [value]="street">
        {{street}}
      </mat-option>
    </mat-autocomplete>
  </form>
</div>

We added our own input into the form instead of a mat-form-field .

Conclusion

We can add an autocomplete with Angular Material.

Categories
JSON

Working with JSON — Using Fetch API with React

JSON stands for JavaScript Object Notation.

It’s a popular data-interchange format that has many uses.

In this article, we’ll take a look at how to use JSON with React and Fetch API.

Making HTTP Requests with React

React is a view library, so it doesn’t come with any way to make HTTP requests.

To do this, we have to use our own HTTP client library.

Most modern browsers today comes with the Fetch API.

We can use it with the useEffect hook.

To use it, we can write:

import React, { useEffect, useState } from "react";

export default function App() {
  const [answer, setAnwser] = useState();

  const getAnswer = async () => {
    const res = await fetch("https://yesno.wtf/api");
    const answer = await res.json();
    setAnwser(answer);
  };

  useEffect(() => {
    getAnswer();
  }, []);
  return <div className="App">{JSON.stringify(answer)}</div>;
}

to make a GET request to an endpoint with the fetch function.

It returns a promise that resolves to the data and then we call json to get the data from the JSON.

Then we call setAnswer to set the answer state.

The empty array in 2nd argument means that we only run the callback when the component mounts.

Also, we can move the getAnswer function into its own hook by writing:

import React, { useEffect, useState } from "react";

const useAnswer = () => {
  const [answer, setAnwser] = useState();

  const getAnswer = async () => {
    const res = await fetch("https://yesno.wtf/api");
    const answer = await res.json();
    setAnwser(answer);
  };

  useEffect(() => {
    getAnswer();
  }, []);
  return answer;
};

export default function App() {
  const answer = useAnswer();
  return <div className="App">{JSON.stringify(answer)}</div>;
}

We move all the logic to get the response from the API endpoint to its own hook.

Then we can keep our component squeaky clean with no side effects.

To make a POST request with the Fetch API, we can write:

import React, { useEffect, useState } from "react";

const makeRequest = async (data) => {
  const res = await fetch("https://jsonplaceholder.typicode.com/todos", {
    method: "POST",
    mode: "cors",
    cache: "no-cache",
    credentials: "same-origin",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify(data)
  });
  const response = await res.json();
  return response;
};

export default function App() {
  const submit = async () => {
    const res = await makeRequest({
      title: "delectus aut autem",
      completed: false
    });
    console.log(res);
  };

  return (
    <>
      <button onClick={submit}>add todo</button>
    </>
  );
}

We added the makeRequest function that calls fetch with the URL to make the request to an an object with the options for our request.

The method property is the request method.

The mode is the request mode. 'cors' makes a cross-origin request.

cache set to 'no-cache' disables cache.

credentials sets the cookies origin.

headers has the HTTP request headers that we want to send.

body has the HTTP request body.

When we click the ‘add todo’ button, then submit function makes the request with the makeRequest function.

Then the response data is returned with the promise returned from the makeRequest function and logged from the console log.

Conclusion

We make HTTP requests in a React app with the Fetch API.

Categories
Redux

Intro to React State Management with React-Redux

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

To store data in a central place for easy accessibility by components, we have to use some state management solutions. React-Redux is a popular choice.

In this article, we’ll look at how to add it to our React app and simple use cases.

Installation

To install the react-redux package, we have install react-redux and its dependency redux .

We can install both by running:

npm install react-redux redux

with NPM or if we use Yarn:

yarn add react-redux redux

Set Up a Redux Store

After installing both packages, we have to set up our Redux store to hold our data.

To do this we write:

import { createStore } from "redux";

function counterReducer(state = 0, action) {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    default:
      return state;
  }
}

const store = createStore(counterReducer);

The code above creates a Redux store by creating the counterReducer reducer function.

The reducer specifies how the app’s state changes in response to actions sent to the store.

Our counterReducer only accepts one action, which is 'INCREMENT' . We respond to the action by returning the state and adding 1 to it.

There’s also a default case to just return the state value as is.

Then we create a store by calling Redux’s createStore function and passing in our reducer.

It returns the store , which we can pass into our React app.

Connecting the Store to our React App

This is where we need the functions of React-Redux.

We can connect the store to our app so we can store our app’s state in the store by using React-Redux’s connect function.

connect takes 2 functions, which are mapStateToProps and mapDispatchToProps . They’re the first and second argument respectively.

First, we can put the whole app together by connecting our store with the React app as follows:

import React from "react";
import { Provider, connect } from "react-redux";
import { createStore } from "redux";
import ReactDOM from "react-dom";

function counterReducer(state = 0, action) {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    default:
      return state;
  }
}

const store = createStore(counterReducer);

class App extends React.Component {
  onClick() {
    this.props.increment();
  }
  render() {
    const { count } = this.props;
    return (
      <>
        <button onClick={this.onClick.bind(this)}>Increment</button>
        <p>{count}</p>
      </>
    );
  }
}

const mapDispatchToProps = dispatch => {
  return {
    increment: () => dispatch({ type: "INCREMENT" })
  };
};
const mapStateToProps = state => ({
  count: state
});

App = connect(
  mapStateToProps,
  mapDispatchToProps
)(App);
const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);

In the code, we have an app that shows a number going up as we click the Increment button.

The first step to connect the store to our React app is to wrap the Provider component around our whole app.

To do this we, wrote the following:

<Provider store={store}>
  <App />
</Provider>

Then we define the mapStateToProps and mapDispatchToProps functions as follows:

const mapDispatchToProps = dispatch => {
  return {
    increment: () => dispatch({ type: "INCREMENT" })
  };
};
const mapStateToProps = state => ({
  count: state
});

In mapStateToProps we return a object to map the state object to a React component prop name as indicated in the property name. The state is the state stored in the Redux store.

So we mapped state to the prop count .

In mapDispatchToProps , we get the dispatch parameter, which is a function used to dispatch our action to the store, and return an object with the name for the function we can access from the props to call and dispatch the action.

So in mapDispatchToProps , increment is our function name. We can call it by running this.props.increment in our App component.

increment is a function that runs dispatch({ type: “INCREMENT” }) .

Then in our App component, we define the onClick method to call this.props.increment to dispatch the ‘INCREMENT’ action to our counterReducer via our Redux store and increase the state by 1.

Then since we have mapStateToProps , the latest value is being observed by App and the latest value is available via this.state.count as indicated in mapStateToProps .

Then when we click the Increment button, we’ll get the number going up by 1.

This will happen with every click.

Conclusion

We can use Redux in our React app by creating a store.

Next, we use the React-Redux Provider component and wrap it around our App entry-point component. We pass our Redux store to the store prop so that we can map the state and dispatch actions to props.

Then connecting it to our store via React Redux’s connect function. We pass in the mapStateToProps and mapDispatchToProps to map the store’s state to our component’s props and map our dispatch function call to our props respectively.

Categories
JSON

Working with JSON — Schemas and CSRF

JSON stands for JavaScript Object Notation.

It’s a popular data-interchange format that has many uses.

In this article, we’ll take a look at how to use JSON.

JSON Schemas

We can check the value of JSON schemas to check if our data types are correct.

Also, we can check if we have the required data.

And we check if the values are in the format that we require.

For example, if we have:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Person",
    "properties": {
        "name": {
            "type": "string"
        },
        "age": {
            "type": "number",
            "description": "Your person's age in years."
        },
        "gender": {
            "type": "string"
        },
        "description": {
            "type": "string"
        }
    },
    "required": [
        "name",
        "age",
        "gender"
    ]
}

then we have a Person schema that has the name with type string .

The age property has type number .

gender is of type string and description is also of type string .

Also, it has the required property that has an array of required properties.

For example, if we have:

{
    "name": "james",
    "age": 2,
    "gender": "male"
}

then it conforms to the schema that we just created above.

We can add more validation to our schema.

We can set the minimum number allowed for age .

For example, we can write:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Person",
    "properties": {
        "name": {
            "type": "string"
        },
        "age": {
            "type": "number",
            "description": "Your person's age in years.",
            "minimum": 0
        },
        "gender": {
            "type": "string"
        },
        "description": {
            "type": "string"
        }
    },
    "required": [
        "name",
        "age",
        "gender"
    ]
}

to set the minimum allowed value for age .

Then if we want to validate an object against our schema, we can go to https://www.jsonschemavalidator.net/.

If we have anything that doesn’t conform to the schema we specified, we’ll see the errors.

We put the schema object on the left side and the JSON object we want to check against on the right side.

JSON Security

Since we’re using JSON to communicate between 2 or more parties, we’ll have to look at security.

Anything that communicates over a network will have security risks.

There are various kinds of attacks that we have to concerned about.

Cross-Site Request Forgery (CSRF)

One kind of attack that we have to worry about is the cross-site request forgery.

This where an attack goes to a site that is already authenticated by a legitimate user.

So the attacker can see the sensitive data that is in the site.

Attackers can gain access to sites that require authentication because cookies are included with requests, so they may be able to intercept them and use them to authenticate.

There’s no way to distinguish between legitimate requests and forged requests unless a CSRF token is used to distinguish between them.

Most web frameworks have protection for this attack built in to stop this attack.

Conclusion

We can validate JSON schemas against our JSON objects to validate our objects with it.

Also, we have to be careful about CSRF attacks to stop attackers from accessing sensitive data with forged requests.