Статьи

Отображение данных из связанных таблиц с использованием Angular с API-сервером

Angular — это обновленный фреймворк для динамических веб-приложений, основанный на расширяющихся принципах Angular JS. Сервер CData API позволяет создавать API REST для более 80 источников данных, включая как локальные, так и облачные базы данных. В этой статье мы расскажем о настройке CData API Server для создания REST API на основе OData для данных QuickBooks Online и создании простого одностраничного приложения (SPA), которое имеет прямой доступ к данным QuickBooks Online.

SPA будет динамически создавать и заполнять таблицы HTML на основе связанных таблиц QuickBooks Online (т.е. счетов-фактур и позиций счетов-фактур). Несмотря на то, что статья перебирает большую часть кода, вы можете скачать пример проекта Angular, чтобы увидеть полный исходный код и проверить функциональность самостоятельно.

Готовы начать? Загрузите бесплатную пробную версию CData API Server !

Настройка API-сервера

Если вы еще этого не сделали, вам необходимо загрузить CData API Server . После установки API-сервера вам нужно будет запустить приложение, настроить приложение для подключения к вашим данным (инструкции в этой статье относятся к прилагаемому образцу базы данных) и настроить приложение для создания REST API для любых таблиц. Вы хотите получить доступ в вашем SPA.

Включить CORS

Если веб-приложение Angular и сервер API находятся в разных доменах, то Angular будет генерировать междоменные запросы. Это означает, что CORS (совместное использование ресурсов между источниками) должно быть включено на любых серверах, запрашиваемых приложениями Angular Web. Мы можем включить CORS для Сервера API, перейдя на вкладку Сервер на странице НАСТРОЙКИ Сервера API. Вам нужно будет настроить следующие параметры:

  • Установите флажок «Включить общий доступ к ресурсам между источниками (CORS)».
  • Либо установите флажок «Разрешить все домены без« * »», либо укажите домены, которым разрешено подключаться, в Access-Control-Allow-Origin.
  • Установите Access-Control-Allow-Methods на «GET, PUT, POST, OPTIONS».
  • Установите Access-Control-Allow-Headers на «авторизацию».
  • Нажмите Сохранить изменения.

Подключитесь к QuickBooks Online

После развертывания укажите значения аутентификации и другие свойства подключения, необходимые для подключения к QuickBooks Online, нажав «Настройки» -> «Подключения» и добавив новое подключение в консоли администрирования API-сервера.
QuickBooks Online использует стандарт аутентификации OAuth. OAuth требует, чтобы аутентифицирующий пользователь входил в систему через браузер.

Для аутентификации с использованием OAuth вы можете использовать встроенные OAuthClientId, OAuthClientSecret и CallbackURL, или вы можете получить свой собственный, зарегистрировав приложение в Intuit. Вам также нужно будет указать CompanyId.
Обратитесь к главе «Приступая к работе» справочной документации для получения руководства по использованию OAuth.

Настроить пользователя

Затем создайте пользователя для доступа к данным вашей базы данных через сервер API. Вы можете добавлять и настраивать пользователей на вкладке «Пользователи» на странице «НАСТРОЙКИ». Поскольку мы создаем только простой SPA для просмотра данных, мы создадим пользователя, который имеет доступ только для чтения. Нажмите «Добавить», дайте пользователю имя, выберите «GET» для привилегий и нажмите «Сохранить изменения».

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

Доступ к таблицам

Создав пользователя, мы готовы разрешить доступ к таблицам базы данных. Чтобы включить таблицы, нажмите кнопку «Добавить ресурсы» на вкладке «Ресурсы» на странице «НАСТРОЙКИ». Выберите соединение для передачи данных, к которому вы хотите получить доступ, и нажмите Далее. Выбрав соединение, вы можете начать активацию ресурсов, щелкнув имя таблицы и нажав Далее. Вам нужно будет добавлять ресурсы по одной таблице за раз. В этом примере мы включили все таблицы.

Примеры URL для API REST

Настроив соединение с базой данных, создав пользователя и добавив ресурсы на API-сервер, теперь у нас есть легкодоступный REST API на основе протокола OData для этих ресурсов. Ниже вы увидите список таблиц и URL-адреса для доступа к ним. Для получения информации о доступе к таблицам вы можете перейти на страницу API для сервера API. Для URL вам понадобится адрес и порт сервера API. Поскольку мы работаем с Angular, мы добавим параметр @json в конец URL-адресов, которые по умолчанию не возвращают данные JSON.

Стол URL
Список объектов (таблиц) HTTP: // адрес: порт / api.rsc /
Метаданные для таблицы QBO_Invoices HTTP: // адрес: порт / api.rsc / QBO_Invoices / $ метаданных @json?
Данные QBO_Invoices HTTP: // адрес: порт / api.rsc / QBO_Invoices

Создание одностраничного приложения Как и в случае стандартных фидов OData, если вы хотите ограничить возвращаемые поля, вы можете добавить в запрос параметр $ select вместе с другими стандартными параметрами URL, такими как $ filter, $ orderby, $ skip и $ сверху.

После завершения настройки API-сервера мы готовы создать наш SPA. Мы пройдемся по исходным файлам для SPA, содержащимся в файле .zip, отмечая любые соответствующие разделы кода по мере продвижения. Некоторые из исходных файлов основаны на уроке Angular из angular.io.

SRC / index.html

Это домашняя страница нашего SPA, и исходный код в основном состоит из элементов скрипта для импорта необходимых библиотек Angular.

SRC / main.ts

Этот файл TypeScript загружает приложение.

ЦСИ / приложение / app.module.ts

Этот файл TypeScript создает класс, включая определения Компонентов и Сервисов, который импортирует необходимые модули для создания и запуска нашего SPA.

SRC / приложение / приложение-routing.module.ts

Этот файл TypeScript определяет маршруты и пути, используемые для навигации по содержимому нашего SPA.

ЦСИ / приложение / app.component.css

Этот файл создает наборы правил CSS для изменения элементов h1 и h2 в нашем веб-приложении.

ЦСИ / приложение / app.component.ts

Этот файл TypeScript создает Компонент для нашего SPA и определяет шаблон. Хотя это приложение простое, оно может быть легко расширено, чтобы включить несколько маршрутов и компонентов.

ЦСИ / приложение / dashboard.component.css

Этот файл создает наборы правил CSS для изменения элементов table, th и td в нашем HTML.

ЦСИ / приложение / dashboard.component.html

Этот файл определяет расположение компонентов панели нашего SPA. Шаблон состоит из раскрывающихся списков для выбора таблицы и связанной таблицы, раскрывающихся списков, указывающих, какие поля являются внешними ключами для таблиц, HTML-таблицы для отображения данных родительской таблицы и другой HTML-таблицы для отображения данных дочерней таблицы. ,

Различные разделы включаются / отключаются на основе критериев в директивах * ngIf, а меню и таблицы создаются динамически на основе результатов обращений к API-серверу с использованием директивы * ngFor для циклического перемещения по возвращаемым данным.

Все вызовы API-сервера и присвоение значений переменным выполняются в классах DashboardComponent и AppService.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<div style='float:left' class="table_select">
  <label>Select a Table</label>
  <br/>
  <select [(ngModel)]="selectedTable" (change)="tableChanged()">
    <option *ngFor="let sel_table of tableNames" [value]="sel_table">{{sel_table}}</option>
  </select>
  <br/>
  <div *ngIf="selectedTable">
    <label>Select the Key for [{{selectedTable}}]</label>
    <br/>
    <select [(ngModel)]="tableKey">
      <option *ngFor="let sel_column of tableColumns" [value]="sel_column">{{sel_column}}</option>
    </select>
    <br/>
  </div>
</div>
<div class="subtable_select" *ngIf="selectedTable">
  <label>Select a SubTable</label>
  <br/>
  <select [(ngModel)]="selectedSubTable" (change)="subTableChanged()">
    <option *ngFor="let sel_table of tableNames" [value]="sel_table">{{sel_table}}</option>
  </select>
  <br/>
  <div *ngIf="selectedSubTable">
    <label>Select the Key for [{{selectedSubTable}}]</label>
    <br/>
    <select *ngIf="selectedSubTable" [(ngModel)]="subTableKey">
      <option *ngFor="let sel_column of subTableColumns" [value]="sel_column">{{sel_column}}</option>
    </select>
    <br/>
  </div>
</div>
<div *ngIf="selectedTable && tableKey && selectedSubTable && subTableKey && tableData?.length > 0" class="data_retrieve">
  <br/>
  <h2>Click an Entry from [{{selectedTable}}] to Expand the [{{selectedSubTable}}] Entities</h2>
  <table>
    <tr>
      <th *ngFor="let column of tableColumns">{{ column }}</th>
    </tr>
    <tr style='cursor:pointer' *ngFor="let row of tableData" (click)="rowClicked(row[tableKey])">
      <td *ngFor="let column of tableColumns">{{ row[column] }}</td>
    </tr>
  </table>
</div>
<div *ngIf="selectedSubTable && subTableColumns && subTableData?.length > 0">
  <br/>
  <hr/>
  <h2>Data from [{{selectedSubTable}}]</h2>
  <table>
    <tr>
      <th *ngFor="let column of subTableColumns">{{ column }}</th>
    </tr>
    <tr *ngFor="let row of subTableData">
      <td align=center *ngFor="let column of subTableColumns">{{ row[column] }}</td>
    </tr>
  </table>
</div>

ЦСИ / приложение / app.service.ts

Этот файл TypeScript создает сервис для получения данных с сервера API. В нем есть функции для извлечения списка таблиц, извлечения списка столбцов для конкретной таблицы и извлечения данных из таблицы. У нас также есть класс, который представляет метаданные таблицы, возвращаемые сервером API.

API_Table

Метаданные, возвращаемые сервером API для таблицы, включают имя таблицы, вид и URL-адрес. Мы используем только поле имени, но передаем весь объект в том случае, если нам понадобится другая информация, если мы решим опираться на наш SPA.

1
2
3
4
5
export class API_Table {
  name: string;
  kind: string;
  url: string;
}

конструктор()

В конструкторе мы создаем частный экземпляр класса Http и устанавливаем HTTP-заголовок Authorization на основе учетных данных user / authtoken для пользователя, которого мы создали ранее. Затем мы включаем этот заголовок в наши HTTP-запросы.

1
2
3
constructor(private http: Http) {
  this.headers.append('Authorization', 'Basic ' + btoa(this.userName+":"+this.authToken));
}

getTables ()

Эта функция возвращает список таблиц. Список извлекается с сервера API путем выполнения запроса HTTP GET, включая заголовок авторизации, на базовый URL-адрес сервера API: http: // localhost: 8153 / api.rsc

1
2
3
4
5
6
getTables(): Promise<API_Table[]> {
  return this.http.get(this.baseUrl, {headers: this.headers})
    .toPromise()
    .then(response => response.json().value )
    .catch(this.handleError);
}

getColumns ()

Эта функция возвращает список столбцов для таблицы, указанной в tableName. Поскольку конечная точка $ metadata возвращает данные в формате XML по умолчанию, мы передаем параметр @json в URL, чтобы гарантировать получение данных JSON с API-сервера. Получив данные JSON, мы можем перейти к получению списка имен столбцов.

1
2
3
4
5
6
7
getColumns(tableName: string): Promise<string[]> {
  return this.http.get(`${this.baseUrl}/${tableName}/$metadata?@json`,
                       {headers: this.headers})
    .toPromise()
    .then(response => response = response.json().items[0]["odata:cname"] )
    .catch(this.handleError);
}

getTableDataByColumns (tableName: string, columnList: string)

Эта функция возвращает строки данных для указанной таблицы и столбцов. Мы передаем tableName в URL, а затем передаем список столбцов (разделенную запятыми строку) в качестве значения параметра $ select URL. Если конкретные столбцы не запрошены, мы отправляем, не используем параметр $ select URL и запрашиваем все столбцы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
getTableDataByColumns(tableName:string, columnList: string): Promise<Object[]> {
  if (columnList) {
    return this.http.get(`${this.baseUrl}/${tableName}/?$select=${columnList}`,
                         {headers: this.headers})
      .toPromise()
      .then(response => response = response.json().value )
      .catch(this.handleError);
  } else {
    return this.http.get(`${this.baseUrl}/${tableName}/`, {headers: this.headers})
      .toPromise()
      .then(response => response = response.json().value )
      .catch(this.handleError);
  }
}

getAllTableDataById (tableName: строка, idColumn: строка, idValue: строка)

Эта функция возвращает строки данных для указанной таблицы на основе указанного столбца ID и значения. Мы передаем tableName в URL, а затем используем столбец ID и значение для запроса данных, относящихся к определенной записи в основной таблице.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
getTableDataByColumns(tableName:string, columnList: string): Promise<Object[]> {
  if (columnList) {
    return this.http.get(`${this.baseUrl}/${tableName}/?$select=${columnList}`,
                         {headers: this.headers})
      .toPromise()
      .then(response => response = response.json().value )
      .catch(this.handleError);
  } else {
    return this.http.get(`${this.baseUrl}/${tableName}/`, {headers: this.headers})
      .toPromise()
      .then(response => response = response.json().value )
      .catch(this.handleError);
  }
}

getAllTableDataById (tableName: строка, idColumn: строка, idValue: строка)

Эта функция возвращает строки данных для указанной таблицы на основе указанного столбца ID и значения. Мы передаем tableName в URL, а затем используем столбец ID и значение для запроса данных, относящихся к определенной записи в основной таблице.

1
2
3
4
5
6
7
getAllTableDataById(tableName:string, idColumn:string, idValue:string): Promise<Object[]> {
  return this.http.get(`${this.baseUrl}/${tableName}(${idColumn}='${idValue}')`,
                       {headers: this.headers})
    .toPromise()
    .then(response => response = JSON.parse('[' + response['_body'] + ']'))
    .catch(this.handleError);
}

ЦСИ / приложение / dashboard.component.ts

В этом файле TypeScript мы определили функции, которые реагируют на события в SPA; внутри этих функций мы вызываем функции из AppService и используем результаты для заполнения различных элементов SPA. Эти функции довольно просты, при необходимости присваивая значения различным переменным.

ngOnInit ()

В этой функции мы вызываем функцию getTables из нашего AppService. Поскольку getTables возвращает необработанные объекты данных из нашего запроса к таблице API-сервера, нам нужно помещать только поле имени из каждого результата в массив доступных таблиц, а не передавать весь объект целиком.

1
2
3
4
5
6
7
8
9
ngOnInit(): void {
  this.appService
    .getTables()
    .then( tables => {
      for (let tableObj of tables) {
        this.tableNames.push( tableObj.name )
      }
    });
}

tableChanged ()

Эта функция вызывается всякий раз, когда пользователь выбирает таблицу из выпадающего меню в SPA. Функция вызывает API-сервер для получения списка столбцов для данной таблицы, который заполняет другое раскрывающееся меню. Функция также извлекает данные для выбранной таблицы, которая используется для заполнения таблицы HTML.

1
2
3
4
5
6
7
8
tableChanged(): void
  this.appService
    .getColumns(this.selectedTable)
    .then( columns => this.tableColumns = columns.sort() );
  this.appService
    .getTableDataByColumns(this.selectedTable, this.tableColumns)
    .then( data => this.tableData = data );
}

subTableChanged ()

Эта функция вызывается всякий раз, когда пользователь выбирает связанную таблицу из раскрывающегося меню. Функция вызывает API-сервер для получения списка столбцов для данной таблицы, который заполняет другое раскрывающееся меню.

1
2
3
4
5
subTableChanged(): void
  this.appService
    .getColumns(this.selectedSubTable)
    .then( columns => this.subTableColumns = columns.sort() ); 
}

rowClicked (keyValue: строка)

Эта функция вызывается всякий раз, когда щелкают строки данных из основной таблицы. Он захватывает значение идентификатора для строки (на основе выбранного столбца для основной таблицы) и вызывает сервер API для получения данных из связанной таблицы на основе идентификатора выбора. Полученные данные затем используются для заполнения таблицы HTML.

1
2
3
4
5
6
rowClicked(keyValue: string): void {
  columnList = this.selectedColumns.join(',');
  this.appService
    .getTableData( this.selectedTable, columnList )
    .then( data => this.tableData = data );
}

Запуск одностраничного приложения

Теперь, когда мы настроили соединение с данными и рассмотрели исходные файлы для SPA, мы готовы запустить одностраничное приложение. Чтобы запустить SPA, вам нужно будет установить node.js и npm на вашем компьютере. В пример загрузки входит предварительно настроенный файл package.json.

Вы можете установить необходимые модули, запустив npm install из командной строки в корневом каталоге SPA. Чтобы запустить SPA, просто запустите npm start в том же каталоге.

Когда SPA запускается, вы увидите заголовок и раскрывающееся меню для выбора таблицы. Список таблиц извлекается из API-сервера и включает все таблицы, добавленные вами в качестве ресурсов при настройке API-сервера.

С выбранной таблицей появляется выпадающий список для столбцов, позволяющий выбрать ключевой столбец, который связывает таблицу и вложенную таблицу.

Выбрав основную таблицу и ключевой столбец, вы можете выбрать соответствующую вложенную таблицу.

После выбора вложенной таблицы появляется выпадающий список для столбцов, позволяющий выбрать ключевой столбец, который связывает таблицу и вложенную таблицу.

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

Бесплатная пробная версия и дополнительная информация

Теперь, когда вы увидели базовый пример подключения к данным вашей базы данных на динамических веб-страницах, посетите нашу страницу API-сервера, чтобы узнать больше информации об API-сервере и загрузить API-сервер.

Начните создавать динамические веб-страницы с использованием оперативных данных из локальных и облачных баз данных, приложений и служб, таких как QuickBooks Online!

Готовы начать? Загрузите бесплатную пробную версию CData API Server !