Customize CRUD pages | Create a Blazor CRM application with Radzen (Blazor)
In this step from the CRM Application tutorial we will customize the automatically scaffolded CRUD pages.
- Start by adding some contacts
- Customize the Opportunities page
- Associate an opportunity with the current application user.
- Display the user first and last name in the opportunity DataGrid.
- Add icons for the Task Status and Task Type columns in the Tasks page.
- Add search capabilities to the Contacts page.
Add some contacts
The contact is a paramount entity in the RadzenCRM application as everything depends on it - opportunities, tasks etc.
Let’s add some contacts. We will use some famous members from the tech industry.
- Open the Contacts page in Radzen.
- Select the DataGrid component and remove the ID column as we don’t need it.
- Run the application from Radzen.
- Log in with
salesmanager@demo.radzen.com
andSalesManager1@
. Those are the credentials of the Jane Smith application user who is a Sales Manager (we added that user in the Customize security article). - Navigate to the Contacts page.
- Add two contacts:
- FirstName:
Bill
, LastName:Gates
, Email:bill.gates@microsoft.com
(probably not his real email), Company:Microsoft
. - FirstName:
Elon
, LastName:Musk
, Email:elon.musk@tesla.com
(maybe not real too), Company:Tesla
.
- FirstName:
Customize the Opportunities page
Associate opportunities with users
The first thing we will do is associate the opportunity with the current user and filter the opportunities so:
- Members of the Sales Representative role see only their opportunities.
- Members of the Sales Manager role see all opportunities.
We do that by customizing the server-side code of the Radzen application.
Server-side Blazor
- Stop the running application.
- Open
RadzenCrm.sln
with Vusual Studio (or theserver
directory with Visual Studio Code). - Add a new file
server\Services\CrmService.Custom.cs
with the following content.
using System.Linq;
using System.Linq.Dynamic.Core;
using RadzenCrm.Data;
using RadzenCrm.Models.Crm;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Components;
namespace RadzenCrm
{
public partial class CrmService
{
private readonly SecurityService security;
public CrmService(CrmContext context, NavigationManager navigationManager, SecurityService security)
: this(context, navigationManager)
{
this.security = security;
}
partial void OnOpportunityCreated(Opportunity item)
{
var userId = security.User.Id;
// Set the UserId property of the opportunity to the current user's id
item.UserId = userId;
}
partial void OnOpportunitiesRead(ref IQueryable<Opportunity> items)
{
if (!security.IsInRole("Sales Manager"))
{
var userId = security.User.Id;
// Filter the opportunities by the current user's id
items = items.Where(item => item.UserId == userId);
}
}
}
}
Client-side WebAssembly Blazor
- Stop the running application.
- Open
RadzenCrm.sln
with Vusual Studio (or theserver
directory with Visual Studio Code). - Add a new file
server\Controllers\CRM\OpportunitiesController.Custom.cs
with the following content.
using System;
using System.Data;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using BlazorCrmWasm.Models;
using Microsoft.AspNetCore.Identity;
namespace BlazorCrmWasm.Controllers.Crm
{
[Authorize]
public partial class OpportunitiesController
{
partial void OnOpportunitiesRead(ref IQueryable<Models.Crm.Opportunity> items)
{
var userManager = (UserManager<ApplicationUser>)HttpContext.RequestServices.GetService(typeof(UserManager<ApplicationUser>));
var user = userManager.GetUserAsync(HttpContext.User).Result;
if (user != null)
{
var roles = userManager.GetRolesAsync(user).Result;
if (!roles.Contains("Sales Manager"))
{
// Filter the opportunities by the current user's id
items = items.Where(item => item.UserId == user.Id);
}
}
}
partial void OnOpportunityCreated(Models.Crm.Opportunity item)
{
var userManager = (UserManager<ApplicationUser>)HttpContext.RequestServices.GetService(typeof(UserManager<ApplicationUser>));
var userId = userManager.GetUserId(HttpContext.User);
// Set the UserId property of the opportunity to the current user's id
item.UserId = userId;
}
}
}
Since we are manually setting the UserId of the opportunity we should remove the corresponding form field from the Add Opportunity and Edit Opportunity pages.
- Open the Add Opportunity page.
- Select the TemplateForm component.
- Delete the row that contains the UserId text box.
- Repeat steps 1 to 3 for the Edit Opportunity page.
Let’s try if it worked!
- Run the application from Radzen.
- Log in with
salesmanager@demo.radzen.com
andSalesManager1@
. - Add a new opportunity with the following details:
- Amount:
50000
- Contact:
bill.gates@microsoft.com
- Opportunity Status:
Active
- Close Date:
02/01/2019
- Name:
Microsoft deal
- Amount:
You should see this.
Display the first and last name in the Opportunities DataGrid
But what is that GUID in the User ID column? It is the Id of Jane Smith - the user we have logged in as. Let’s display something more user-friendy than a GUID.
Server-side Blazor
- Open the Opportunities page in Radzen.
- First we have to get the current user as an entity so we can use its properties in the DataGrid.
- Open
server\Services\CrmService.Custom.cs
with Visual Studio. - Append the following statement to the
OnOpportunitiesRead
method:items = items.Include(item => item.User);
. Here is how it should look like:
partial void OnOpportunitiesRead(ref IQueryable<Opportunity> items)
{
if (!security.IsInRole("Sales Manager"))
{
var userId = security.User.Id;
// Filter the opportunities by the current user's id
items = items.Where(item => item.UserId == userId);
}
// Include the User
items = items.Include(item => item.User);
}
Client-side WebAssembly Blazor
- Open the Opportunities page in Radzen.
- First we have to get the current user as an entity so we can use its properties in the DataGrid.
- Select the DataGrid component and edit its LoadData event handlers.
- Append
,User
in the$expand
parameter value. The $expand parameter is similar to a SQL JOIN (and in fact does exactly that behind the scenes). Click the Done button to close the Event Editor.
Now let’s customize the DataGrid!
- Go to the Properties tab of the property grid and edit the Template of the User ID column.
- Drag and drop a Label component. Set its Text property to
${data.User.FirstName}
. - Drag and drop another Label. Set its Text property to
${data.User.LastName}
. - Close the template editor by clicking the End template editing button.
- Finally set the FilterProperty and SortProperty of the User ID column to
User.FirstName
. This tells the DataGrid what property to sort by when the user changes the order by clicking the column header.
If you run the application now it should display the current user name instead of the GUID.
Add icons in the Tasks DataGrid
We will improve the look and feel of the Tasks page by using icons in the Task Status and Task Type DataGrid columns.
Run the application and add three tasks with the following properties
- Title:
Introduction
, Opportunity:Microsoft deal
, Due Date:02/02/2019
, Task Type:Email
, Task Status:Complete
. - Title:
Demonstration
, Opportunity:Microsoft deal
, Due Date:03/06/2019
, Task Type:Online Meeting
, Task Status:In Progress
. - Title:
Negotiation
, Opportunity:Microsoft deal
, Due Date:03/26/2019
, Task Type:Call
, Task Status:Not Started
.
Now lets add the icons for the Task Type column.
- Open the Tasks page in Radzen.
- Select the DataGrid component.
- Edit the Template property of the Task Type column.
- Drag and drop a Label component and set its Text property to
${data.TaskType.Name}
. - Drag and drop an Icon component. Set the Icon property to
settings_phone
(click the … button to open the Icon Picker dialog). Set the Visible property to${data.TaskType.Name == "Call"}
. This icon will be visible only when the current task type is equal toCall
. . Go to the Style tab of the property grid and set VerticalAlign tomiddle
. - Duplicate the existing icon by right-clicking it and picking the Duplicate option.
Change the Icon property to
video_call
and set the Visible property to${data.TaskType.Name == "Online Meeting"}
. - Duplicate the last icon. Change its Icon property to
email
and set the Visible property to${data.TaskType.Name == "Email"}
. - Click End template editing.
The Tasks page should now look like this.
Do the same for the Task Status column.
- Edit the Template of the Task Status column.
- Drag and drop a Label component and set its Text property to
${data.TaskStatus.Name}
. - Drag and drop an Icon component. Set the Icon property to
check_circle
(click the … button to open the Icon Picker dialog). Set the Visible property to${data.TaskStatus.Name == "Complete"}
. Go to the Style tab of the property grid and set VerticalAlign tomiddle
. - Duplicate the existing icon by right-clicking it and picking the Duplicate option.
Change the Icon property to
cancel
and set the Visible property to${data.TaskStatus.Name == "Not Started"}
. - Duplicate the last icon. Change its Icon property to
autorenew
and set the Visible property to${data.TaskStatus.Name == "In Progress"}
. - Click End template editing.
Here is how the end result should look like:
Implement contacts search
The final task from this help article is to add search capabilities to the Contacts page.
Add search UI components
First we will add the search UI components - a TextBox and a Button.
- Drag and drop a TextBox component.
- Create a page property called contactFilter. You can check the page properties help article for more info.
- Set the Value of the TextBox to
${contactFilter}
. Now when the user types in the TextBox thecontactFilter
property will update. - Drag and drop a Button component after the TextBox. Set its Text property to
Search
.
Implement the search
Server-side Blazor
- Add an event handler for the Click event of the search button.
- Set the Type of the event handler to Invoke data source method and Name to
getContacts
. - Open the Query builder to specify the filtering query which will do the search.
- Add a new Filter Group and set its logical operation to
OR
. - Add a new filter with the for the Company property, with
Contains
comparison and"${contactFilter}"
value. - Do the same for the FirstName, LastName and Email properties.
- Add a Set property action in the Then event. Set Name to
getContactsResult
and Value to${result}
.
Client-side WebAssembly Blazor
- Add an event handler for the Click event of the search button.
- Set the Type of the event handler to Execute C# and Code to
grid0.Reload()
. - Go to DataGrid LoadData Invoke data source method event handler, open the Query builder to specify the filtering query which will do the search. Clear existing filters.
- Add a new filter with the for the Company property, with
Contains
comparison and"${contactFilter}"
value. - Do the same for the FirstName, LastName and Email properties.
You can now search for contacts.
It is time to learn how to create a dashboard page with Radzen.