Библиотека 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:
Здесь мы отобразим кнопку отображения диалога открытия файла. Загруженный файл мы передаем в функцию 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 приложения.