AngularJS против VueJS. Создание трекера Stackoverflow в обеих средах. Часть 1/2

Я считаю, что и AngularJS, и VueJS являются мощными интерфейсными фреймворками, которые значительно повышают производительность и скорость команды разработчиков, но обеспечивают качество кода и удобство сопровождения.

Каждый из них имеет свои сильные стороны, и в зависимости от состава и навыков команды, а также существующей кодовой базы следует выбирать один из них.

Давайте создадим простое приложение для отслеживания StackOverFlow, чтобы понять структуру и уникальные преимущества каждого фреймворка. Наше приложение Stackoverflow поможет ежедневно отслеживать самые популярные вопросы об AngularJS и VueJS в определенных популярных тегах Angular/VuejS.

Это теги stackoverflow, которые нас интересуют:

И Angular, и Vue имеют CLI для разработки приложений. Мы оба можем установить их, используя npm внутри терминала.

  • Установить интерфейс командной строки CLI
# AngularJS
$ npm install -g @angular/cli 

# VueJS
$ npm install -g @vue/cli 
  • Создать шаблон проекта
# AngularJS
$ ng new angular-stack 

# VueJS
$ vue create vue-stack 
  • Запустить живую среду разработки проекта
# AngularJS
$ cd angular-stack && ng serve 

# VueJS
$ cd vue-stack && npm run serve 

Наше приложение Stackoverflow будет включать список из 5 тегов которым мы хотели бы следовать. После того, как пользователи нажмут на каждый тег, мы увидим список активных вопросов относящийся к каждому тегу с ссылка на вопрос stackoverflow.

Угловой стек

Угловая диаграмма стека
Приложение Angular-Stack будет включать 4 модуля: Core, Global, Tags и Questions.

  • Основной модуль: включает все наши услуги (данные для работы в сети и сортировка для сортировки вопросов)

  • Глобальный модуль: включите наш интерфейс тегов и вопросов Angular и другие модули, которыми мы хотели бы поделиться в приложении.

  • Модуль тегов: маршрутизация и компоненты для просмотра тегов.

  • Модуль вопросов: маршрутизация и компонент для представления вопросов.

  • Сгенерируйте наши 4 модуля:

$ ng generate module Tags 
$ ng generate module Questions
$ ng generate module Core
$ ng generate module Global

Глобальный модуль

Мы можем объявить нашу модель данных тегов и вопросов здесь, используя интерфейс

Интерфейс.ts

export interface ITag {
  id: number;
  name: string;
  description: string;
  questionsTotal : number;
}

export interface IQuestion {
  id: number;
  tag: string;
  voteCount: number;
  questionTitle: string;
  questionLink: string;
}

Модуль тегов

Внутри модуля тегов нам нужно добавить:

  • tags.component.html — Разметка компонента в HTML
<h2> {{ title }}</h2>
<!-- Adding Code for List of Angular Tags Below -->

  • tags.component.css — Компонент стиля в CSS
.h2 {
  text-align: left;
  color: blue;
}
  • tags.component.ts — написать логику компонента машинописного текста
# Importing Component and OnInit from Angular Core module
import {Component, OnInit} from '@angular/core';

# Config Component using @Component decorator
@Component({
  selector: 'app-tags',
  templateUrls: './tags.componemt.html',
  styleUrls: './tags.component.css'
});

# Implemnt Component Logic
class TagsComponent implements OnInit {
  <!-- Store our title here -->
  title: string;         
  
  <!--  Store our Tag data here -->
  popularTags: ITag[];    
  
 <!--  Execute on load -->
  ngOnInint(){
    this.title = "Popular AngularJS tags on Stackoverflow "
  }
}
  • tags-routing.module.ts — логика маршрутизации для компонента.
# Importing Angular Core and Router Module
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';

# Import our created TagsComponent
import {TagsComponent} from './tags.component';

# Create route " that serve TagsComponent
const routes: Routes = [
  { path: 'tags', component: TagsComponent}
];

# Import 'routes' and export for accessing from root Route
@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})

export class TagsRoutingModule { }
  • tags.modules.ts — Регистрация внутренних компонентов, модулей
# Importing Core and Common Angular Module
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

# Importing Created TagsRoutingModule and TagsComponent
import { TagsRoutingModule } from './tags-routing.modules';
import { TagsComponent } from './tags.component';


@NgModule({

  # Register Module for Internal Usage
  imports: [
    CommonModule,
    TagsRoutingModule, 
    CoreModule
  ],
  
  # Declare and Export so TagsComponent is accessbile from outside
  declarations: [TagsComponent],
  exports: [TagsComponent]
})

export class TagsModule { }

  • Создайте дочерний компонент TagsList внутри нашей папки Tags с помощью нашего Angular CLI:
$ ng generate component tags/tags-list
  • Добавление кода компонента Tags-List в файл ts, html и css

теги-list.component.html

<br/>
<br/>

<!-- Create Table to present tags data -->
<table class="table table-hover">

    <!-- Table Header of our Data -->
    <thead>
        <tr>
        	<!-- Bootstrap 4 class to adjust column width -->
            <th class="w-30">Tags</th>
            <th class="w-40">Description</th>
            <th class="w-30">Questions Count</th>
        </tr>
    </thead>
    
    <!-- Load our Data Here-->
    
    <!-- *ngFor Structure Directive to loop over data -->
    <tr> 
        <td>
        
        	<!-- RouterLink pointing to list of questions related to this tag !>
        	<a>
                
            </a>
        </td>

        <!-- Using Angular Template tag to load tag properties -->
        <td>
           
        	
        </td>
        <td>
       
        </td>
    </tr>
</table>

теги-list.component.css

.table {
  text-align: left;
}

теги-list.component.ts

## Importing necessary modules from angular core
import { Component, OnInit, Input } from '@angular/core';

## Importing Tag Data Model via ITag Interface
import { ITag } from '../../global/interfaces';

## Config TagsList Component
@Component({
  selector: 'app-tags-list',
  templateUrl: './tags-list.component.html',
  styleUrls: ['./tags-list.component.css']
})

## Writing Logic Code for TagsList Component
export class TagsListComponent implements OnInit {
  ## Create
  private _tags: ITag[] = []

  # Using @Input decorator and set to pass data from parents to child component
  @Input() get tags(): ITag[]{
  return this._tags;
  }

  set tags(value: ITag[]){
  	if (value){
  		this._tags = value;
  	}
  }
  constructor() { }

  ngOnInit() {
  	console.log(this._tags);
  }
}
  • Теперь нам нужно вернуться к нашим tags.component.html и tags.module.ts, чтобы загрузить наш компонент tags-list:

tags.component.html

<br/>
<br/>

<h2>{{ title }}</h2>
<app-tags-list ="popularTags"></app-tags-list>

теги.module.ts

...
import { TagsListComponent } from './tags-list/tags-list.component';
...

@NgModule({
  ...
  declarations: [TagsComponent, TagsListComponent],
  ...
})

export class TagsModule { }

Последний шаг — добавить App Route в корень наших app.component.html и app.module.ts.

app.component.html

## Display Rendered route here
<router-outlet></router-outlet>

app.module.ts

...
import { AppRoutingModule } from './app-routing.module';
...

...
  imports: [
    BrowserModule,
    CoreModule,
    TagsModule,
    QuestionsModule,
    AppRoutingModule
  ],
...

После этого шага, если мы запустим «ng serve» и перейдем к нему, мы сможем увидеть нашу таблицу тегов без данных.

Службы данных

В Angular задача компонентов — представлять данные и делегировать доступ к данным сервису. Служба Angular доступна для всех компонентов приложения.

Обычно мы пишем наш код DataService внутри CoreModule.

*** data.service.ts***

// Importing Angular Injectable and HTTPClient
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';


// Importinng RXJS Obserable, map and catchError
import { Observable, of} from 'rxjs';
import { map, catchError } from 'rxjs/operators';

// Importing Tag and Question Data Model from Interfaces
import { ITag, IQuestion } from '../../app/global/interfaces';


// Using @Injectable decorator to create DataService service.
@Injectable ()
export class DataService {

  <!-- We load local angular.json data from this url -->
  baseUrl: string = 'assets/';
  
  <!-- We load data from Stackoverflow API here -->
  stackUrl="
  site="/faq?site=stackoverflow";
  
  <!-- Initialize http: HTTPClient() inside our constructor
  constructor(private http: HttpClient) { }	
  
  <!-- getTags method to pass data via http get -->
  getTags(): Observable<ITag[]>{
    let myTags = this.http.get<ITag[]>(this.baseUrl + 'angular.json')
      .pipe(
        catchError(this.handleError)
    );
    return myTags
  }

  <!-- getQuestions method to pass data via http get from Stackoverlfow APIs-->
  getQuestions(tag: string): Observable<IQuestion[]>{
    return this.http.get<IQuestion[]>(this.stackUrl + tag + this.site)
      .pipe(
        map( data => {
          var items = data['items'];
          var questions: IQuestion[] = [];
          
          <!-- Iterate over response data and filter what we need for IQuestion-->
          for (let item of items){
            var question: IQuestion = {
              id: item['question_id'],
              tag: tag,
              voteCount: item['score'],
              questionTitle: item['title'],
              questionLink: item['link']
            };
            questions.push(question);
          }
          return questions;
        }),
        
        <!-- Calling handleError method -->
        catchError(this.handleError)
      );
  }

  // Handling Error Method
    private handleError(error: any) {
      console.error('server error:', error);
      if (error.error instanceof Error) {
          const errMessage = error.error.message;
          return Observable.throw(errMessage);
          // Use the following instead if using lite-server
          // return Observable.throw(err.text() || 'backend server error');
      }
      return Observable.throw(error || 'Node.js server error');
    }
}

Без создания DataServices теперь мы можем вернуться к нашим «tags.components.ts» и «tags-list.component.html», чтобы загрузить данные наших тегов.

tags.component.ts

...
import { DataService } from '../core/data.service';
...
...
export class TagsComponent implements OnInit {
  title: string;
  popularTags: ITag[];
  myQuestions = [];
  
  constructor(private dataService: DataService) {}
  ngOnInit(){
    this.title="Popular Angular Tags";
  
    <!-- ADDING DATA SERVICE INSIDE ngOnInit() -->
    this.dataService.getTags()
      .subscribe((tags: ITag[]) => this.popularTags = tags);
   }
}

теги-list.component.html

    ...
  <tr *ngFor="let tag of tags">
    	<!-- Tags Name -->
        <td>
        	<a [routerLink]="['/questions',tag.name]">
                {{ tag.name }}
            </a>
        </td>

        <!-- Tag Description -->
        <td>
            {{ tag.description }}
        	
        </td>
        <!-- Tag Question Count -->
        <td>
        	{{ tag.questionsTotal }}
        </td>
    </tr>
    ...

Теперь, если мы снова запустим «ng serve» и посетим «, мы сможем увидеть наши данные, загруженные из ‘assets/angular.json’.

Модуль вопросов

  • Вопросы Компоненты
    *вопросы.component.html
<!-- Rendering if Question Exist -->
<br />
<br />
<div>
  <h2>Questions for Angular Tag 
    <span id="tag">
      {{tag}}
    </span>
  </h2>

  <table class="table table-hover">
    <thead>
      <tr>
        <th class="w-20">ID</th>
              <th class="w-40">Vote Count</th>
              <th class="w-40">Question Content</th>
      </tr>
    </thead>
        
        <!-- Using Structire directive to loop over myQuestions data --> 
    <tr *ngFor="let question of myQuestions">
      <td>
        <a href="{{question.questionLink}}">{{question.id}}</a>
      </td>
      <td>{{question.voteCount}}</td>
      <td>{{question.questionTitle}}</td>
    </tr>
  </table>
</div>

<!-- Using routerLink to link back to our tag list page -->
<a routerLink="/tags">View All Angular Tags</a>

*вопросы.component.css

.table {
  text-align: left;
}

#tag {
  background-color: yellow;
}

вопросы.component.ts

# Importing Angular Component, Router, ActivatedRoute, Params
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';

# Importing IQuestion interface and DataService
import { IQuestion } from '../global/interfaces';
import { DataService } from '../core/data.service';

# Config Component
@Component({
  selector: 'app-questions',
  templateUrl: './questions.component.html',
  styleUrls: ['./questions.component.css']
})

# Implement Component Logic
export class QuestionsComponent implements OnInit {

  # Initialize myQuestions and tag to store this data
  myQuestions: IQuestion[] = [];
  tag: string;
  
  # Initialize dataService and route to use later
  constructor(
    private dataService: DataService,
    private route:ActivatedRoute
  ){};

  ngOnInit() {
  
    # Getting value of Tag param from url
  	this.tag = this.route.snapshot.paramMap.get('tag');
    
    # Passing questions data via DataService and store in 
    this.dataService.getQuestions('angularjs-directive')
      .subscribe((questions:IQuestion[]) => {
        this.myQuestions = questions;
    }
  }
}

  • Маршрутизация вопросов
    Мы создаем динамический маршрут с тегом параметра внутри questions-routing.module.ts.
# Angular Module
import {NgModule} from '@angular/core';
import {RouterModule, Routes } from '@angular/router';

# Questions Compomemt
import {QuestionsComponent } from './questions.component';

# Load Component via 'questions/:tag' path
const routes: Routes = [
  {path:'questions/:tag', component: QuestionsComponent}
];

# Register and Export Module
@NgModule({
  imports: [RouterModule.forChild(routes )],
  exports: [RouterModule]
})

export class QuestionsRoutingModule {

}

Последний шаг — добавить маршрутизацию вопросов внутри «questions.module.ts» и добавить модуль «Вопросы» в «app.module.ts».

вопросы.модуль.тс

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';

import { QuestionsComponent } from './questions.component';
import { QuestionsRoutingModule } from './questions-routing.module';

@NgModule({
  imports: [
    CommonModule,
    QuestionsRoutingModule
  ],
  declarations: [QuestionsComponent], 
  exports: [QuestionsComponent]
})
export class QuestionsModule {
  
}

app.module.ts

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

import { TagsModule} from './tags/tags.module';
import { QuestionsModule } from './questions/questions.module';
import { CoreModule } from './core/core.module';
import { AppRoutingModule } from './app-routing].module';

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

@NgModule({
    AppComponent
  ],
  imports: [
    BrowserModule,
    CoreModule,
    TagsModule,
    QuestionsModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Теперь, если мы снова запустим «Ng Serve» и посетим «приложение должно отображать список из 5 тегов и после того, как вы щелкнете по тегу, оно должно загрузить список вопросов, связанных с этим тегом.

Полный исходный код приложения здесь.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *