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:
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:
Every Radzen page outpus three files in their own directory:
<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.<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.<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:
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.