В один из дней как обычно попивая кофе, мне приходит сообщение в Skype о том что нужно в кратчайшие сроки сделать приложение с нехитрым функционалом. Нужно было иметь таблицу с сортировкой и пагинацией, возможность фильтровать по слову, дате и.т.п. Так же иметь страницу с информацией о выбранном элементе из таблицы.

Имея на руках такое тз, было решено использовать последний Angular в связке с Ag-Grid, что бы не тратить драгоценное время.

Начинается все довольно стандартно, установка и создание стандартного Angular приложения:

Устанавливаем сам Angular

npm install -g @angular/cli

Создаем новый проект

ng new my-app

И можем запускать

cd my-appng serve --open

Все, ничего нового или сложного здесь нет. Вся процедура занимает от силы 20 минут и мы уже имеем работающее приложение. Можем начинать навешивать на него то что нам нужно.

Начинаем с простого.

Bootstrap. Почему он? Просто, быстро эффективно. В рамках той задачи которая есть у нас, нам его вполне хватит. По сути это несколько полей для ввода, пара кнопок разных по цвету и все. Ставим третью версию вместе с jQuey:

$ npm install bootstrap@3 jquery --save 

Затем отправляемся в файл angular-cli.json и добавляем в него:

"styles": [
    "styles.css",
    "../node_modules/bootstrap/dist/css/bootstrap.min.css"
  ],
  "scripts": [
    "../node_modules/jquery/dist/jquery.min.js",
    "../node_modules/bootstrap/dist/js/bootstrap.min.js"
  ],

И готово, отныне на ваших страницах Bootstrap будет работать как нужно. А мы продолжаем.

Ag-Grid Для себя его выбрал, так как есть опыт работы с этим компонентом, он достаточно гибкий, и как-никак лучше подходит для нашей задачи. Для полноценного его использование ставим:

npm install ag-grid-angular
npm install ag-grid

Затем идем в app.module.ts и импортируем нужные нам компоненты:

import {AgGridModule} from "ag-grid-angular/main";

Установленный нами install ag-grid понадобиться нам чуть позже. Так же добавляем в наш файл

  imports: [
       AgGridModule.withComponents([
            LinkComponent
        ])
  ],

Хочу обратить внимание на AgGridModule.withComponents это позволит нам добавлять и использовать написанные нами компоненты в Ag-Grid. LinkComponent который там есть, это наш компонент для отрисовки данных в виде ссылки. Позже вернемся к нему.

Так же создаем папки Components, Services, View-Models, в них свои компоненты, сервисы для них и подключаем все это дело, думаю здесь никаких вопросов и проблем быть не должно. Вернемся лучше к нашему Ag-Grid.

В компоненте где используется Ag-Grid подключаем:

import { GridOptions, IDatasource, IGetRowsParams, ColDef, ColGroupDef, NodeChildDetails, RowNode } from "ag-grid";
import { LinkComponent } from '../example/example';

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

Настало время начинать создать грид. В целом у меня получилось вот так:

        this.gridOptions = <GridOptions>{};
        this.gridOptions.columnDefs = [
            {
                headerName: "Example1",
                field: "Example1",
            },
            {
                headerName: "Example2",
                field: "Example2",
                cellRendererFramework: LinkComponent,
            },
            {
                headerName: "Example3",
                field: "Example3",
            }

        ];
        this.ExampleService.getExamplesFields().then(response => {
            this.examples = response;
            this.gridOptions.api.setRowData(this.examples);
        });
}

А в html нашего компонента грид подключаем вот так:


      <ag-grid-angular #agGrid style="width: 100%; height: 500px;" class="ag-fresh"
                    [gridOptions]="gridOptions">
      </ag-grid-angular>

Но давайте по порядку. Для того что бы колонка была и заполнилась нужными нам данными, необходим минимум, это ее название и fieldid. Если обратите внимание на cellRendererFramework во второй колонке, то это и есть тот самый параметр который позволяет нам отрисовывать данные в колонке так как мы захотим. Давайте посмотрим как устроен этот компонент внутри.

import { Component } from "@angular/core";

@Component({
    selector: 'link-component',
    templateUrl: './link-component.component.html'
})
export class LinkComponent {
    public params: any;

    agInit(params: any): void {
        this.params = params;
    }
}

Вот и все, в params у нас хранится содержимое нужной ячейки. Если быть более конкретным то в params.value. В params.data мы можем увидеть все данные выбранной строки в гриде, нам например понадобится exampleID, для того что бы переходить на следующую страницу с нужным ID записи. Шаблон этого компонента выглядит вот так:

<a [routerLink]="['/exampleform', params.data.exampleID]">{{ params.value }}</a>

Тут мы пойдем немного дальше и поговорим о роутинге, но пока о нашей ссылке. Как мы видим, нажатие на данную строку будет отправлять нас на страничку выбранной записи, добавляя ее ID в линк, благодаря к чему впоследствии мы сможем обратиться к серверу который вернет нам нужную запись.

Роутинг я сделал достаточно просто.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ExamplesView } from './components/Examples/Examples.component';
import { AllExamples } from './components/Examples/Examplest.component';
const appRoutes: Routes = [
    {
        path: 'Examples/:id', component: ExamplesView 
    },
 {
        path: '', component: AllExamples
    },
];

@NgModule({
    imports: [
        RouterModule.forRoot(appRoutes, { enableTracing: true })
    ],
    exports: [
        RouterModule
    ],
    providers: [

    ]
})
export class AppRoutingModule { }

Это все что нам нужно на данный момент. Думаю здесь заострять внимание особо не нужно. В следующих статьях более подробно заострим внимание.

Вернемся к нашему компоненту с гридом. Как рисовать колонки, и менять отображение данных в них мы разобрались. Теперь нужно заполнить грид этими данными. Тут все тоже достаточно просто:

        this.ExampleService.getExamplesFields().then(response => {
            this.examples = response;
            this.gridOptions.api.setRowData(this.examples);
        });

Обращаемся к нашему сервису, который вернет нам json с данными. this.examples у нас examples: examplesViewModel[]; наша view модель выглядит скажем так:

export class FeedbackFormData
{
 public Example1: string;
 public Example2: string;
 public Example3: number;
}

Ее мы так же импортируем в наш компонент. И наши fieldid в гриде должны соответствовать тому что есть в нашей модели. И вуаля, наши данные оказываются там где нам нужно. В последствии например если при наличии серверной фильтрации, мы хотим обновить данные в гриде, сделать это можем так:

filter() {
    this.exampleFilter = this.datePipe.transform(this.maxDate, 'yyyy-MM-dd');
    this.HomeService.getPeriodForm(this.exampleFilter, this.search).then(response => {
        this.examples= response;
        this.gridOptions.api.setRowData(this.examples);
    })     
}

И данные в нашем гриде обновятся. Ну а о второй странице говорить особо нечего. Получаем данные о записи с нужным id и работаем с ними. Вот так вот буквально за пару часов можно сделать простенькое приложение, с гридом и блекджеком. В следующей статье подробнее поговорим о функционале грида и что с ним можно делать, наш компонент рисующий ссылку это только самая вершина айсберга. Так же расскажу о реализации пагинации и сортировки в гриде.

Спасибо.

Комментарии (4)


  1. vasIvas
    03.10.2017 16:40

    Последнее время так часто встречаю упоминание jQ и Angular 4 в одном контексте, что напрашивается вопрос — а это что, уже норма?


    1. valitskiydmitriy Автор
      03.10.2017 16:44

      Ну я стараюсь использовать его по минимуму, разве что для реализации каких то эффектов, но и то это очень и очень редко


  1. wispoz
    03.10.2017 21:01

    За пару часов… Такую задачу в свое время на Extjs делал за час максимум и уже с со всеми плюшками.


    1. valitskiydmitriy Автор
      03.10.2017 21:16

      Это было сказано как пример, а не реальные затраты времени на это приложение.