Как использовать Online Designer в приложении на основе библиотеки knockout.js

30.06.2019

Библиотека knockout.js была достаточно популярна среди веб разработчиков до появления angular. Несмотря на явные преимущества последнего, knockout все еще остается востребованным. На нем написано множество веб приложений. Некоторые из них нуждаются в отчетности. Поэтому мы рассмотрим в этой статье, как внедрить онлайн дизайнер отчетов FastReport в приложение, с клиентской частью на knockout, а серверной на ASP.Net Core.

Прежде всего нам потребуется шаблон приложения Knockout для dotnet платформы. У вас должен быть установлен NET Core 2.0 SDK или MS Visual Studio 2017. Откроем командную строку Windows. Перейдем в каталог, где планируется создать приложение. Вводим команду:

dotnet new — install Microsoft.AspNetCore.SpaTemplates::*

В результате мы должны увидеть список доступных шаблонов одностраничных приложений:

В этом списке мы видим knockout – значит можно создать приложение по шаблону. Вводим команду:

dotnet new knockout –o KnockOnlineDesigner

Эта команда создает демонстрационное приложение с готовой структурой. После создания приложения нужно установить нужные пакеты. Поэтому переходим в папку с созданным приложением:

cd KnockOnlineDesigner

И устанавливаем пакеты:

npm install

Открываем созданный проект. Сейчас вы можете запустить приложение и увидеть три демонстрационные страницы. Но наша задача создать свою страницу с онлайн дизайнером. Прежде всего нужно установить пакеты FastReport .NET в диспетчере пакетов Nuget. Открываем менеджер и настраиваем локальный источник пакетов – папка Nuget в каталоге установки FastReport .NET.

Пакеты FastReport.Core и FastReport.Web установлены, теперь нужно включить использование FastReport в приложении. Редактируем файл Startup.cs. Добавляем в метод Configure строку:

  app.UseFastReport();

В папку wwwroot добавим папку App_Data, а в нее базу данных xml для отчетов:

 

Переходим к редактированию контроллера 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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
 [Route("api/[controller]")]
 public class SampleDataController : Controller
 {
 
 private IHostingEnvironment _env;
 
 public SampleDataController(IHostingEnvironment env)
 {
 _env = env;
 }
 
 // public string ReportPath;
 
 [HttpGet("[action]")]
 public IActionResult Design(string ReportName)
 {
 var webRoot = _env.WebRootPath;
 WebReport WebReport = new WebReport();
 WebReport.Width = "1000";
 WebReport.Height = "1000";
 Task.WaitAll();
 var path = System.IO.Path.Combine(webRoot, "App_Data/", ReportName);
 if (System.IO.File.Exists(path))
 {
 WebReport.Report.Load(System.IO.Path.Combine(webRoot, "App_Data/", ReportName)); //Загружаем отчет в объект WebReport
 }
 System.Data.DataSet dataSet = new System.Data.DataSet(); //Создаем источник данных
 dataSet.ReadXml(System.IO.Path.Combine(webRoot, "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)
 {
 var webRoot = _env.WebRootPath;
 ViewBag.Message = String.Format("Confirmed {0} {1}", reportID, reportUUID); //Задаем сообщение для представления
 
 Stream reportForSave = Request.Body; //Записываем в поток результат Post запроса
 string pathToSave = System.IO.Path.Combine(webRoot, @"App_Data/TestReport.frx"); //получаем путь для сохранения файла
 using (FileStream file = new FileStream(pathToSave, FileMode.Create)) //Создаем файловый поток
 {
 reportForSave.CopyTo(file); //Сохраняем результат запроса в файл
 }
 return View();
 }
 
 [HttpPost("[action]")]
 public async Task<IActionResult> Upload(List<IFormFile> files)
 {
 long size = files.Sum(f => f.Length);
 var webRoot = _env.WebRootPath;
 var filePath = System.IO.Path.Combine(webRoot, (String.Format("App_Data/{0}", files[0].FileName)));
 // full path to file in temp location
 
 if (files[0].Length > 0)
 {
 using (var stream = new FileStream(filePath, FileMode.Create))
 {
 await files[0].CopyToAsync(stream);
 stream.Close();
 }
 }
 return RedirectToAction("Design", "SampleData", new { ReportName = Path.GetFileName(filePath)});
 }
 
 }

Метод Design создает объект веб отчета и загружает в него шаблон отчета. Затем, этот веб отчет переключается в режим разработки, а по сути передается в онлайн дизайнер.

Метод SaveDesidnedReport нужен для сохранения отредактированного отчета на сервере.

Метод Upload служит для загрузки файла отчета на сервер.

Для методов Design и SaveDesignedReport нужно создать представления. Делаем правый клик по сигнатуре метода и выбираем Add view. Для представления Design.cshtml изменяем код на:

@await ViewBag.WebReport.Render()

А для SaveDesignedReport.cshtml - на:

@ViewBag.Message

В папке ClientApp располагается приложение на knockout.js. Давайте будем выводить наш дизайнер отчетов прямо на домашней странице. Разворачиваем папку ClientApp->components->home-page. Редактируем файл home-page.html:

1
2
3
4
<div id="app">
 <input type="file" id="FileName" accept=".frx" data-bind="event: { change: upload($element.files[0]) }" />
</div>
<div data-bind="html: designer"></div>

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

Теперь изменим файл скрипта home-page.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import * as ko from 'knockout';
 
class HomePageViewModel {
 public designer = ko.observable('');
 
 upload(file: Blob) {
 var files = new FormData();
 files.append("files", file)
console.log(files);
 if (file != null) {
 fetch('api/SampleData/Upload', {
 method: 'POST',
 body: files
 })
 .then(response => response.text())
 .then(data => {
 this.designer(data);
 });
 }
 }
}
 
export default { viewModel: HomePageViewModel, template: require('./home-page.html') };

Метод Upload получает файл и передает его на сервер. В свою очередь контроллер на сервере загружает файл отчета в онлайн дизайнер и возвращает его клиенту. В методе Upload мы записываем ответ сервера в переменную designer.

Осталось исправить меню сайта nav-menu.html. Уберем из него ненужные страницы:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div class='main-nav'>
 <div class='navbar navbar-inverse'>
 <div class='navbar-header'>
 <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
 <span class='sr-only'>Toggle navigation</span>
 </button>
 <a class='navbar-brand' href='/'>KnockOnlineDesigner</a>
 </div>
 <div class='clearfix'></div>
 <div class='navbar-collapse collapse'>
 <ul class='nav navbar-nav'>
 <li>
 <a data-bind='attr: { href: router.link("/") }, css: { active: route().page === "home-page" }'>
 <span class='glyphicon glyphicon-home'></span> Home
 </a>
 </li>
 </ul>
 </div>
 </div>
</div>

А также отредактируем файл app-root.ts и исключим из него ненужные компоненты:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class AppRootViewModel {
public route: KnockoutObservable<Route>;
public router: Router;
 
constructor(params: { history: History.History, basename: string }) {
this.router = new Router(params.history, routes, params.basename);
this.route = this.router.currentRoute;
ko.components.register('nav-menu', navMenu);
ko.components.register('home-page', require('bundle-loader?lazy!../home-page/home-page'));
}
 
public dispose() {
this.router.dispose(); Object.getOwnPropertyNames((<any>ko).components._allRegisteredComponents).forEach(componentName => {
ko.components.unregister(componentName);
});
}
}
 
export default { viewModel: AppRootViewModel, template: require('./app-root.html') };

Запустим наше демонстрационное приложение. Выберем файл отчета в формате frx. Файл загрузится на сервер и передастся в дизайнер отчета:

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

4 октября 2023

Как из приложения ASP.NET Core сформировать отчет с использованием FastReport.Core.Skia

Рассказываем как сформировать отчет на Windows и Linux с использованием FastReport.Core.Skia и приватного NuGet сервера.
22 марта 2023

Создание PDF отчета в JetBrains Rider (C#) на «Альт Рабочая станция К» 10

В этой статье мы взглянем на платформу .NET в «Альт Рабочая станция К» 10 и создадим отчет, который можно экспортировать в PDF.
7 марта 2023

Настройка API для сборки FastReport Online Designer

Рассказываем об автоматизации процесса сборки FastReport Online Designer Builder через API при изменении версии продукта.