---
title: "DevExpress Reporting (Angular)"
description: "ASP.NET Zero is the base solution for building robust and scalable web applications. Unlock your full potential today!"
url: "https://aspnetzero.com/blog/devexpress-reporting-angular"
image: "https://aspnetzero.com/Images/Blog/devexpress-reporting-angular.png"
---

[Blog](https://aspnetzero.com/blog)

# DevExpress Reporting (Angular)

02 March 2022 6 min read [Angular](https://aspnetzero.com/blog/topics/angular) [Dev Express](https://aspnetzero.com/blog/topics/dev-express) [Reporting](https://aspnetzero.com/blog/topics/reporting)

![DevExpress Reporting (Angular)](https://aspnetzero.com/Images/Blog/devexpress-reporting-angular.png)

Share [X](https://twitter.com/intent/tweet?url=https%3A%2F%2Faspnetzero.com%2Fblog%2Fdevexpress-reporting-angular&text=DevExpress+Reporting+%28Angular%29) [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Faspnetzero.com%2Fblog%2Fdevexpress-reporting-angular) [Reddit](https://reddit.com/submit?url=https%3A%2F%2Faspnetzero.com%2Fblog%2Fdevexpress-reporting-angular&title=DevExpress+Reporting+%28Angular%29) [Hacker News](https://news.ycombinator.com/submitlink?u=https%3A%2F%2Faspnetzero.com%2Fblog%2Fdevexpress-reporting-angular&t=DevExpress+Reporting+%28Angular%29)

# DevExpress Reporting in ASP.NET Zero (ASP.NET Core & Angular version)

ASP.NET Zero, with its modular and extensible architecture, provides a solid foundation for developing robust web applications. By combining the capabilities of **DevExpress Reporting** with ASP.NET Zero, you can effortlessly create and customize visually appealing reports that meet your business requirements.

## Server Side

1. Download [DevExpress Reporting](https://www.devexpress.com/subscriptions/reporting/).
2. Open your ASP.NET Zero project.
3. Import `DevExpress.AspNetCore.Reporting` package to `[YOURAPPNAME].Web.Host` project.
4. Then go to `Startup.cs` and add these code parts:

```csharp
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    //...
    services.AddDevExpressControls(); //add this line
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
    //...
    app.UseDevExpressControls(); //add this line
}
```

5. Now, you can create a sample report to test if it all works. Go to `[YOURAPPNAME].Web.Host` and create a folder named `Reports`.
6. Right click on the `Reports` folder then click `Add` -&gt; `New Item`, then select `DevExpress Report` item.
7. Select `Blank` report in the opening wizard, and create new empty report named SampleReport.

![blank-selection](https://aspnetzero.com/Images/Blog/devexpress-reporting-blank-selection.png)

*(Design your report as you wish)*

### Custom Controllers

DevExpress Reporting has some abstract Controller definitions which we must implement. You can create Controllers below in your \*.Web.Host project.

```csharp
[ApiExplorerSettings(IgnoreApi = true)]
public class CustomWebDocumentViewerController : WebDocumentViewerController, ITransientDependency
{
    public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService) : base(controllerService)
    {
    }
}

[ApiExplorerSettings(IgnoreApi = true)]
public class CustomReportDesignerController : ReportDesignerController, ITransientDependency
{
    public CustomReportDesignerController(IReportDesignerMvcControllerService controllerService) : base(controllerService)
    {
    }
}

[ApiExplorerSettings(IgnoreApi = true)]
public class CustomQueryBuilderController : QueryBuilderController, ITransientDependency
{
    public CustomQueryBuilderController(IQueryBuilderMvcControllerService controllerService) : base(controllerService)
    {
    }
}
```

### Reports Factory

Create a factory class which provides reports by name `ReportsFactory`

*ReportsFactory.cs*

```csharp
public static class ReportsFactory
{
    public static Dictionary<string, Func<XtraReport>> Reports = new Dictionary<string, Func<XtraReport>>()
    {
        ["SampleReport"] = () => new SampleReport()
    };
}
```

### Custom Report Storage Extension

Create a class named `CustomReportStorageWebExtension` as seen below

*CustomReportStorageWebExtension.cs*

```csharp
public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension
{
    readonly string ReportDirectory;
    const string FileExtension = ".repx";
    public CustomReportStorageWebExtension(IWebHostEnvironment env)
    {
        ReportDirectory = Path.Combine(env.ContentRootPath, "Reports");
        if (!Directory.Exists(ReportDirectory))
        {
            Directory.CreateDirectory(ReportDirectory);
        }
    }

    private bool IsWithinReportsFolder(string url, string folder)
    {
        var rootDirectory = new DirectoryInfo(folder);
        var fileInfo = new FileInfo(Path.Combine(folder, url));
        return fileInfo.Directory.FullName.ToLower().StartsWith(rootDirectory.FullName.ToLower());
    }

    public override bool CanSetData(string url)
    {
        // Determines whether or not it is possible to store a report by a given URL. 
        // For instance, make the CanSetData method return false for reports that should be read-only in your storage. 
        // This method is called only for valid URLs (i.e., if the IsValidUrl method returned true) before the SetData method is called.

        return true;
    }

    public override bool IsValidUrl(string url)
    {
        // Determines whether or not the URL passed to the current Report Storage is valid. 
        // For instance, implement your own logic to prohibit URLs that contain white spaces or some other special characters. 
        // This method is called before the CanSetData and GetData methods.

        return Path.GetFileName(url) == url;
    }

    public override byte[] GetData(string url)
    {
        // Returns report layout data stored in a Report Storage using the specified URL. 
        // This method is called only for valid URLs after the IsValidUrl method is called.
        try
        {
            if (Directory.EnumerateFiles(ReportDirectory).Select(Path.GetFileName).Contains(url))
            {
                return File.ReadAllBytes(Path.Combine(ReportDirectory, url + FileExtension));
            }
            if (ReportsFactory.Reports.ContainsKey(url))
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    ReportsFactory.Reports[url]().SaveLayoutToXml(ms);
                    return ms.ToArray();
                }
            }
        }
        catch (Exception ex)
        {
            throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Could not get report data.", ex);
        }
        throw new DevExpress.XtraReports.Web.ClientControls.FaultException(string.Format("Could not find report '{0}'.", url));
    }

    public override Dictionary<string, string> GetUrls()
    {
        // Returns a dictionary of the existing report URLs and display names. 
        // This method is called when running the Report Designer, 
        // before the Open Report and Save Report dialogs are shown and after a new report is saved to a storage.

        return Directory.GetFiles(ReportDirectory, "*" + FileExtension)
            .Select(Path.GetFileNameWithoutExtension)
            .Union(ReportsFactory.Reports.Select(x => x.Key))
            .ToDictionary<string, string>(x => x);
    }

    public override void SetData(XtraReport report, string url)
    {
        // Stores the specified report to a Report Storage using the specified URL. 
        // This method is called only after the IsValidUrl and CanSetData methods are called.
        if (!IsWithinReportsFolder(url, ReportDirectory))
            throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Invalid report name.");
        report.SaveLayoutToXml(Path.Combine(ReportDirectory, url + FileExtension));
    }

    public override string SetNewData(XtraReport report, string defaultUrl)
    {
        // Stores the specified report using a new URL. 
        // The IsValidUrl and CanSetData methods are never called before this method. 
        // You can validate and correct the specified URL directly in the SetNewData method implementation 
        // and return the resulting URL used to save a report in your storage.
        SetData(report, defaultUrl);
        return defaultUrl;
    }
}
```

Add `CustomReportStorageWebExtension` to dependency injection

```csharp
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddDevExpressControls();
    services.AddScoped<ReportStorageWebExtension, CustomReportStorageWebExtension>();//add this line
```

## Client Side

1. Go to `package.json` and add following dependencies. (It is located in `angular` project) We have used the version `23.1.6` which is the latest version when this document is created. You can use the latest version.

```json
dependencies: [
    "devextreme": "^23.1.6",
    "devextreme-angular": "^23.1.6",    
    "@devexpress/analytics-core": "^23.1.6",
    "devexpress-reporting-angular": "^23.1.6"
]
```

*Note: Version of the nuget and npm packages should match*

2. Create a folder named `sample-report` under `src\app\admin` folder and then create a new component named `sample-report` as shown below.

*sample-report.component.html*

```html
<div [@routerTransition]>
    <div class="content d-flex flex-column flex-column-fluid">
        <sub-header [title]="'SampleReport' | localize">
        </sub-header>

        <div [class]="containerClass">
            <div class="card card-custom">
                <div class="card-body">
                    <dx-report-viewer [reportUrl]="reportUrl" height="400px">
                        <dxrv-request-options [invokeAction]="invokeAction" [host]="hostUrl"></dxrv-request-options>
                    </dx-report-viewer>
                </div>
            </div>
        </div>
    </div>
</div>
```

*sample-report.component.ts*

```typescript
import {Component, Injector, OnInit, ViewEncapsulation} from '@angular/core';
import {AppComponentBase} from "@shared/common/app-component-base";
import {appModuleAnimation} from "@shared/animations/routerTransition";

@Component({
    selector: 'app-sample-report',
    encapsulation: ViewEncapsulation.None,
    templateUrl: './sample-report.component.html',
    styleUrls: ['./sample-report.component.css',
        '../../../../node_modules/devextreme/dist/css/dx.common.css',
        '../../../../node_modules/devextreme/dist/css/dx.light.css',
        '../../../../node_modules/@devexpress/analytics-core/dist/css/dx-analytics.common.css',
        '../../../../node_modules/@devexpress/analytics-core/dist/css/dx-analytics.light.css',
        '../../../../node_modules/devexpress-reporting/dist/css/dx-webdocumentviewer.css'
    ],
    animations: [appModuleAnimation()]
})
export class SampleReportComponent extends AppComponentBase implements OnInit {

    title = 'DXReportViewerSample';
    reportUrl = 'SampleReport';
    hostUrl = 'https://localhost:44301/';
    invokeAction = 'DXXRDV';

    constructor(
        injector: Injector
    ) {
        super(injector);
    }

    ngOnInit(): void {
    }
}
```

3. Create `sample-report` module

   ```typescript
   import {NgModule} from '@angular/core';
   import {SampleReportRoutingModule} from './sample-report-routing.module';
   import {SampleReportComponent} from './sample-report.component';
   import {AppSharedModule} from "@app/shared/app-shared.module";
   import {AdminSharedModule} from "@app/admin/shared/admin-shared.module";
   import {DxReportViewerModule} from "@node_modules/devexpress-reporting-angular";

@NgModule({
       declarations: [SampleReportComponent],
       imports: [
           AppSharedModule,
           AdminSharedModule,
           SampleReportRoutingModule,
           DxReportViewerModule
       ],
       exports:[DxReportViewerModule]
   })
   export class SampleReportModule {
   }
   ```

4. Create `sample-report-routing.module.ts` routing module

   ```typescript
   import {NgModule} from '@angular/core';
   import {RouterModule, Routes} from '@angular/router';
   import {SampleReportComponent} from './sample-report.component';

   const routes: Routes = [{
       path: '',
       component: SampleReportComponent,
       pathMatch: 'full'
   }];

   @NgModule({
       imports: [RouterModule.forChild(routes)],
       exports: [RouterModule]
   })
   export class SampleReportRoutingModule {
   }
   ```

5. Add sample report route to `admin-routing.module.ts`

   ```typescript
   {
   	path: 'sample-report',
   	loadChildren: () => import('./sample-report/sample-report.module').then(m => m.SampleReportModule)
   }
   ```

You can also add `sample-report` to navigation menu or you can manually visit `http://localhost:4200/app/admin/sample-report`

## See the full documentation

Explore detailed guides, API references, and step-by-step tutorials for ASP.NET Zero.

[Browse documentation](https://aspnetzero.com/documents)

## Have questions before purchasing?

Schedule a free consultation with our team to find the right plan for your project.

[Schedule a meeting](https://aspnetzero.com/schedule-meeting)

## You might also like

### [Business Benefits of Using ASP.NET Zero for Enterprise Software Development](https://aspnetzero.com/blog/business-benefits-of-aspnetzero)

11 September 2026

### [Common Mistakes and Errors When Hosting ASP.NET Core Apps](https://aspnetzero.com/blog/common-mistakes-and-errors-hosting-aspnet-core-apps)

26 August 2026

### [Using AutoMapper After Migrating to Mapperly](https://aspnetzero.com/blog/using-automapper-after-migrating-to-mapperly)

04 August 2026

## Recent posts

### [Business Benefits of Using ASP.NET Zero for Enterprise Software Development](https://aspnetzero.com/blog/business-benefits-of-aspnetzero)

11 September 2026

### [Common Mistakes and Errors When Hosting ASP.NET Core Apps](https://aspnetzero.com/blog/common-mistakes-and-errors-hosting-aspnet-core-apps)

26 August 2026

### [Using AutoMapper After Migrating to Mapperly](https://aspnetzero.com/blog/using-automapper-after-migrating-to-mapperly)

04 August 2026

## Start building with ASP.NET Zero

Full source code, premium support, Angular, React & MVC UI options.

[View pricing](https://aspnetzero.com/pricing)

## Topics
