[Tutorial] How to install Angular CLI and show Steemit Posts in an Angular Web App

In this tutorial you will learn

  • how to install Angular CLI on your machine
  • initialize your app using Angular CLI
  • create a Post class to describe particular posts
  • create a PostDataService service to retrieve posts from steemit
  • use the base AppComponent component to bring it all together and display the steemit posts
  • style your app with CSS
  • push your app to GitHub Pages

You will need an environment capable of runing Node.js and NPM.

This tutorial is suggested for basic to intermediate users.

Before we start, let's take a sneak peek at the end result.

See it in action on GitHub Pages: Steemit Posts or clone the repository from GitHub: Steemit Posts and run it on your machine, or checkout the following screenshot.

Angular App Steemit Posts Listing

Ready? Let’s get started!

Note: Angular starting from version 2, is a completely new framework built on ideas and lessons learned from AngularJS which is version 1.

Install Angular CLI

The most comfortable way to start an Angular app is to use the Angular command line interface (CLI)

To install Angular CLI in your NodeJS environment, open your console and run

> npm install -g @angular/cli

after the installation, the ng command will be globally available on your system.

To verify that the installation went through successfully, run:

> ng version

it will print something like this

Angular CLI: 1.7.2
Node: 9.6.1
OS: linux x64
Angular: 5.2.7

Now Angular CLI is installed on your machine, let’s head over and create your app.

Initialize your app using Angular CLI

The Angular CLI makes it easy to create an application that already works, right out of the box. Run:

> ng new steem-posts

Now, navigate into the project directory

> cd steem-posts

and generate your first class.

Generate the Post class

Angular CLI uses TypeScript, so we can use a class to describe the Post items.

Let’s generate a Post class using Angular CLI

> ng generate class post

which will create

src/app/post.ts

Let's open the new class file and add the logic

export class Post {
  title = '';
  author = '';
  body = '';
  created = '';
  images: Array<string> = [];
  tags: Array<string> = [];
  net_votes = 0;
  pending_payout_value = 0;

  constructor(values = {}) {
    Object.assign(this, values);
  }
}

In the Post class you define that every Post instance will contain these attributes

title: string, title of the post
author: string, author of the post
body: string, the actual content
created: string, creation date of the post
images: array of strings, images belong to the post
tags: array of strings, associated tags
net_votes: number, post upvotes count
pending_payout_value: number, value of the post

The attributes have initial values assigned already, so you know the types by declaration and don't have to specify them additionally. Except images and tags, there you define the array content type.

Along the class attributes, you also define the constructor that adds the ability to define values during instantiation, so you can create new Post instances just like this

let post = new Post({
  title: 'Beyond Awesome!',
  author: 'RhinoTheHamster',
  body: 'Rhino is awesome! He's so awesome! He is... he is beyond awesome! He's bey-awesome!',
  // …
});

Now that you have a descriptive Post class, let’s move on and generate a PostDataService service to retrieve posts from steemit.

Generate the PostDataService service

The PostDataService will handle the API requests and manage the Post items.

Let’s install the steem API using NPM, so we have an interface to the steemit data

> npm install --save steem

and generate the PostDataService service using Angular CLI

> ng generate service post-data

which will create

src/app/post-data.service.spec.ts
src/app/post-data.service.ts

Angular CLI also generated the spec file, which you can use to define unit tests. Let’s skip them for now and move straight to the service file and add the logic we need

import { Injectable } from '@angular/core';
import { api as steemapi } from 'steem';
import { Post } from './post';

@Injectable()
export class PostDataService {

  constructor() { }

  getPosts(): Promise<Post[]> {
    return new Promise((resolve, reject) => {
      steemapi.getDiscussionsByCreated({ limit: 10 }, (error, result) => {
        if (error) {
          reject(error);
        } else {
          const posts = [];
          result.map((post) => {
            const meta = JSON.parse(post.json_metadata);
            posts.push(new Post({
              title: post.title,
              author: post.author,
              body: post.body,
              created: post.created,
              images: meta.image,
              tags: meta.tags,
              net_votes: post.net_votes,
              pending_payout_value: post.pending_payout_value,
            }));
          });
          resolve(posts);
        }
      });
    });
  }
}

Et voilà! Now that we have a PostDataService, let’s move on to the display part.

Hands on the AppComponent component

Angular CLI automatically generated AppComponent for you when you created the project. You can find it’s parts here

src/app/app.component.css
src/app/app.component.html
src/app/app.component.ts
src/app/app.component.spec.ts

Let’s edit src/app/app.component.html and replace the dummy content with

<article *ngFor="let post of posts; let even = even; let odd = odd" [ngClass]="{ odd: odd, even: even }">
  <div class="image" [style.background-image]="'url('+(post.images? post.images[0] : '')+')'"></div>
  <div class="body">
    <h1 [innerText]="post.title"></h1>
    <div class="meta">
      By <a href="https://steemit.com/@{{post.author}}" target="_blank">{{post.author}}</a> &bull; {{post.created}} &bull; ${{post.pending_payout_value}} &bull; {{post.net_votes}} votes
    </div>
    <div class="tags" *ngIf="post.tags.length > 0">
      <a *ngFor="let tag of post.tags" href="https://steemit.com/trending/{{tag}}" target="_blank">{{tag}}</a>
    </div>
    <p [innerText]="(post.body.length > 300)? (post.body | slice:0:300)+'..': (post.body)"></p>
  </div>
</article>

Have a look at Angular’s template syntax and directives. It makes our life a lot easier. Here's an excerpt

  • *ngFor="..." - iterates an array of items and creates elements dynamically from a template element; exports values that can be aliased to local variables: e.g. even: boolean, odd: boolean
  • *ngIf="expression" - conditionally includes a template based on the value of an expression
  • [property]="expression" - sets the property of an element to the value of expression
  • [class.class-name]="expression" - adds class-name to the elements list of CSS class names when the value of expression is truthy
  • [style.property]="expression" - sets an elements CSS style property to the value of expression

Want to know more? Head over to the official Angular template syntax documentation.

Now let's add the logic or the so called expression context. The expression context in this case is the component instance which is an instance of the class AppComponent. So let's dive into the file src/app/app.component.ts and add the logic.

import { Component, OnInit } from '@angular/core';
import { PostDataService } from './post-data.service';
import { Post } from './post';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [PostDataService]
})
export class AppComponent implements OnInit {

  posts: Post[] = [];

  constructor(private postDataService: PostDataService) { }

  public ngOnInit() {
    this.postDataService.getPosts().then((posts) => {
      this.posts = posts;
    }).catch((error) => {
      console.log(error);
    });
  }
}

What do we have here? Let's have look

At first we import the Post class and the PostDataService service and define PostDataService in the providers array of the Compontent decorator, so the dependency injector (DI) of AppComponent knows, it has to create only a single instance whenever we ask for the service.

Wanna learn more about Angular’s dependency injection system? Head over to Angular’s official dependency injection documentation.

Now in the constructor of AppComponent we instruct the instance to provide PostDataService service as an private attribute of the AppComponent instance.

We also added a public property called posts of type Post, declared it's initial value to be an empty array and added the public method ngOnInit() to ask PostDataService to pull in steemit posts and assign it to this.post, overwritting the empty array. Now when Angular initializes AppComponent, ngOnInit() gets executed and the train starts to move :)

Let's try it, run

> ng serve

this will start a local develompent server, which you can access by navigating your browser to http://localhost:4200/

When you change a file, your local develompent server will send a signal to your browser, so it can automatically reload the application.

Ok! Let's check the result.. Missing something? Oh yah! We miss the styling.

Style your app

Angular loads stylesheets which belong to components automatically, so just edit src/app/app.component.css and add

@import url(https://fonts.googleapis.com/css?family=Roboto|Open+Sans);

article {
  display: flex;
  margin: 0 auto 10px;
  height: 300px;
  width: 800px;
  font-family: 'Open Sans', sans-serif;
    font-size: 14px;
  line-height: 21px;
    border-radius: 3px;
    box-shadow: 0 3px 7px -3px rgba(0, 0, 0, 0.3);
}
article .image {
  position: relative;
  width: 300px;
  height: 300px;
  background-image: url('https://placeimg.com/300/300/tech');
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center center;
}
article .body {
  width: 500px;
  padding: 20px;
  overflow: hidden;
  background-color: #fff;
}
article .body h1 {
  margin: 0 0 10px;
  font-family: 'Roboto', sans-serif;
  font-size: 28px;
  font-weight: 700;
  line-height: 32px;
  letter-spacing: 2px;
}
article .body .meta,
article .body .tags {
  font-family: 'Open Sans', sans-serif;
  font-size: 14px;
  font-weight: 400;
  color: #9a9a9a;
}
article .body .tags {
  margin-top: -2px;
}
article .body .tags a::before {
  content: ', ';
}
article .body .tags a:first-child::before {
  content: '';
}
article .body > p {
  position: relative;
  margin: 15px 0 0;
}
article .body > p::before {
  content: "";
  position: absolute;
  top: -10px;
  left: 0;
  height: 5px;
  width: 50px;
  background-color: #689F38;
}
article.odd .image {
  order: 2;
}
article.odd .image::before,
article.even .image::after {
  content: "";
  position: absolute;
  top: 0px;
  width: 22px;
  height: 100%;
  background-color: #fff;
  transform: skewX(-4deg);
}
article.odd .image::before {
  left: -11px;
}
article.even .image::after {
  right: -11px;
}

Now open the application's global stylesheet src/styles.css and add

html, body {
  background-color: #f1f1f1;
}
a {
  color: #689f38;
  text-decoration: none;
}

Great! Save the files and watch the LiveReload system doing it's magic :)

Before we end this tutorial, let's take advantage of an awesome feature of Angular CLI.

Deploy your app to GitHub Pages

With Angular CLI it's super simple to deploy your work. The following command tells Angular CLI to build a static version of your app and save it to the docs/ directory in the root of your project.

> ng build --prod --output-path docs --base-href steemit-posts

Now push your project to GitHub into your repository's master branch and change the settings of this repository to serve from master branch /docs folder by doing this:

  1. On GitHub, navigate to your GitHub Pages site's repository.
  2. Under your repository name, click Settings.
  3. Scroll to "GitHub Pages" and use the Select source drop-down menu to select master branch /docs folder as your GitHub Pages publishing source.

Note: Need more input on GitHub Pages and how to configure a publishing source for GitHub Pages? Read Configuring a publishing source for GitHub Pages

When all goes right, you should now see your new app hosted on GitHub Pages, like this

https://learnsteem.github.io/steemit-posts/

That's bey-awesome! Isnt't it?

That's it :)

Feel free to clone the source of this tutorial from GitHub: Steemit Posts

Thanks for reading, hope you enjoyed it :)



Posted on Utopian.io - Rewarding Open Source Contributors

H2
H3
H4
3 columns
2 columns
1 column
Join the conversation now
Logo
Center