Настройка Online Designer в приложении React .NET Core

Многим пользователям FastReport.Core интересно, как будет работать генератор отчетов в веб приложении, написанном с использованием библиотеки React. Это мы уже рассмотрели в статье {}. В этой статье мы рассмотрим использование онлайн дизайнера. Несмотря на то, что он выводится все в том же веб объекте, что и обычный отчет, разница при отображении в React существенная. Но, обо всем по порядку.

Если вы никогда еще не создавали приложения на React с бекендом на .Net Core, то вам нужно:

1) Установить NodeJS. Это программный комплекс, который позволяет выполнять JavaScript код на стороне сервера, а также устанавливать различные JavaScript библиотеки.

2) Установить Microsoft Visual Studio 2017 или другую IDE + .Net Core SDK 2.0.

Чтобы создать приложение, откройте командную строку Windows в папке, где будет расположен проект и выполните команду:

dotnet new react -o ReactFRCoreDesigner

Откроем созданный проект. Давайте сразу добавим библиотеки FastReport в менеджере пакетов NuGet. Настраиваем локальный источник пакетов на папку:

C:\Program Files (x86)\FastReports\FastReport.Net\Nugets

Устанавливаем пакет FastReport.Core.

Найдите в проекте файл Startup.cs и добавьте в метод Configure() одну строку кода:

1
app.UseFastReport();

Теперь мы можем использовать генератор отчета в нашем проекте.

Помимо отображения онлайн дизайнера, мы также рассмотрим как передать имя нужного отчета и загрузить его в онлайн дизайнер. Поэтому добавим в проект папку App_Data. А в нее - шаблоны отчетов из папки Demos\Reports в каталоге установки FR.Net.

Как видите, мы также добавили xml файл из той же папки. Это база данных для отчетов.

Найдите папку Controllers. Нам доступен контроллер SampleDataController. Добавьте в него два метода:

1
2
3
4
5
6
7
8
9
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
...
using FastReport.Web;
using System.IO;
...
[HttpGet("[action]")]
 public IActionResult Design(string name)
 {
 WebReport WebReport = new WebReport();
 WebReport.Width = "1000";
 WebReport.Height = "1000";
 if (name != "Blank")
 WebReport.Report.Load("App_Data/" + name + ".frx"); //Загружаем отчет в объект WebReport
 //WebReport.Report.Load("App_Data/Text.frx"); //Загружаем отчет в объект WebReport
 System.Data.DataSet dataSet = new System.Data.DataSet(); //Создаем источник данных
 dataSet.ReadXml("App_Data/nwind.xml"); //Открываем базу данных xml
 WebReport.Report.RegisterData(dataSet, "NorthWind"); //Регистрируем источник данных в отчете

 WebReport.Mode = WebReportMode.Designer; //Устанавливаем режим объекта веб отчет - отображение дизайнера
 WebReport.DesignerLocale = "en";
 WebReport.DesignerPath = @"WebReportDesigner/index.html"; //Задаем URL онлайн дизайнера
 WebReport.DesignerSaveCallBack = @"api/SampleData/SaveDesignedReport"; //Задаем URL представления для метода сохранения отчета
 WebReport.Debug = true;
 ViewBag.WebReport = WebReport; //передаем отчет во View
 return View();
 }

 [HttpPost("[action]")]
 // call-back for save the designed report 
 public IActionResult SaveDesignedReport(string reportID, string reportUUID)
 {
 ViewBag.Message = String.Format("Confirmed {0} {1}", reportID, reportUUID); //Задаем сообщение для представления

 Stream reportForSave = Request.Body; //Записываем в поток результат Post запроса
 string pathToSave = @"App_Data/TestReport.frx"; //получаем путь для сохранения файла
 using (FileStream file = new FileStream(pathToSave, FileMode.Create)) //Создаем файловый поток
 {
 reportForSave.CopyTo(file); //Сохраняем результат запроса в файл
 }
 return View();
 }

Первый метод создает объект веб отчета, задаем для него шаблон и источник данных, а также устанавливаем режим редактирования отчета, настройки дизайнера отчетов. В итоге метод вернет представление, где будет отображен объект веб отчета. Метод имеет параметр - имя отчета, который мы подставляем при загрузке шаблона отчета в объект веб отчета.

Второй метод является обработчиком call back"а нажатия кнопки сохранения отчета. Он сохраняет отредактированный отчет в папку App_Data.

Для этих двух методов необходимо создать два представления. Создайте в корне проекта папку Views. Теперь вернемся к контроллеру. Правый клик по сигнатуре метода Design и выбираем из меню Add view. Задаем имя представления - Design. Заменяем все содержимое созданного представления на код:

1
@await ViewBag.WebReport.Render()

Для метода SaveDesignedReport также создаем представление с таким же именем. Его содержимое заменяем на:

1
@ViewBag.Message

Переходим к самому интересному - фронтенду. React приложение располагается в папке ClientApp. Разворачиваем его в дереве в обозревателе решения. Далее раскрываем директорию src и components. Добавим новый компонент в эту папку. Создаем JavaScript файл с именем Designer:

1
2
3
4
5
6
7
8
9
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
import React, { PureComponent, Fragment } from 'react';
import { WebReport } from './WebReport';

export class Designer extends PureComponent {
 constructor(props) {
 super(props);
 this.state = {
 options: [
 {
 value: 'Select report name ...',
 },
 {
 value: 'Matrix',
 },
 {
 value: 'Master-Detail',
 },
 {
 value: 'Text',
 },
 ]
 };
 } 

 handleChange = (event) = > {
 this.setState({ name: event.target.value }); 
 };

 render() {
 const { options, value } = this.state;
 return (
 < div >
 < div >
 < Fragment >
 < select onChange={this.handleChange} value={value} >
 {options.map(item = > (
 < option key={item.value} value={item.value} >
 {item.value}
 < /option >
 ))}
 < /select >
 < /Fragment >
 < /div >
 < WebReport name={this.state.name} / >
 < /div >
 );
 }
 }

Наверное, вы обратили внимание на импорт компонента WebReport, мы рассмотрим его позже.

Прежде всего, в конструкторе класса добавляем состояния. В нашем случае - массив с именами отчетов. Далее рассмотрим сразу render() - метод, который строит веб страницу. Рендеринг выполняется каждый раз, когда состояние изменяется. Например, когда мы выбираем элемент списка, выполняется обработчик события onChanges. В этом методе задается новое состояние переменной name с помощью функции setState. После этого будет перестроено содержимое render"а.

Обратите внимание на тег < WebReport name={this.state.name} / >.

Здесь вызывается другой компонент. В качестве параметра он получает выбранное имя отчета.

Рассмотрим компонент WebReport, который также, как и

Designer.js нужно создать в каталоге components:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React, { Component } from 'react';

export class WebReport extends Component {
 constructor(props) {
 super(props);
 this.state = { designer: "" };
 }

 componentWillReceiveProps(nextProps) {
 fetch('api/SampleData/Design?name=' + nextProps.name + '').then(response = > response.text()).then(text = > {
 this.setState({ designer: text });
 });
 };

 render() {
 return (
 < div dangerouslySetInnerHTML={{ __html: this.state.designer }} / >
 );
 }
}

Вся суть этого компонента - выполнить get запрос к бэкенду и вернуть полученный html код.

Встроенная в react функция componentWillReceiveProps(nextProps) выполняется каждый раз, когда свойство props изменяется. То есть, когда этот компонент получит новое значение при вызове. Мы получаем имя отчета из свойства и подставляем его в url запроса. Получаем ответ в формате текст. Его нужно преобразовать в безопасный html код, чтобы вставить в DOM. В этом нам помогает атрибут dangerouslySetInnerHTML.

Осталось лишь добавить компонент Designer в меню. В файл NavMenu добавим:

1
2
3
4
5
6
7
8
9
10
< Navbar.Collapse >
 < Nav >
 ...
 < LinkContainer to={'/designer'} >
 < NavItem >
 Designer
 < /NavItem >
 < /LinkContainer >
 < /Nav >
< /Navbar.Collapse >

А в файл App.js - это:

1
2
3
4
5
6
7
8
...
import { Designer } from './components/Designer';
...
 < Layout >
...
 < Route path='/designer' component={Designer} / >
 < /Layout >
...

Вот и все. Запускаем приложение. На странице Designer мы увидим выпадающий список:

Выберем имя отчета Matrix:

А теперь - Master-Detail:

Перейдем на вкладку Report и нажмем кнопку Save:

Справа появилось сообщение saved, что говорит нам об успешном сохранении отчета на сервере. Проверим это:

В папке App_Data появился еще один файл - TestReport.frx.

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