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

in #utopian-io7 years ago (edited)

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

Sort:  

this clearly tutorial what i looking for a long time. hope can help me thank you very much

Changed repository to angular:

Submissions that include a GitHub repository with additional materials (like code samples), should be linked to the repository of the original project discussed in the tutorial and not the supplementary repository created for the contribution. Links to the supplementary repository for the post should be included in the submission post.

Please change your tutorial to follow template provided in the editor:

Failure to apply the templates provided in the editor for each category may lead to rejection. The standard contribution submission templates should be followed when detailing your contribution. However, templates can be edited and extended as long as the formatting is clear.

Hi @laxam. Thanks for approving the tutorial. I've changed the text to include Requirements and Difficulty.

About changing the repo to angular: I would not suggest changing the repo to angular, since angular cli is a bootstrapper and development tool for angular projects. It copies template files from the angular cli repo, so it's easy to start a new project. So in this case angular cli is not the "original project" and the tutorial is not a "supplementary repository" but a whole new project created with tools from angular cli.

Hi @aley. Thanks for including Requirements and Difficulty.

I would argue that this tutorial teaches reader how to use angular and not steemit-posts. If you view it as a new project - there is a development section for such cases. You can send future contributions there if they follow the rules for that section.

@laxam, so in case I would make a tutorial for "how to program in javascript" I would add the javascript language repo as original project? It's a bit confusing, but I guess I got the idea. Thanks.

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Hey @aley I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • This is your first accepted contribution here in Utopian. Welcome!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x

Coin Marketplace

STEEM 0.25
TRX 0.20
JST 0.036
BTC 95982.42
ETH 3487.66
USDT 1.00
SBD 3.47