Save $100 with our Black Friday deals!

Get $100 OFF with promo code BLACKFRIDAY2024 at checkout until November 30.

See Pricing

Still using Radzen Studio?
Achieve more with Radzen Blazor Studio

Radzen Blazor Studio is our new flagship product and vision of how rapid Blazor application development should be done.

Go to Radzen Blazor Studio

Architecture (Angular)

This help article describes the structure and architecture of the typical Radzen Angular application.

Layers

Radzen applications have two layers:

  • Angular application

This is a client-side SPA (single-page application) that runs in the browser of your users. It supports responsive web design out of the box which makes it fit various device sizes - desktop, tablet and mobile.

  • .NET Core application

This is an optional server-side .NET Core application that exposes your MS SQL, MySQL, Oracle or PostgreSQL databases as OData services. OData is an open protocol that serves data in JSON format over RESTful API.

The .NET Core application also handles application security (authentication and authorization).

Folders

The Radzen application has three top-level directories:

client
meta
server

The client directory contains the client-side Angular application.

The meta directory contains JSON files that describe the application structure - data sources, pages, assets. Radzen generates the contents of the client and server directories from those files.

The server directory contains the server-side (.NET Core) application.

Angular application

The Radzen Angular application (output in the client directory) follows the Angular CLI guidelines:

client
src
app
<page-name>
<page-name>.component.html
<page-name>.component.ts
<page-name>.component.ts
<data-source>.model.ts
<data-source>.service.ts

Every Radzen page outpus three files in their own directory:

  1. <page-name>.component.html - the template of the page which contains the HTML markup of the UI components. This file is generated always. Any changes made to it by the user will be overwritten.
  2. <page-name>-generated.component.ts - TypeScript class that implements the Angular component - handles UI component events (e.g. button clicks), loads data from a RESTful API etc. This file is generated always. Any changes made to it by the user will be overwritten.
  3. <page-name>.component.ts - TypeScript class that inherits from the one defined in <page-name>-generated.component.ts. Its purpose is to allow extensibility via custom code.

Check customizing the application code for more details.

Angular Components

Radzen relies on the powerful PrimeNG component library. Charts are powered by D3 via the ngx-charts library.

Radzen users can also add custom Angular components or wrap 3rd party ones.

.NET Core Application

A server-side application is needed to support the following Radzen features:

  • Database access (MSSQL, MySQL, Oracle or Postgres)
  • Security (ActiveDirectory or built-in)
  • Export to Excel (CSV or XSLX format)

Data-access is achieved by using Entity Framework Core. The database tables are exposed as OData entities available in debug mode at the following URL: http://localhost:5000/odata/<data-source>/<entity> e.g. http://localhost:5000/odata/Northwind/Customers.

Every table (or database view) has a corresponding ODataController with CRUD operations generated out of the box. Here is an example:

[ODataRoutePrefix("odata/Northwind/Customers")]
public partial class CustomersController : ODataController
{
  private NorthwindContext context;

  public CustomersController(NorthwindContext context)
  {
    this.context = context;
  }

  // GET /odata/Northwind/Customers
  [EnableQuery(MaxExpansionDepth=10)]
  [HttpGet]
  public IEnumerable<Models.Northwind.Customer> GetCustomers()
  {
    var items = this.context.Customers.AsQueryable<Models.Northwind.Customer>();

    this.OnCustomersRead(ref items);

    return items;
  }

  partial void OnCustomersRead(ref IQueryable<Models.Northwind.Customer> items);

  [EnableQuery(MaxExpansionDepth=10)]
  [HttpGet("{CustomerID}")]
  public SingleResult<Customer> GetCustomer(string key)
  {
      var items = this.context.Customers.Where(i=>i.CustomerID == key);

      return SingleResult.Create(items);
  }

  partial void OnCustomerDeleted(Models.Northwind.Customer item);

  [HttpDelete("{CustomerID}")]
  public IActionResult DeleteCustomer(string key)
  {
      var item = this.context.Customers
          .Where(i => i.CustomerID == key)
          .Include(i => i.Orders)
          .Include(i => i.CustomerCustomerDemos)
          .SingleOrDefault();

      if (item == null)
      {
          return NotFound();
      }

      this.OnCustomerDeleted(item);
      this.context.Customers.Remove(item);
      this.context.SaveChanges();

      return new NoContentResult();
  }

  partial void OnCustomerUpdated(Models.Northwind.Customer item);

  [HttpPut("{CustomerID}")]
  public IActionResult PutCustomer(string key, [FromBody]Models.Northwind.Customer newItem)
  {
      if (newItem == null || (newItem.CustomerID != key))
      {
          return BadRequest();
      }

      this.OnCustomerUpdated(newItem);
      this.context.Customers.Update(newItem);
      this.context.SaveChanges();

      return new NoContentResult();
  }

  [HttpPatch("{CustomerID}")]
  public IActionResult PatchCustomer(string key, [FromBody]Delta<Models.Northwind.Customer> patch)
  {
      var item = this.context.Customers.Where(i=>i.CustomerID == key).FirstOrDefault();

      if (item == null)
      {
          return BadRequest();
      }

      patch.Patch(item);

      this.OnCustomerUpdated(item);
      this.context.Customers.Update(item);
      this.context.SaveChanges();

      return new NoContentResult();
  }

  partial void OnCustomerCreated(Models.Northwind.Customer item);

  [HttpPost]
  public IActionResult Post([FromBody] Models.Northwind.Customer item)
  {
      if (item == null)
      {
          return BadRequest();
      }

      this.OnCustomerCreated(item);
      this.context.Customers.Add(item);
      this.context.SaveChanges();

      return Created($"odata/Northwind/Customers/{item.CustomerID}", item);
  }
}

Radzen outputs the server-side application in the server directory in the following structure:

server
Startup.cs
Data
Models
<data-source>
<table>.cs
Controllers
<data-source>
<table>Controller.cs
  • Startup.cs contains initialization and configuration code. The class is partial to allow for further customization via partial methods.
  • The Data directory contains the Entity Framework Core DBContext classes that Radzen has generated for every data source.
  • The Models directory contains a sub-directory for every data source and then a class for every table e.g. Models\Northwind\Customer.cs - the model (class with properties) that corresponds to the Customers table from Northwind database.
  • The Controllers directory contains a sub-directory for every data source and then an OData controller for every table e.g. Controllers\Northwind\CustomersController.cs - the controller (with implemented CRUD operations) that exposes the Customers table from the Northwind database as an OData entity.

Most of the generated classes are partial to allow for customization via partial methods. More info is available in the customization help article.