Многим пользователям 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.
На этом создание нашего демонстрационного приложения закончено. Мы удачно отобразили дизайнер отчетов, загрузили в него нужный отчет и сохранили его.