Using DevExtreme Components With the ABP Framework
Hi, in this step by step article, I will show you how to integrate DevExtreme components into ABP Framework-based applications.
(A screenshot from the example application developed in this article)
Create the Project
ABP Framework offers startup templates to get into the business faster. We can download a new startup template using ABP CLI:
abp new DevExtremeSample
After the download is finished, open the solution in the Visual Studio (or your favorite IDE):
Run the DevExtremeSample.DbMigrator
application to create the database and seed initial data (which creates the admin user, admin role, related permissions, etc). Then we can run the DevExtremeSample.Web
project to see our application working.
Default admin username is admin and password is 1q2w3E*
Install DevExtreme
You can follow this documentation to install DevExpress packages into your computer.
Don't forget to add "DevExpress NuGet Feed" to your Nuget Package Sources.
Adding DevExtreme NuGet Packages
Add the DevExtreme.AspNet.Core
NuGet package to the DevExtremeSample.Application.Contracts
project.
Install-Package DevExtreme.AspNet.Core
Add the DevExtreme.AspNet.Data
package to your DevExtremeSample.Web
project.
Install-Package DevExtreme.AspNet.Data
Adding DevExtreme NPM Dependencies
Open your DevExtremeSample.Web
project folder with a command line and add devextreme
and devextreme-aspnet-data
NPM packages:
npm install devextreme
npm install devextreme-aspnet-data
Adding Resource Mappings
The devextreme
and devextreme-aspnet-data
NPM packages are saved under node_modules
folder. We need to move the needed files in our wwwroot/libs
folder to use them in our web project. We can do it using the ABP client side resource mapping system.
Open the abp.resourcemapping.js
file in your DevExtremeSample.Web
project and add the following definitions to inside mappings
object.
"@node_modules/devextreme/dist/**/*": "@libs/devextreme/",
"@node_modules/devextreme-aspnet-data/js/dx.aspnet.data.js": "@libs/devextreme/js/"
The final abp.resourcemapping.js
file should look like below:
module.exports = {
aliases: {},
mappings: {
"@node_modules/devextreme/dist/**/*": "@libs/devextreme/",
"@node_modules/devextreme-aspnet-data/js/dx.aspnet.data.js": "@libs/devextreme/"
},
};
Open your DevExtremeSample.Web
project folder with a command line and run the abp install-libs
command. This command will copy the needed library files into the /wwwroot/libs/devextreme/
folder.
abp install-libs
You can see devextreme
folder inside the wwwroot/libs
:
Adding DevExtremeStyleContributor
We will add DevExtreme CSS files to the global bundle by creating a bundle contributor.
Create a Bundling
folder in the DevExtremeSample.Web
project and a DevExtremeStyleContributor.cs
file with the following content:
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace DevExtremeSample.Web.Bundling
{
public class DevExtremeStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/devextreme/css/dx.common.css");
context.Files.AddIfNotContains("/libs/devextreme/css/dx.light.css");
}
}
}
You can choose another theme than the light theme. Check the
/libs/devextreme/css/
folder and the DevExtreme documentation for other themes.
Open your DevExtremeSampleWebModule.cs
file in your DevExtremeSample.Web
project and add following code into the ConfigureServices
method:
Configure<AbpBundlingOptions>(options =>
{
options
.StyleBundles
.Get(StandardBundles.Styles.Global)
.AddContributors(typeof(DevExtremeStyleContributor));
});
Adding DevExtremeScriptContributor
We can not add DevExtreme js packages to Global Script Bundles, just like done for the CSS files. Because DevExtreme requires to add its JavaScript files into the <head>
section of the HTML document, while ABP Framework adds all JavaScript files to the end of the <body>
(as a best practice).
Fortunately, ABP Framework has a layout hook system that allows you to add any code into some specific positions in the HTML document. All you need to do is to create a ViewComponent
and configure the layout hooks.
Let's begin by creating a DevExtremeScriptContributor.cs
file in the Bundling
folder by copying the following code inside it:
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery;
using Volo.Abp.Modularity;
namespace DevExtremeSample.Web.Bundling
{
[DependsOn(
typeof(JQueryScriptContributor)
)]
public class DevExtremeScriptContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/devextreme/js/dx.all.js");
context.Files.AddIfNotContains("/libs/devextreme/js/dx.aspnet.mvc.js");
context.Files.AddIfNotContains("/libs/devextreme/js/dx.aspnet.data.js");
}
}
}
As you see, the DevExtremeScriptContributor
depends on JQueryScriptContributor
which adds JQuery related files before the DevExpress packages (see the bundling system for details).
Create DevExtremeJsViewComponent
Create a new view component, named DevExtremeJsViewComponent
inside the /Components/DevExtremeJs
folder of the Web project, by following the steps below:
- Create a
DevExtremeJsViewComponent
class inside the/Components/DevExtremeJs
(create the folders first):
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace DevExtremeSample.Web.Components.DevExtremeJs
{
public class DevExtremeJsViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View("/Components/DevExtremeJs/Default.cshtml");
}
}
}
- Create
Default.cshtml
file in the same folder with the following content:
@using DevExtremeSample.Web.Bundling
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
<abp-script type="typeof(DevExtremeScriptContributor)" />
Your final Web project should be like the following:
- Now, we can add this view component to
<head>
section by using the layout hooks.
Open your DevExtremeSampleWebModule.cs
file in your DevExtremeSample.Web
project and add following code into the ConfigureServices
method:
Configure<AbpLayoutHookOptions>(options =>
{
options.Add(
LayoutHooks.Head.Last, //The hook name
typeof(DevExtremeJsViewComponent) //The component to add
);
});
Known Issue: Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object.
This issue does exist in the ABP Framework v3.0 and earlier versions. If you are using ABP Framework v3.1 or a later version, you can skip this section.
When you run your *.Web
project, you will see an exception (Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object.
) at your console.
To fix that issue, download this file abp.jquery.js and replace with the wwwroot/libs/abp/jquery/abp.jquery.js
file of your Web project.
Result
The installation step was done. You can use any DevExtreme component in your application.
Example: A button and a progress bar component:
This example has been created by following this documentation.
The Sample Application
We have created a sample application with Tree List and Data Grid examples.
The Source Code
You can download the source code from here.
Data Grid
You can see the full working example of Data Grid.
The related files for this example are highlighted at the following screenshots.
Tree List
You can see the full working example of Tree List.
The related files for this example are highlighted at the following screenshots.
Additional Notes
Data Storage
I've used an in-memory list to store data for this example, instead of a real database. Because it is not related to DevExpress usage. There is a SampleDataService.cs
file in Data
folder at .Application.Contracts
project. All the data is stored here.
JSON Serialization
You can see some JsonProperty
attributes on the DTO properties. I use these attributes because DevExtreme example expects PascalCase
property names in the serialized JSON that is sent to the client. But ABP Framework & ASP.NET Core conventionally uses camelCase
property names on JSON serialization. Adding these JsonProperty
attributes ensures that the related properties are serialized as PascalCase
.
DevExtreme Components vs Application Service Methods
ABP Framework conventionally converts application services to API Controllers. For example, see the application service below:
public class OrderAppService : DevExtremeSampleAppService, IOrderAppService
{
public async Task<LoadResult> GetOrdersAsync(DataSourceLoadOptions loadOptions)
{
...
}
public async Task<Order> InsertOrder(string values)
{
...
}
...
}
You can use these service methods for your DevExtreme components as shown below:
Html.DevExtreme().DataGrid<Order>()
.DataSource(d => d.Mvc()
.Controller("Order") // Application Service Name without 'AppService'
.LoadAction("GetOrders") // Method Name without 'Async'
.InsertAction("InsertOrder")
.UpdateAction("UpdateOrder")
.DeleteAction("DeleteOrder")
.Key("OrderID")
)
Conclusion
In this article, I've explained how to use DevExtreme components in your application. ABP Framework is designed so that it can work with any UI library/framework.
Comments
Halil İbrahim Kalkan 215 weeks ago
Great article. Thanks for sharing.
EL HADI Abdelhalim 213 weeks ago
Great... can you provide an example with Abp + Angular + Devexterme. Regards
rcalv002 190 weeks ago
The only issue I found is that in your example you've created a model for Order which uses a customer OrderID, you then set the json property so it becomes PascalCase and therefore works with the datasource from devextreme, but really only because you're taking the input (for create forexample) as a string values which you then convert to object. But that's not how the documentation says to create our services, instead we should use strongly typed dtos to take data and give data.
rcalv002 190 weeks ago
Also most of the time we are using entities in the system that inherit the Id property for the abp entity, in this case, how do we get around this issue? Could you please speak to these points to have a real example where you can both retrieve data from the entities in the system and display on devextreme, as well as push back data using the regular services built using dtos?
David Crumb 109 weeks ago
My experience using abp.io and aspnetzero, it's .ID("Id")
Good luck!
pdpalma@hotmail.com 181 weeks ago
Hi! Great article. I wondering how integrate with database. I'm trying to implement it on this tutorial https://docs.abp.io/en/abp/latest/Tutorials/Part-3?UI=MVC&DB=EF
David Crumb 109 weeks ago
The methodology really isn't that different. I believe here are the steps. In your service/interface public asyncTask<LoadResult>GetBooksAsync(DataSourceLoadOptionsloadOptions)
The in your page the control would be something like: Html.DevExtreme().DataGrid<Book>().DataSource(d=>d.Mvc().Controller("Book")// Application Service Name without 'AppService'.LoadAction("GetBooks")// Method Name without 'Async'.InsertAction("InsertBook").UpdateAction("UpdateBook").DeleteAction("DeleteBook").Key("Id"))
Hope this helps
--Dave
Piseth San 169 weeks ago
Very nice! Thanks for all these. By the way, right now i cannot find DevExtreme.AspNet.Core in Nuget. How can i have it?
David Crumb 109 weeks ago
You have to have a DevExtpress/DevExtreme license to use these products. If you do have one, you should get a nuget api URL. You place that in your NuGet.config file. From there you should be able to go to NuGet Package Managersee the products you are licensed.
anunnaki 103 weeks ago
Greate! Thanks for all these.
koe61831@gmail.com 67 weeks ago
Although the code is not perfect, but for beginners or intermediate programmers, this article is very useful, so I think it's not worth getting to the bottom of the details. Everyone works as he wants and as he can. I often use stock photos from Depositphotos in my work, recently I used coaching images. As a content creator, I rely on Depositphotos to provide me with top-notch visuals for my works.
nia542376 23 weeks ago
fgdfg fg df