Browse Source

docs: separate the ASPNET Core into two sections that named User Interface and API

pull/3072/head
mehmet-erim 7 years ago
parent
commit
df133a6842
  1. 140
      docs/en/API/Auto-API-Controllers.md
  2. 165
      docs/en/API/Dynamic-CSharp-API-Clients.md
  3. 3
      docs/en/API/JavaScript-API/Auth.md
  4. 24
      docs/en/API/JavaScript-API/Index.md
  5. 141
      docs/en/AspNetCore/Auto-API-Controllers.md
  6. 352
      docs/en/AspNetCore/Bundling-Minification.md
  7. 116
      docs/en/AspNetCore/Client-Side-Package-Management.md
  8. 166
      docs/en/AspNetCore/Dynamic-CSharp-API-Clients.md
  9. 4
      docs/en/AspNetCore/JavaScript-API/Auth.md
  10. 25
      docs/en/AspNetCore/JavaScript-API/Index.md
  11. 4
      docs/en/AspNetCore/Tag-Helpers/Dynamic-Forms.md
  12. 4
      docs/en/AspNetCore/Tag-Helpers/Index.md
  13. 5
      docs/en/AspNetCore/Theming.md
  14. 505
      docs/en/AspNetCore/Widgets.md
  15. 4
      docs/en/Authorization.md
  16. 2
      docs/en/Blog-Posts/2018-09-24-Announcement/Post.md
  17. 2
      docs/en/Blog-Posts/2019-08-16 v0_19_Release/Post.md
  18. 4
      docs/en/Microservice-Architecture.md
  19. 4
      docs/en/Samples/Microservice-Demo.md
  20. 4
      docs/en/Startup-Templates/Application.md
  21. 6
      docs/en/Tutorials/Part-1.md
  22. 0
      docs/en/UI/Angular/AddingSettingTab.md
  23. 2
      docs/en/UI/Angular/ComponentReplacement.md
  24. 0
      docs/en/UI/Angular/Localization.md
  25. 0
      docs/en/UI/Angular/PermissionManagement.md
  26. 0
      docs/en/UI/Angular/images/custom-settings.png
  27. 0
      docs/en/UI/Angular/images/inaction.gif
  28. 352
      docs/en/UI/AspNetCore/Bundling-Minification.md
  29. 116
      docs/en/UI/AspNetCore/Client-Side-Package-Management.md
  30. 3
      docs/en/UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md
  31. 3
      docs/en/UI/AspNetCore/Tag-Helpers/Index.md
  32. 3
      docs/en/UI/AspNetCore/Theming.md
  33. 505
      docs/en/UI/AspNetCore/Widgets.md
  34. 53
      docs/en/docs-nav.json

140
docs/en/API/Auto-API-Controllers.md

@ -0,0 +1,140 @@
# Auto API Controllers
Once you create an [application service](../Application-Services.md), you generally want to create an API controller to expose this service as an HTTP (REST) API endpoint. A typical API controller does nothing but redirects method calls to the application service and configures the REST API using attributes like [HttpGet], [HttpPost], [Route]... etc.
ABP can **automagically** configure your application services as API Controllers by convention. Most of time you don't care about its detailed configuration, but it's possible to fully customize it.
## Configuration
Basic configuration is simple. Just configure `AbpAspNetCoreMvcOptions` and use `ConventionalControllers.Create` method as shown below:
````csharp
[DependsOn(BookStoreApplicationModule)]
public class BookStoreWebModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options
.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly);
});
}
}
````
This example code configures all the application services in the assembly containing the class `BookStoreApplicationModule`. The figure below shows the resulting API on the [Swagger UI](https://swagger.io/tools/swagger-ui/).
![bookstore-apis](../images/bookstore-apis.png)
### Examples
Some example method names and the corresponding routes calculated by convention:
| Service Method Name | HTTP Method | Route |
| ----------------------------------------------------- | ----------- | -------------------------- |
| GetAsync(Guid id) | GET | /api/app/book/{id} |
| GetListAsync() | GET | /api/app/book |
| CreateAsync(CreateBookDto input) | POST | /api/app/book |
| UpdateAsync(Guid id, UpdateBookDto input) | PUT | /api/app/book/{id} |
| DeleteAsync(Guid id) | DELETE | /api/app/book/{id} |
| GetEditorsAsync(Guid id) | GET | /api/app/book/{id}/editors |
| CreateEditorAsync(Guid id, BookEditorCreateDto input) | POST | /api/app/book/{id}/editor |
### HTTP Method
ABP uses a naming convention while determining the HTTP method for a service method (action):
- **Get**: Used if the method name starts with 'GetList', 'GetAll' or 'Get'.
- **Put**: Used if the method name starts with 'Put' or 'Update'.
- **Delete**: Used if the method name starts with 'Delete' or 'Remove'.
- **Post**: Used if the method name starts with 'Create', 'Add', 'Insert' or 'Post'.
- **Patch**: Used if the method name starts with 'Patch'.
- Otherwise, **Post** is used **by default**.
If you need to customize HTTP method for a particular method, then you can use one of the standard ASP.NET Core attributes ([HttpPost], [HttpGet], [HttpPut]... etc.). This requires to add [Microsoft.AspNetCore.Mvc.Core](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Core) nuget package to your project that contains the service.
### Route
Route is calculated based on some conventions:
* It always starts with '**/api**'.
* Continues with a **route path**. Default value is '**/app**' and can be configured as like below:
````csharp
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly, opts =>
{
opts.RootPath = "volosoft/book-store";
});
});
````
Then the route for getting a book will be '**/api/volosoft/book-store/book/{id}**'. This sample uses two-level root path, but you generally use a single level depth.
* Continues with the **normalized controller/service name**. Normalization removes 'AppService', 'ApplicationService' and 'Service' postfixes and converts it to **camelCase**. If your application service class name is 'BookAppService' then it becomes only '/book'.
* If you want to customize naming, then set the `UrlControllerNameNormalizer` option. It's a func delegate which allows you to determine the name per controller/service.
* If the method has an '**id**' parameter then it adds '**/{id}**' ro the route.
* Then it adds the action name if necessary. Action name is obtained from the method name on the service and normalized by;
* Removing '**Async**' postfix. If the method name is 'GetPhonesAsync' then it becomes 'GetPhones'.
* Removing **HTTP method prefix**. 'GetList', 'GetAll', 'Get', 'Put', 'Update', 'Delete', 'Remove', 'Create', 'Add', 'Insert', 'Post' and 'Patch' prefixes are removed based on the selected HTTP method. So, 'GetPhones' becomes 'Phones' since 'Get' prefix is a duplicate for a GET request.
* Converting the result to **camelCase**.
* If the resulting action name is **empty** then it's not added to the route. If it's not empty, it's added to the route (like '/phones'). For 'GetAllAsync' method name it will be empty, for 'GetPhonesAsync' method name it will be 'phones'.
* Normalization can be customized by setting the `UrlActionNameNormalizer` option. It's an action delegate that is called for every method.
* If there is another parameter with 'Id' postfix, then it's also added to the route as the final route segment (like '/phoneId').
## Service Selection
Creating conventional HTTP API controllers are not unique to application services actually.
### IRemoteService Interface
If a class implements the `IRemoteService` interface then it's automatically selected to be a conventional API controller. Since application services inherently implement it, they are considered as natural API controllers.
### RemoteService Attribute
`RemoteService` attribute can be used to mark a class as a remote service or disable for a particular class that inherently implements the `IRemoteService` interface. Example:
````csharp
[RemoteService(IsEnabled = false)] //or simply [RemoteService(false)]
public class PersonAppService : ApplicationService
{
}
````
### TypePredicate Option
You can further filter classes to become an API controller by providing the `TypePredicate` option:
````csharp
services.Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly, opts =>
{
opts.TypePredicate = type => { return true; };
});
});
````
Instead of returning `true` for every type, you can check it and return `false` if you don't want to expose this type as an API controller.
## API Explorer
API Exploring a service that makes possible to investigate API structure by the clients. Swagger uses it to create a documentation and test UI for an endpoint.
API Explorer is automatically enabled for conventional HTTP API controllers by default. Use `RemoteService` attribute to control it per class or method level. Example:
````csharp
[RemoteService(IsMetadataEnabled = false)]
public class PersonAppService : ApplicationService
{
}
````
Disabled `IsMetadataEnabled` which hides this service from API explorer and it will not be discoverable. However, it still can be usable for the clients know the exact API path/route.

165
docs/en/API/Dynamic-CSharp-API-Clients.md

@ -0,0 +1,165 @@
# Dynamic C# API Clients
ABP can dynamically create C# API client proxies to call remote HTTP services (REST APIs). In this way, you don't need to deal with `HttpClient` and other low level HTTP features to call remote services and get results.
## Service Interface
Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project. Example:
````csharp
public interface IBookAppService : IApplicationService
{
Task<List<BookDto>> GetListAsync();
}
````
Your interface should implement the `IRemoteService` interface to be automatically discovered. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookAppService` above satisfies this condition.
Implement this class in your service application. You can use [auto API controller system](Auto-API-Controllers.md) to expose the service as a REST API endpoint.
## Client Proxy Generation
First, add [Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget package to your client project:
````
Install-Package Volo.Abp.Http.Client
````
Then add `AbpHttpClientModule` dependency to your module:
````csharp
[DependsOn(typeof(AbpHttpClientModule))] //add the dependency
public class MyClientAppModule : AbpModule
{
}
````
Now, it's ready to create the client proxies. Example:
````csharp
[DependsOn(
typeof(AbpHttpClientModule), //used to create client proxies
typeof(BookStoreApplicationModule) //contains the application service interfaces
)]
public class MyClientAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
//Create dynamic client proxies
context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationModule).Assembly
);
}
}
````
`AddHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, creates and registers proxy classes.
### Endpoint Configuration
`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. Simplest configuration is shown below:
````
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://localhost:53929/"
}
}
}
````
See the "RemoteServiceOptions" section below for more detailed configuration.
## Usage
It's straightforward to use. Just inject the service interface in the client application code:
````csharp
public class MyService : ITransientDependency
{
private readonly IBookAppService _bookService;
public MyService(IBookAppService bookService)
{
_bookService = bookService;
}
public async Task DoIt()
{
var books = await _bookService.GetListAsync();
foreach (var book in books)
{
Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}");
}
}
}
````
This sample injects the `IBookAppService` service interface defined above. The dynamic client proxy implementation makes an HTTP call whenever a service method is called by the client.
### IHttpClientProxy Interface
While you can inject `IBookAppService` like above to use the client proxy, you could inject `IHttpClientProxy<IBookAppService>` for a more explicit usage. In this case you will use the `Service` property of the `IHttpClientProxy<T>` interface.
## Configuration
### RemoteServiceOptions
`RemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<RemoteServiceOptions>(options =>
{
options.RemoteServices.Default =
new RemoteServiceConfiguration("http://localhost:53929/");
});
//...
}
````
### Multiple Remote Service Endpoints
The examples above have configured the "Default" remote service endpoint. You may have different endpoints for different services (as like in a microservice approach where each microservice has different endpoints). In this case, you can add other endpoints to your configuration file:
````json
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://localhost:53929/"
},
"BookStore": {
"BaseUrl": "http://localhost:48392/"
}
}
}
````
`AddHttpClientProxies` method can get an additional parameter for the remote service name. Example:
````csharp
context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationModule).Assembly,
remoteServiceName: "BookStore"
);
````
`remoteServiceName` parameter matches the service endpoint configured via `RemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint.
### As Default Services
When you create a service proxy for `IBookAppService`, you can directly inject the `IBookAppService` to use the proxy client (as shown in the usage section). You can pass `asDefaultServices: false` to the `AddHttpClientProxies` method to disable this feature.
````csharp
context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationModule).Assembly,
asDefaultServices: false
);
````
Using `asDefaultServices: false` may only be needed if your application has already an implementation of the service and you do not want to override/replace the other implementation by your client proxy.
> If you disable `asDefaultServices`, you can only use `IHttpClientProxy<T>` interface to use the client proxies (see the related section above).

3
docs/en/API/JavaScript-API/Auth.md

@ -0,0 +1,3 @@
# abp.auth JavaScript API
TODO

24
docs/en/API/JavaScript-API/Index.md

@ -0,0 +1,24 @@
# JavaScript API
ABP provides some JavaScript APIs for ASP.NET Core MVC / Razor Pages applications. They can be used to perform some common application requirements in the client side.
## APIs
* abp.ajax
* [abp.auth](Auth.md)
* abp.currentUser
* abp.dom
* abp.event
* abp.features
* abp.localization
* abp.log
* abp.ModalManager
* abp.notify
* abp.security
* abp.setting
* abp.ui
* abp.utils
* abp.ResourceLoader
* abp.WidgetManager
* Other APIs

141
docs/en/AspNetCore/Auto-API-Controllers.md

@ -1,140 +1,3 @@
# Auto API Controllers
This document has moved.
Once you create an [application service](../Application-Services.md), you generally want to create an API controller to expose this service as an HTTP (REST) API endpoint. A typical API controller does nothing but redirects method calls to the application service and configures the REST API using attributes like [HttpGet], [HttpPost], [Route]... etc.
ABP can **automagically** configure your application services as API Controllers by convention. Most of time you don't care about its detailed configuration, but it's possible to fully customize it.
## Configuration
Basic configuration is simple. Just configure `AbpAspNetCoreMvcOptions` and use `ConventionalControllers.Create` method as shown below:
````csharp
[DependsOn(BookStoreApplicationModule)]
public class BookStoreWebModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options
.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly);
});
}
}
````
This example code configures all the application services in the assembly containing the class `BookStoreApplicationModule`. The figure below shows the resulting API on the [Swagger UI](https://swagger.io/tools/swagger-ui/).
![bookstore-apis](../images/bookstore-apis.png)
### Examples
Some example method names and the corresponding routes calculated by convention:
| Service Method Name | HTTP Method | Route |
| ----------------------------------------------------- | ----------- | -------------------------- |
| GetAsync(Guid id) | GET | /api/app/book/{id} |
| GetListAsync() | GET | /api/app/book |
| CreateAsync(CreateBookDto input) | POST | /api/app/book |
| UpdateAsync(Guid id, UpdateBookDto input) | PUT | /api/app/book/{id} |
| DeleteAsync(Guid id) | DELETE | /api/app/book/{id} |
| GetEditorsAsync(Guid id) | GET | /api/app/book/{id}/editors |
| CreateEditorAsync(Guid id, BookEditorCreateDto input) | POST | /api/app/book/{id}/editor |
### HTTP Method
ABP uses a naming convention while determining the HTTP method for a service method (action):
- **Get**: Used if the method name starts with 'GetList', 'GetAll' or 'Get'.
- **Put**: Used if the method name starts with 'Put' or 'Update'.
- **Delete**: Used if the method name starts with 'Delete' or 'Remove'.
- **Post**: Used if the method name starts with 'Create', 'Add', 'Insert' or 'Post'.
- **Patch**: Used if the method name starts with 'Patch'.
- Otherwise, **Post** is used **by default**.
If you need to customize HTTP method for a particular method, then you can use one of the standard ASP.NET Core attributes ([HttpPost], [HttpGet], [HttpPut]... etc.). This requires to add [Microsoft.AspNetCore.Mvc.Core](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Core) nuget package to your project that contains the service.
### Route
Route is calculated based on some conventions:
* It always starts with '**/api**'.
* Continues with a **route path**. Default value is '**/app**' and can be configured as like below:
````csharp
Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly, opts =>
{
opts.RootPath = "volosoft/book-store";
});
});
````
Then the route for getting a book will be '**/api/volosoft/book-store/book/{id}**'. This sample uses two-level root path, but you generally use a single level depth.
* Continues with the **normalized controller/service name**. Normalization removes 'AppService', 'ApplicationService' and 'Service' postfixes and converts it to **camelCase**. If your application service class name is 'BookAppService' then it becomes only '/book'.
* If you want to customize naming, then set the `UrlControllerNameNormalizer` option. It's a func delegate which allows you to determine the name per controller/service.
* If the method has an '**id**' parameter then it adds '**/{id}**' ro the route.
* Then it adds the action name if necessary. Action name is obtained from the method name on the service and normalized by;
* Removing '**Async**' postfix. If the method name is 'GetPhonesAsync' then it becomes 'GetPhones'.
* Removing **HTTP method prefix**. 'GetList', 'GetAll', 'Get', 'Put', 'Update', 'Delete', 'Remove', 'Create', 'Add', 'Insert', 'Post' and 'Patch' prefixes are removed based on the selected HTTP method. So, 'GetPhones' becomes 'Phones' since 'Get' prefix is a duplicate for a GET request.
* Converting the result to **camelCase**.
* If the resulting action name is **empty** then it's not added to the route. If it's not empty, it's added to the route (like '/phones'). For 'GetAllAsync' method name it will be empty, for 'GetPhonesAsync' method name it will be 'phones'.
* Normalization can be customized by setting the `UrlActionNameNormalizer` option. It's an action delegate that is called for every method.
* If there is another parameter with 'Id' postfix, then it's also added to the route as the final route segment (like '/phoneId').
## Service Selection
Creating conventional HTTP API controllers are not unique to application services actually.
### IRemoteService Interface
If a class implements the `IRemoteService` interface then it's automatically selected to be a conventional API controller. Since application services inherently implement it, they are considered as natural API controllers.
### RemoteService Attribute
`RemoteService` attribute can be used to mark a class as a remote service or disable for a particular class that inherently implements the `IRemoteService` interface. Example:
````csharp
[RemoteService(IsEnabled = false)] //or simply [RemoteService(false)]
public class PersonAppService : ApplicationService
{
}
````
### TypePredicate Option
You can further filter classes to become an API controller by providing the `TypePredicate` option:
````csharp
services.Configure<AbpAspNetCoreMvcOptions>(options =>
{
options.ConventionalControllers
.Create(typeof(BookStoreApplicationModule).Assembly, opts =>
{
opts.TypePredicate = type => { return true; };
});
});
````
Instead of returning `true` for every type, you can check it and return `false` if you don't want to expose this type as an API controller.
## API Explorer
API Exploring a service that makes possible to investigate API structure by the clients. Swagger uses it to create a documentation and test UI for an endpoint.
API Explorer is automatically enabled for conventional HTTP API controllers by default. Use `RemoteService` attribute to control it per class or method level. Example:
````csharp
[RemoteService(IsMetadataEnabled = false)]
public class PersonAppService : ApplicationService
{
}
````
Disabled `IsMetadataEnabled` which hides this service from API explorer and it will not be discoverable. However, it still can be usable for the clients know the exact API path/route.
[Click to navigate to Auto API Controllers document](../API/Auto-API-Controllers.md)

352
docs/en/AspNetCore/Bundling-Minification.md

@ -1,352 +1,4 @@
# ASP.NET Core MVC Bundling & Minification
This document has moved.
There are many ways of bundling & minification of client side resources (JavaScript and CSS files). Most common ways are:
* Using the [Bundler & Minifier](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier) Visual Studio extension or the [NuGet package](https://www.nuget.org/packages/BuildBundlerMinifier/).
* Using [Gulp](https://gulpjs.com/)/[Grunt](https://gruntjs.com/) task managers and their plugins.
ABP offers a simple, dynamic, powerful, modular and built-in way.
## Volo.Abp.AspNetCore.Mvc.UI.Bundling Package
> This package is already installed by default with the startup templates. So, most of the time, you don't need to install it manually.
Install the `Volo.Abp.AspNetCore.Mvc.UI.Bundling` nuget package to your project:
````
install-package Volo.Abp.AspNetCore.Mvc.UI.Bundling
````
Then you can add the `AbpAspNetCoreMvcUiBundlingModule` dependency to your module:
````C#
using Volo.Abp.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))]
public class MyWebModule : AbpModule
{
//...
}
}
````
## Razor Bundling Tag Helpers
The simplest way of creating a bundle is to use `abp-script-bundle` or `abp-style-bundle` tag helpers. Example:
````html
<abp-style-bundle name="MyGlobalBundle">
<abp-style src="/libs/bootstrap/css/bootstrap.css" />
<abp-style src="/libs/font-awesome/css/font-awesome.css" />
<abp-style src="/libs/toastr/toastr.css" />
<abp-style src="/styles/my-global-style.css" />
</abp-style-bundle>
````
This bundle defines a style bundle with a **unique name**: `MyGlobalBundle`. It's very easy to understand how to use it. Let's see how it *works*:
* ABP creates the bundle as **lazy** from the provided files when it's **first requested**. For the subsequent calls, it's returned from the **cache**. That means if you conditionally add the files to the bundle, it's executed only once and any changes of the condition will not effect the bundle for the next requests.
* ABP adds bundle files **individually** to the page for the `development` environment. It automatically bundles & minifies for other environments (`staging`, `production`...).
* The bundle files may be **physical** files or [**virtual/embedded** files](../Virtual-File-System.md).
* ABP automatically adds **version query string** to the bundle file URL to prevent browsers from caching when the bundle is being updated. (like ?_v=67872834243042 - generated from last change date of the related files). The versioning works even if the bundle files are individually added to the page (on the development environment).
### Importing The Bundling Tag Helpers
> This is already imported by default with the startup templates. So, most of the time, you don't need to add it manually.
In order to use bundle tag helpers, you need to add it into your `_ViewImports.cshtml` file or into your page:
````
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
````
### Unnamed Bundles
The `name` is **optional** for the razor bundle tag helpers. If you don't define a name, it's automatically **calculated** based on the used bundle file names (they are **concatenated** and **hashed**). Example:
````html
<abp-style-bundle>
<abp-style src="/libs/bootstrap/css/bootstrap.css" />
<abp-style src="/libs/font-awesome/css/font-awesome.css" />
<abp-style src="/libs/toastr/toastr.css" />
@if (ViewBag.IncludeCustomStyles != false)
{
<abp-style src="/styles/my-global-style.css" />
}
</abp-style-bundle>
````
This will potentially create **two different bundles** (one incudes the `my-global-style.css` and other does not).
Advantages of **unnamed** bundles:
* Can **conditionally add items** to the bundle. But this may lead to multiple variations of the bundle based on the conditions.
Advantages of **named** bundles:
* Other **modules can contribute** to the bundle by its name (see the sections below).
### Single File
If you need to just add a single file to the page, you can use the `abp-script` or `abp-style` tag without a wrapping in the `abp-script-bundle` or `abp-style-bundle` tag. Example:
````xml
<abp-script src="/scripts/my-script.js" />
````
The bundle name will be *scripts.my-scripts* for the example above ("/" is replaced by "."). All bundling features are work as expected for single file bundles too.
## Bundling Options
If you need to use same bundle in **multiple pages** or want to use some more **powerful features**, you can configure bundles **by code** in your [module](../Module-Development-Basics.md) class.
### Creating A New Bundle
Example usage:
````C#
[DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))]
public class MyWebModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBundlingOptions>(options =>
{
options
.ScriptBundles
.Add("MyGlobalBundle", bundle => {
bundle.AddFiles(
"/libs/jquery/jquery.js",
"/libs/bootstrap/js/bootstrap.js",
"/libs/toastr/toastr.min.js",
"/scripts/my-global-scripts.js"
);
});
});
}
}
````
> You can use the same name (*MyGlobalBundle* here) for a script & style bundle since they are added to different collections (`ScriptBundles` and `StyleBundles`).
After defining such a bundle, it can be included into a page using the same tag helpers defined above. Example:
````html
<abp-script-bundle name="MyGlobalBundle" />
````
This time, no file defined in the tag helper definition because the bundle files are defined by the code.
### Configuring An Existing Bundle
ABP supports [modularity](../Module-Development-Basics.md) for bundling as well. A module can modify an existing bundle that is created by a dependant module. Example:
````C#
[DependsOn(typeof(MyWebModule))]
public class MyWebExtensionModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBundlingOptions>(options =>
{
options
.ScriptBundles
.Configure("MyGlobalBundle", bundle => {
bundle.AddFiles(
"/scripts/my-extension-script.js"
);
});
});
}
}
````
> It's not possible to configure unnamed bundle tag helpers by code, because their name are not known at the development time. It's suggested to always use a name for a bundle tag helper.
## Bundle Contributors
Adding files to an existing bundle seems useful. What if you need to **replace** a file in the bundle or you want to **conditionally** add files? Defining a bundle contributor provides extra power for such cases.
An example bundle contributor that replaces bootstrap.css with a customized version:
````C#
public class MyExtensionGlobalStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.ReplaceOne(
"/libs/bootstrap/css/bootstrap.css",
"/styles/extensions/bootstrap-customized.css"
);
}
}
````
Then you can use this contributor as like below:
````C#
services.Configure<AbpBundlingOptions>(options =>
{
options
.ScriptBundles
.Configure("MyGlobalBundle", bundle => {
bundle.AddContributors(typeof(MyExtensionGlobalStyleContributor));
});
});
````
> You can also add contributors while creating a new bundle.
Contributors can also be used in the bundle tag helpers. Example:
````xml
<abp-style-bundle>
<abp-style type="@typeof(BootstrapStyleContributor)" />
<abp-style src="/libs/font-awesome/css/font-awesome.css" />
<abp-style src="/libs/toastr/toastr.css" />
</abp-style-bundle>
````
`abp-style` and `abp-script` tags can get `type` attributes (instead of `src` attributes) as shown in this sample. When you add a bundle contributor, its dependencies are also automatically added to the bundle.
### Contributor Dependencies
A bundle contributor can have one or more dependencies to other contributors.
Example:
````C#
[DependsOn(typeof(MyDependedBundleContributor))] //Define the dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{
//...
}
````
When a bundle contributor is added, its dependencies are **automatically and recursively** added. Dependencies added by the **dependency order** by preventing **duplicates**. Duplicates are prevented even if they are in separated bundles. ABP organizes all bundles in a page and eliminates duplications.
Creating contributors and defining dependencies is a way of organizing bundle creation across different modules.
### Contributor Extensions
In some advanced scenarios, you may want to do some additional configuration whenever a bundle contributor is used. Contributor extensions works seamlessly when the extended contributor is used.
The example below adds some styles for prism.js library:
````csharp
public class MyPrismjsStyleExtension : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/prismjs/plugins/toolbar/prism-toolbar.css");
}
}
````
Then you can configure `BundleContributorOptions` to extend existing `PrismjsStyleBundleContributor`.
````csharp
Configure<BundleContributorOptions>(options =>
{
options
.Extensions<PrismjsStyleBundleContributor>()
.Add<MyPrismjsStyleExtension>();
});
````
Whenever `PrismjsStyleBundleContributor` is added into a bundle, `MyPrismjsStyleExtension` will also be automatically added.
### Accessing to the IServiceProvider
While it is rarely needed, `BundleConfigurationContext` has a `ServiceProvider` property that you can resolve service dependencies inside the `ConfigureBundle` method.
### Standard Package Contributors
Adding a specific NPM package resource (js, css files) into a bundle is pretty straight forward for that package. For example you always add the `bootstrap.css` file for the bootstrap NPM package.
There are built-in contributors for all [standard NPM packages](Client-Side-Package-Management.md). For example, if your contributor depends on the bootstrap, you can just declare it, instead of adding the bootstrap.css yourself.
````C#
[DependsOn(typeof(BootstrapStyleContributor))] //Define the bootstrap style dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{
//...
}
````
Using the built-in contributors for standard packages;
* Prevents you typing **the invalid resource paths**.
* Prevents changing your contributor if the resource **path changes** (the dependant contributor will handle it).
* Prevents multiple modules adding the **duplicate files**.
* Manages **dependencies recursively** (adds dependencies of dependencies, if necessary).
#### Volo.Abp.AspNetCore.Mvc.UI.Packages Package
> This package is already installed by default in the startup templates. So, most of the time, you don't need to install it manually.
Standard package contributors are defined in the `Volo.Abp.AspNetCore.Mvc.UI.Packages` NuGet package.
To install it to your project:
````
install-package Volo.Abp.AspNetCore.Mvc.UI.Packages
````
Then add the `AbpAspNetCoreMvcUiPackagesModule` module dependency to your own module;
````C#
using Volo.Abp.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpAspNetCoreMvcUiPackagesModule))]
public class MyWebModule : AbpModule
{
//...
}
}
````
### Bundle Inheritance
In some specific cases, it may be needed to create a **new** bundle **inherited** from other bundle(s). Inheriting from a bundle (recursively) inherits all files/contributors of that bundle. Then the derived bundle can add or modify files/contributors **without modifying** the original bundle.
Example:
````c#
services.Configure<AbpBundlingOptions>(options =>
{
options
.StyleBundles
.Add("MyTheme.MyGlobalBundle", bundle => {
bundle
.AddBaseBundles("MyGlobalBundle") //Can add multiple
.AddFiles(
"/styles/mytheme-global-styles.css"
);
});
});
````
## Themes
Themes uses the standard package contributors to add library resources to page layouts. Themes may also define some standard/global bundles, so any module can contribute to these standard/global bundles. See the [theming documentation](Theming.md) for more.
## Best Practices & Suggestions
It's suggested to define multiple bundles for an application, each one is used for different purposes.
* **Global bundle**: Global style/script bundles are included to every page in the application. Themes already defines global style & script bundles. Your module can contribute to them.
* **Layout bundles**: This is a specific bundle to an individual layout. Only contains resources shared among all the pages use the layout. Use the bundling tag helpers to create the bundle as a good practice.
* **Module bundles**: For shared resources among the pages of an individual module.
* **Page bundles**: Specific bundles created for each page. Use the bundling tag helpers to create the bundle as a best practice.
Establish a balance between performance, network bandwidth usage and count of many bundles.
## See Also
* [Client Side Package Management](Client-Side-Package-Management.md)
* [Theming](Theming.md)
[Click to navigate to ASP.NET Core MVC Bundling & Minification document](../UI/AspNetCore/Bundling-Minification.md)

116
docs/en/AspNetCore/Client-Side-Package-Management.md

@ -1,116 +1,4 @@
## ASP.NET Core MVC Client Side Package Management
This document has moved.
ABP framework can work with any type of client side package management systems. You can even decide to use no package management system and manage your dependencies manually.
However, ABP framework works best with **NPM/Yarn**. By default, built-in modules are configured to work with NPM/Yarn.
Finally, we suggest the [**Yarn**](https://yarnpkg.com/) over the NPM since it's faster, stable and also compatible with the NPM.
### @ABP NPM Packages
ABP is a modular platform. Every developer can create modules and the modules should work together in a **compatible** and **stable** state.
One challenge is the **versions of the dependant NPM packages**. What if two different modules use the same JavaScript library but its different (and potentially incompatible) versions.
To solve the versioning problem, we created a **standard set of packages** those depends on some common third-party libraries. Some example packages are [@abp/jquery](https://www.npmjs.com/package/@abp/jquery), [@abp/bootstrap](https://www.npmjs.com/package/@abp/bootstrap) and [@abp/font-awesome](https://www.npmjs.com/package/@abp/font-awesome). You can see the **list of packages** from the [Github repository](https://github.com/volosoft/abp/tree/master/npm/packs).
The benefit of a **standard package** is:
* It depends on a **standard version** of a package. Depending on this package is **safe** because all modules depend on the same version.
* It contains the gulp task to copy library resources (js, css, img... files) from the **node_modules** folder to **wwwroot/libs** folder. See the *Mapping The Library Resources* section for more.
Depending on a standard package is easy. Just add it to your **package.json** file like you normally do. Example:
````
{
...
"dependencies": {
"@abp/bootstrap": "^1.0.0"
}
}
````
It's suggested to depend on a standard package instead of directly depending on a third-party package.
#### Package Installation
After depending on a NPM package, all you should do is to run the **yarn** command from the command line to install all the packages and their dependencies:
````
yarn
````
Alternatively, you can use `npm install` but [Yarn](https://yarnpkg.com/) is suggested as mentioned before.
#### Package Contribution
If you need a third-party NPM package that is not in the standard set of packages, you can create a Pull Request on the Github [repository](https://github.com/volosoft/abp). A pull request that follows these rules is accepted:
* Package name should be named as `@abp/package-name` for a `package-name` on NPM (example: `@abp/bootstrap` for the `bootstrap` package).
* It should be the **latest stable** version of the package.
* It should only depend a **single** third-party package. It can depend on multiple `@abp/*` packages.
* The package should include a `abp.resourcemapping.js` file formatted as defined in the *Mapping The Library Resources* section. This file should only map resources for the depended package.
* You also need to create [bundle contributor(s)](Bundling-Minification.md) for the package you have created.
See current standard packages for examples.
### Mapping The Library Resources
Using NPM packages and NPM/Yarn tool is the de facto standard for client side libraries. NPM/Yarn tool creates a **node_modules** folder in the root folder of your web project.
Next challenge is copying needed resources (js, css, img... files) from the `node_modules` into a folder inside the **wwwroot** folder to make it accessible to the clients/browsers.
ABP defines a [Gulp](https://gulpjs.com/) based task to **copy resources** from **node_modules** to **wwwroot/libs** folder. Each **standard package** (see the *@ABP NPM Packages* section) defines the mapping for its own files. So, most of the time, you only configure dependencies.
The **startup templates** are already configured to work all these out of the box. This section will explain the configuration options.
#### Resource Mapping Definition File
A module should define a JavaScript file named `abp.resourcemapping.js` which is formatted as in the example below:
````js
module.exports = {
aliases: {
"@node_modules": "./node_modules",
"@libs": "./wwwroot/libs"
},
clean: [
"@libs"
],
mappings: {
}
}
````
* **aliases** section defines standard aliases (placeholders) that can be used in the mapping paths. **@node_modules** and **@libs** are required (by the standard packages), you can define your own aliases to reduce duplication.
* **clean** section is a list of folders to clean before copying the files.
* **mappings** section is a list of mappings of files/folders to copy. This example does not copy any resource itself, but depends on a standard package.
An example mapping configuration is shown below:
````js
mappings: {
"@node_modules/bootstrap/dist/css/bootstrap.css": "@libs/bootstrap/css/",
"@node_modules/bootstrap/dist/js/bootstrap.bundle.js": "@libs/bootstrap/js/",
"@node_modules/bootstrap-datepicker/dist/locales/*.*": "@libs/bootstrap-datepicker/locales/"
}
````
#### Using The Gulp
Once you properly configure the `abp.resourcemapping.js` file, you can run the gulp command from the command line:
````
gulp
````
When you run the `gulp`, all packages will copy their own resources into the **wwwroot/libs** folder. Running `yarn & gulp` is only necessary if you make a change in your dependencies in the **package.json** file.
> When you run the Gulp command, dependencies of the application are resolved using the package.json file. The Gulp task automatically discovers and maps all resources from all dependencies (recursively).
#### See Also
* [Bundling & Minification](Bundling-Minification.md)
* [Theming](Theming.md)
[Click to navigate to ASP.NET Core MVC Client Side Package Management document](../UI/AspNetCore/Client-Side-Package-Management.md)

166
docs/en/AspNetCore/Dynamic-CSharp-API-Clients.md

@ -1,165 +1,3 @@
# Dynamic C# API Clients
This document has moved.
ABP can dynamically create C# API client proxies to call remote HTTP services (REST APIs). In this way, you don't need to deal with `HttpClient` and other low level HTTP features to call remote services and get results.
## Service Interface
Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project. Example:
````csharp
public interface IBookAppService : IApplicationService
{
Task<List<BookDto>> GetListAsync();
}
````
Your interface should implement the `IRemoteService` interface to be automatically discovered. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookAppService` above satisfies this condition.
Implement this class in your service application. You can use [auto API controller system](Auto-API-Controllers.md) to expose the service as a REST API endpoint.
## Client Proxy Generation
First, add [Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget package to your client project:
````
Install-Package Volo.Abp.Http.Client
````
Then add `AbpHttpClientModule` dependency to your module:
````csharp
[DependsOn(typeof(AbpHttpClientModule))] //add the dependency
public class MyClientAppModule : AbpModule
{
}
````
Now, it's ready to create the client proxies. Example:
````csharp
[DependsOn(
typeof(AbpHttpClientModule), //used to create client proxies
typeof(BookStoreApplicationModule) //contains the application service interfaces
)]
public class MyClientAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
//Create dynamic client proxies
context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationModule).Assembly
);
}
}
````
`AddHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, creates and registers proxy classes.
### Endpoint Configuration
`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. Simplest configuration is shown below:
````
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://localhost:53929/"
}
}
}
````
See the "RemoteServiceOptions" section below for more detailed configuration.
## Usage
It's straightforward to use. Just inject the service interface in the client application code:
````csharp
public class MyService : ITransientDependency
{
private readonly IBookAppService _bookService;
public MyService(IBookAppService bookService)
{
_bookService = bookService;
}
public async Task DoIt()
{
var books = await _bookService.GetListAsync();
foreach (var book in books)
{
Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}");
}
}
}
````
This sample injects the `IBookAppService` service interface defined above. The dynamic client proxy implementation makes an HTTP call whenever a service method is called by the client.
### IHttpClientProxy Interface
While you can inject `IBookAppService` like above to use the client proxy, you could inject `IHttpClientProxy<IBookAppService>` for a more explicit usage. In this case you will use the `Service` property of the `IHttpClientProxy<T>` interface.
## Configuration
### RemoteServiceOptions
`RemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<RemoteServiceOptions>(options =>
{
options.RemoteServices.Default =
new RemoteServiceConfiguration("http://localhost:53929/");
});
//...
}
````
### Multiple Remote Service Endpoints
The examples above have configured the "Default" remote service endpoint. You may have different endpoints for different services (as like in a microservice approach where each microservice has different endpoints). In this case, you can add other endpoints to your configuration file:
````json
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://localhost:53929/"
},
"BookStore": {
"BaseUrl": "http://localhost:48392/"
}
}
}
````
`AddHttpClientProxies` method can get an additional parameter for the remote service name. Example:
````csharp
context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationModule).Assembly,
remoteServiceName: "BookStore"
);
````
`remoteServiceName` parameter matches the service endpoint configured via `RemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint.
### As Default Services
When you create a service proxy for `IBookAppService`, you can directly inject the `IBookAppService` to use the proxy client (as shown in the usage section). You can pass `asDefaultServices: false` to the `AddHttpClientProxies` method to disable this feature.
````csharp
context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationModule).Assembly,
asDefaultServices: false
);
````
Using `asDefaultServices: false` may only be needed if your application has already an implementation of the service and you do not want to override/replace the other implementation by your client proxy.
> If you disable `asDefaultServices`, you can only use `IHttpClientProxy<T>` interface to use the client proxies (see the related section above).
[Click to navigate to Dynamic C# API Clients document](../API/Dynamic-CSharp-API-Clients.md)

4
docs/en/AspNetCore/JavaScript-API/Auth.md

@ -1,3 +1,3 @@
# abp.auth JavaScript API
This document has moved.
TODO
[Click to navigate to JavaScript Auth document](../../API/JavaScript-API/Auth.md)

25
docs/en/AspNetCore/JavaScript-API/Index.md

@ -1,24 +1,3 @@
# JavaScript API
ABP provides some JavaScript APIs for ASP.NET Core MVC / Razor Pages applications. They can be used to perform some common application requirements in the client side.
## APIs
* abp.ajax
* [abp.auth](Auth.md)
* abp.currentUser
* abp.dom
* abp.event
* abp.features
* abp.localization
* abp.log
* abp.ModalManager
* abp.notify
* abp.security
* abp.setting
* abp.ui
* abp.utils
* abp.ResourceLoader
* abp.WidgetManager
* Other APIs
This document has moved.
[Click to navigate to JavaScript API document](../../API/JavaScript-API/Index.md)

4
docs/en/AspNetCore/Tag-Helpers/Dynamic-Forms.md

@ -1,3 +1,3 @@
## Dynamic Forms
This document has moved.
This is not documented yet. You can see a [demo](http://bootstrap-taghelpers.abp.io/Components/DynamicForms) for now.
[Click to navigate to Dynamic Forms document](../../UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md)

4
docs/en/AspNetCore/Tag-Helpers/Index.md

@ -1,3 +1,3 @@
## ABP Tag Helpers
This document has moved.
"ABP tag helpers" is not documented yet. You can see a [demo of components](http://bootstrap-taghelpers.abp.io/) for now.
[Click to navigate to ABP Tag Helpers document](../../UI/AspNetCore/Tag-Helpers/Index.md)

5
docs/en/AspNetCore/Theming.md

@ -1,3 +1,4 @@
# Theming
TODO
This document has moved.
[Click to navigate to Theming document](../UI/AspNetCore/Theming.md)

505
docs/en/AspNetCore/Widgets.md

@ -1,505 +1,4 @@
# Widgets
ABP provides a model and infrastructure to create **reusable widgets**. Widget system is an extension to [ASP.NET Core's ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components). Widgets are especially useful when you want to;
This document has moved.
* Have **scripts & styles** dependencies for your widget.
* Create **dashboards** with widgets used inside.
* Define widgets in reusable **[modules](../Module-Development-Basics.md)**.
* Co-operate widgets with **[authorization](../Authorization.md)** and **[bundling](Bundling-Minification.md)** systems.
## Basic Widget Definition
### Create a View Component
As the first step, create a new regular ASP.NET Core View Component:
![widget-basic-files](../images/widget-basic-files.png)
**MySimpleWidgetViewComponent.cs**:
````csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
Inheriting from `AbpViewComponent` is not required. You could inherit from ASP.NET Core's standard `ViewComponent`. `AbpViewComponent` only defines some base useful properties.
You can inject a service and use in the `Invoke` method to get some data from the service. You may need to make Invoke method async, like `public async Task<IViewComponentResult> InvokeAsync()`. See [ASP.NET Core's ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components) document fore all different usages.
**Default.cshtml**:
```xml
<div class="my-simple-widget">
<h2>My Simple Widget</h2>
<p>This is a simple widget!</p>
</div>
```
### Define the Widget
Add a `Widget` attribute to the `MySimpleWidgetViewComponent` class to mark this view component as a widget:
````csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
## Rendering a Widget
Rendering a widget is pretty standard. Use the `Component.InvokeAsync` method in a razor view/page as you do for any view component. Examples:
````xml
@await Component.InvokeAsync("MySimpleWidget")
@await Component.InvokeAsync(typeof(MySimpleWidgetViewComponent))
````
First approach uses the widget name while second approach uses the view component type.
### Widgets with Arguments
ASP.NET Core's view component system allows you to accept arguments for view components. The sample view component below accepts `startDate` and `endDate` and uses these arguments to retrieve data from a service.
````csharp
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Shared.Components.CountersWidget
{
[Widget]
public class CountersWidgetViewComponent : AbpViewComponent
{
private readonly IDashboardAppService _dashboardAppService;
public CountersWidgetViewComponent(IDashboardAppService dashboardAppService)
{
_dashboardAppService = dashboardAppService;
}
public async Task<IViewComponentResult> InvokeAsync(
DateTime startDate, DateTime endDate)
{
var result = await _dashboardAppService.GetCountersWidgetAsync(
new CountersWidgetInputDto
{
StartDate = startDate,
EndDate = endDate
}
);
return View(result);
}
}
}
````
Now, you need to pass an anonymous object to pass arguments as shown below:
````xml
@await Component.InvokeAsync("CountersWidget", new
{
startDate = DateTime.Now.Subtract(TimeSpan.FromDays(7)),
endDate = DateTime.Now
})
````
## Widget Name
Default name of the view components are calculated based on the name of the view component type. If your view component type is `MySimpleWidgetViewComponent` then the widget name will be `MySimpleWidget` (removes `ViewComponent` postfix). This is how ASP.NET Core calculates a view component's name.
To customize widget's name, just use the standard `ViewComponent` attribute of ASP.NET Core:
```csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget]
[ViewComponent(Name = "MyCustomNamedWidget")]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View("~/Pages/Components/MySimpleWidget/Default.cshtml");
}
}
}
```
ABP will respect to the custom name by handling the widget.
> If the view component name and the folder name of the view component don't match, you may need to manually write the view path as done in this example.
### Display Name
You can also define a human-readable, localizable display name for the widget. This display name then can be used on the UI when needed. Display name is optional and can be defined using properties of the `Widget` attribute:
````csharp
using DashboardDemo.Localization;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(
DisplayName = "MySimpleWidgetDisplayName", //Localization key
DisplayNameResource = typeof(DashboardDemoResource) //localization resource
)]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
See [the localization document](../Localization.md) to learn about localization resources and keys.
## Style & Script Dependencies
There are some challenges when your widget has script and style files;
* Any page uses the widget should also include the **its script & styles** files into the page.
* The page should also care about **depended libraries/files** of the widget.
ABP solves these issues when you properly relate the resources with the widget. You don't care about dependencies of the widget while using it.
### Defining as Simple File Paths
The example widget below adds a style and a script file:
````csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(
StyleFiles = new[] { "/Pages/Components/MySimpleWidget/Default.css" },
ScriptFiles = new[] { "/Pages/Components/MySimpleWidget/Default.js" }
)]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
ABP takes account these dependencies and properly adds to the view/page when you use the widget. Style/script files can be **physical or virtual**. It is completely integrated to the [Virtual File System](../Virtual-File-System.md).
### Defining Bundle Contributors
All resources for used widgets in a page are added as a **bundle** (bundled & minified in production if you don't configure otherwise). In addition to adding a simple file, you can take full power of the bundle contributors.
The sample code below does the same with the code above, but defines and uses bundle contributors:
````csharp
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(
StyleTypes = new []{ typeof(MySimpleWidgetStyleBundleContributor) },
ScriptTypes = new[]{ typeof(MySimpleWidgetScriptBundleContributor) }
)]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
public class MySimpleWidgetStyleBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files
.AddIfNotContains("/Pages/Components/MySimpleWidget/Default.css");
}
}
public class MySimpleWidgetScriptBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files
.AddIfNotContains("/Pages/Components/MySimpleWidget/Default.js");
}
}
}
````
Bundle contribution system is very powerful. If your widget uses a JavaScript library to render a chart, then you can declare it as a dependency, so the JavaScript library is automatically added to the page if it wasn't added before. In this way, the page using your widget doesn't care about the dependencies.
See the [bundling & minification](Bundling-Minification.md) documentation for more information about that system.
## RefreshUrl
A widget may design a `RefreshUrl` that is used whenever the widget needs to be refreshed. If it is defined, the widget is re-rendered on the server side on every refresh (see the refresh `method` of the `WidgetManager` below).
````csharp
[Widget(RefreshUrl = "Widgets/Counters")]
public class CountersWidgetViewComponent : AbpViewComponent
{
}
````
Once you define a `RefreshUrl` for your widget, you need to provide an endpoint to render and return it:
````csharp
[Route("Widgets")]
public class CountersWidgetController : AbpController
{
[HttpGet]
[Route("Counters")]
public IActionResult Counters(DateTime startDate, DateTime endDate)
{
return ViewComponent("CountersWidget", new {startDate, endDate});
}
}
````
`Widgets/Counters` route matches to the `RefreshUrl` declared before.
> A widget supposed to be refreshed in two ways: In the first way, when you use a `RefreshUrl`, it re-rendered on the server and replaced by the HTML returned from server. In the second way the widget gets data (generally a JSON object) from server and refreshes itself in the client side (see the refresh method in the Widget JavaScript API section).
## JavaScript API
A widget may need to be rendered and refreshed in the client side. In such cases, you can use ABP's `WidgetManager` and define APIs for your widgets.
### WidgetManager
`WidgetManager` is used to initialize and refresh one or more widgets. Create a new `WidgetManager` as shown below:
````js
$(function() {
var myWidgetManager = new abp.WidgetManager('#MyDashboardWidgetsArea');
})
````
`MyDashboardWidgetsArea` may contain one or more widgets inside.
> Using the `WidgetManager` inside document.ready (like above) is a good practice since its functions use the DOM and need DOM to be ready.
#### WidgetManager.init()
`init` simply initializes the `WidgetManager` and calls `init` methods of the related widgets if they define (see Widget JavaScript API section below)
```js
myWidgetManager.init();
```
#### WidgetManager.refresh()
`refresh` method refreshes all widgets related to this `WidgetManager`:
````
myWidgetManager.refresh();
````
#### WidgetManager Options
WidgetManager has some additional options.
##### Filter Form
If your widgets require parameters/filters then you will generally have a form to filter the widgets. In such cases, you can create a form that has some form elements and a dashboard area with some widgets inside. Example:
````xml
<form method="get" id="MyDashboardFilterForm">
...form elements
</form>
<div id="MyDashboardWidgetsArea" data-widget-filter="#MyDashboardFilterForm">
...widgets
</div>
````
`data-widget-filter` attribute relates the form with the widgets. Whenever the form is submitted, all the widgets are automatically refreshed with the form fields as the filter.
Instead of the `data-widget-filter` attribute, you can use the `filterForm` parameter of the `WidgetManager` constructor. Example:
````js
var myWidgetManager = new abp.WidgetManager({
wrapper: '#MyDashboardWidgetsArea',
filterForm: '#MyDashboardFilterForm'
});
````
##### Filter Callback
You may want to have a better control to provide filters while initializing and refreshing the widgets. In this case, you can use the `filterCallback` option:
````js
var myWidgetManager = new abp.WidgetManager({
wrapper: '#MyDashboardWidgetsArea',
filterCallback: function() {
return $('#MyDashboardFilterForm').serializeFormToObject();
}
});
````
This example shows the default implementation of the `filterCallback`. You can return any JavaScript object with fields. Example:
````js
filterCallback: function() {
return {
'startDate': $('#StartDateInput').val(),
'endDate': $('#EndDateInput').val()
};
}
````
The returning filters are passed to all widgets on `init` and `refresh`.
### Widget JavaScript API
A widget can define a JavaScript API that is invoked by the `WidgetManager` when needed. The code sample below can be used to start to define an API for a widget.
````js
(function () {
abp.widgets.NewUserStatisticWidget = function ($wrapper) {
var getFilters = function () {
return {
...
};
}
var refresh = function (filters) {
...
};
var init = function (filters) {
...
};
return {
getFilters: getFilters,
init: init,
refresh: refresh
};
};
})();
````
`NewUserStatisticWidget` is the name of the widget here. It should match the widget name defined in the server side. All of the functions are optional.
#### getFilters
If the widget has internal custom filters, this function should return the filter object. Example:
````js
var getFilters = function() {
return {
frequency: $wrapper.find('.frequency-filter option:selected').val()
};
}
````
This method is used by the `WidgetManager` while building filters.
#### init
Used to initialize the widget when needed. It has a filter argument that can be used while getting data from server. `init` method is used when `WidgetManager.init()` function is called. It is also called if your widget requires a full re-load on refresh. See the `RefreshUrl` widget option.
#### refresh
Used to refresh the widget when needed. It has a filter argument that can be used while getting data from server. `refresh` method is used whenever `WidgetManager.refresh()` function is called.
## Authorization
Some widgets may need to be available only for authenticated or authorized users. In this case, use the following properties of the `Widget` attribute:
* `RequiresAuthentication` (`bool`): Set to true to make this widget usable only for authentication users (user have logged in to the application).
* `RequiredPolicies` (`List<string>`): A list of policy names to authorize the user. See [the authorization document](../Authorization.md) for more info about policies.
Example:
````csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(RequiredPolicies = new[] { "MyPolicyName" })]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
## WidgetOptions
As alternative to the `Widget` attribute, you can use the `WidgetOptions` to configure widgets:
```csharp
Configure<WidgetOptions>(options =>
{
options.Widgets.Add<MySimpleWidgetViewComponent>();
});
```
Write this into the `ConfigureServices` method of your [module](../Module-Development-Basics.md). All the configuration done with the `Widget` attribute is also possible with the `WidgetOptions`. Example configuration that adds a style for the widget:
````csharp
Configure<WidgetOptions>(options =>
{
options.Widgets
.Add<MySimpleWidgetViewComponent>()
.WithStyles("/Pages/Components/MySimpleWidget/Default.css");
});
````
> Tip: `WidgetOptions` can also be used to get an existing widget and change its configuration. This is especially useful if you want to modify the configuration of a widget inside a module used by your application. Use `options.Widgets.Find` to get an existing `WidgetDefinition`.
## See Also
* [Example project (source code)](https://github.com/abpframework/abp/tree/dev/samples/DashboardDemo).
[Click to navigate to Widgets document](../UI/AspNetCore/Widgets.md)

4
docs/en/Authorization.md

@ -254,7 +254,7 @@ You may need to check a policy/permission on the client side. For ASP.NET Core M
abp.auth.isGranted('MyPermissionName');
````
See [abp.auth](AspNetCore/JavaScript-API/Auth.md) API documentation for details.
See [abp.auth](API/JavaScript-API/Auth.md) API documentation for details.
## Permission Management
@ -366,4 +366,4 @@ This is already done for the startup template integration tests.
## See Also
* [Permission Management Module](Modules/Permission-Management.md)
* [ASP.NET Core MVC / Razor Pages JavaScript Auth API](AspNetCore/JavaScript-API/Auth.md)
* [ASP.NET Core MVC / Razor Pages JavaScript Auth API](API/JavaScript-API/Auth.md)

2
docs/en/Blog-Posts/2018-09-24-Announcement/Post.md

@ -120,7 +120,7 @@ Dynamic bundling & minification system works on the virtual file system and allo
This code creates a new style bundle on the fly by including bootstrap (and its dependencies if there are) and two more css files. These files are bundled & minified on production environment, but will be added individually on the development environment.
See [the documentation](https://github.com/abpframework/abp/blob/master/docs/AspNetCore/Bundling-Minification.md) for more.
See [the documentation](https://github.com/abpframework/abp/blob/master/docs/UI/AspNetCore/Bundling-Minification.md) for more.
#### Distributed Event Bus
In current ABP, there is an IEventBus service to trigger and handle events inside the application. In addition to this local event bus, we are creating a distributed event bus abstraction (and RabbitMQ integration) to implement distributed messaging patterns.

2
docs/en/Blog-Posts/2019-08-16 v0_19_Release/Post.md

@ -27,7 +27,7 @@ Angular was the first SPA UI option, but it is not the last. After v1.0 release,
### Widget System
[Widget system](https://docs.abp.io/en/abp/latest/AspNetCore/Widgets) allows to **define and reuse** widgets for ASP.NET Core MVC applications. Widgets may have their own script and style resources and dependencies to 3rd-party libraries which are managed by the ABP framework.
[Widget system](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Widgets) allows to **define and reuse** widgets for ASP.NET Core MVC applications. Widgets may have their own script and style resources and dependencies to 3rd-party libraries which are managed by the ABP framework.
### Others

4
docs/en/Microservice-Architecture.md

@ -12,8 +12,8 @@ One of the major goals of the ABP framework is to provide a convenient infrastru
* Offers an [architectural model](Best-Practices/Module-Architecture.md) to develop your modules to be compatible to microservice development and deployment.
* Provides [best practices guide](Best-Practices/Index.md) to develop your module standards-compliance.
* Provides base infrastructure to implement [Domain Driven Design](Domain-Driven-Design.md) in your microservices.
* Provide services to [automatically create REST-style APIs](AspNetCore/Auto-API-Controllers.md) from your application services.
* Provide services to [automatically create C# API clients](AspNetCore/Dynamic-CSharp-API-Clients.md) that makes easy to consume your services from another service/application.
* Provide services to [automatically create REST-style APIs](API/Auto-API-Controllers.md) from your application services.
* Provide services to [automatically create C# API clients](API/Dynamic-CSharp-API-Clients.md) that makes easy to consume your services from another service/application.
* Provides a [distributed event bus](Event-Bus.md) to communicate your services.
* Provides many other services to make your daily development easier.

4
docs/en/Samples/Microservice-Demo.md

@ -341,7 +341,7 @@ Backend admin application uses the Identity and Product microservices for all op
##### HTTP Clients
ABP application modules generally provides C# client libraries to consume services (APIs) easily (they generally uses the [Dynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature of the ABP framework). That means if you need to consume Identity service API, you can reference to its client package and easily use the APIs by provided interfaces.
ABP application modules generally provides C# client libraries to consume services (APIs) easily (they generally uses the [Dynamic C# API Clients](../API/Dynamic-CSharp-API-Clients.md) feature of the ABP framework). That means if you need to consume Identity service API, you can reference to its client package and easily use the APIs by provided interfaces.
For that purpose, `BackendAdminAppHostModule` class declares dependencies for `AbpIdentityHttpApiClientModule` and `ProductManagementHttpApiClientModule`.
@ -1103,7 +1103,7 @@ Product Management is a module that consists of several layers and packages/proj
* `ProductManagement.Application` contains the implementation of application services.
* `ProductManagement.EntityFrameworkCore` contains DbConext and other EF Core related classes and configuration.
* `ProductManagement.HttpApi` contains API Controllers.
* `ProductManagement.HttpApi.Client` contains C# proxies to directly use the HTTP API remotely. Uses [Dynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature of the ABP framework.
* `ProductManagement.HttpApi.Client` contains C# proxies to directly use the HTTP API remotely. Uses [Dynamic C# API Clients](../API/Dynamic-CSharp-API-Clients.md) feature of the ABP framework.
* `ProductManagement.Web` contains the UI elements (pages, scripts, styles... etc).
By the help of this layering, it is possible to use the same module as a package reference in a monolithic application or use as a service that runs in another server. It is possible to separate UI (Web) and API layers, so they run in different servers.

4
docs/en/Startup-Templates/Application.md

@ -145,7 +145,7 @@ While creating database & applying migrations seems only necessary for relationa
This project is used to define your API Controllers.
Most of time you don't need to manually define API Controllers since ABP's [Auto API Controllers](../AspNetCore/Auto-API-Controllers.md) feature creates them automagically based on your application layer. However, in case of you need to write API controllers, this is the best place to do it.
Most of time you don't need to manually define API Controllers since ABP's [Auto API Controllers](../API/Auto-API-Controllers.md) feature creates them automagically based on your application layer. However, in case of you need to write API controllers, this is the best place to do it.
* Depends on the `.Application.Contracts` project to be able to inject the application service interfaces.
@ -153,7 +153,7 @@ Most of time you don't need to manually define API Controllers since ABP's [Auto
This is a project that defines C# client proxies to use the HTTP APIs of the solution. You can share this library to 3rd-party clients, so they can easily consume your HTTP APIs in their Dotnet applications (For other type of applications, they can still use your APIs, either manually or using a tool in their own platform)
Most of time you don't need to manually create C# client proxies, thanks to ABP's [Dynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature.
Most of time you don't need to manually create C# client proxies, thanks to ABP's [Dynamic C# API Clients](../API/Dynamic-CSharp-API-Clients.md) feature.
`.HttpApi.Client.ConsoleTestApp` project is a console application created to demonstrate the usage of the client proxies.

6
docs/en/Tutorials/Part-1.md

@ -493,7 +493,7 @@ namespace Acme.BookStore
### Auto API Controllers
We normally create **Controllers** to expose application services as **HTTP API** endpoints. This allows browsers or 3rd-party clients to call them via AJAX. ABP can [**automagically**](https://docs.abp.io/en/abp/latest/AspNetCore/Auto-API-Controllers) configures your application services as MVC API Controllers by convention.
We normally create **Controllers** to expose application services as **HTTP API** endpoints. This allows browsers or 3rd-party clients to call them via AJAX. ABP can [**automagically**](https://docs.abp.io/en/abp/latest/API/Auto-API-Controllers) configures your application services as MVC API Controllers by convention.
#### Swagger UI
@ -698,8 +698,8 @@ Change the `Pages/Books/Index.cshtml` as following:
</abp-card>
````
* `abp-script` [tag helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro) is used to add external **scripts** to the page. It has many additional features compared to standard `script` tag. It handles **minification** and **versioning**. See the [bundling & minification document](https://docs.abp.io/en/abp/latest/AspNetCore/Bundling-Minification) for details.
* `abp-card` and `abp-table` are **tag helpers** for Twitter Bootstrap's [card component](http://getbootstrap.com/docs/4.1/components/card/). There are other useful tag helpers in ABP to easily use most of the [bootstrap](https://getbootstrap.com/) components. You can also use regular HTML tags instead of these tag helpers, but using tag helpers reduces HTML code and prevents errors by help the of IntelliSense and compile time type checking. Further information, see the [tag helpers](https://docs.abp.io/en/abp/latest/AspNetCore/Tag-Helpers/Index) document.
* `abp-script` [tag helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro) is used to add external **scripts** to the page. It has many additional features compared to standard `script` tag. It handles **minification** and **versioning**. See the [bundling & minification document](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Bundling-Minification) for details.
* `abp-card` and `abp-table` are **tag helpers** for Twitter Bootstrap's [card component](http://getbootstrap.com/docs/4.1/components/card/). There are other useful tag helpers in ABP to easily use most of the [bootstrap](https://getbootstrap.com/) components. You can also use regular HTML tags instead of these tag helpers, but using tag helpers reduces HTML code and prevents errors by help the of IntelliSense and compile time type checking. Further information, see the [tag helpers](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Tag-Helpers/Index) document.
* You can **localize** the column names in the localization file as you did for the menu items above.
##### Add a Script File

0
docs/en/Angular/AddingASettingsTab/index.md → docs/en/UI/Angular/AddingSettingTab.md

2
docs/en/Angular/ReplaceableComponents/index.md → docs/en/UI/Angular/ComponentReplacement.md

@ -1,4 +1,4 @@
# Component Replacement in Angular
# Component Replacement
You can replace some ABP components with your custom components.

0
docs/en/Angular/Localization/Index.md → docs/en/UI/Angular/Localization.md

0
docs/en/Angular/PermissionManagement/index.md → docs/en/UI/Angular/PermissionManagement.md

0
docs/en/Angular/AddingASettingsTab/images/custom-settings.png → docs/en/UI/Angular/images/custom-settings.png

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

0
docs/en/Angular/ReplaceableComponents/images/inaction.gif → docs/en/UI/Angular/images/inaction.gif

Before

Width:  |  Height:  |  Size: 7.5 MiB

After

Width:  |  Height:  |  Size: 7.5 MiB

352
docs/en/UI/AspNetCore/Bundling-Minification.md

@ -0,0 +1,352 @@
# ASP.NET Core MVC Bundling & Minification
There are many ways of bundling & minification of client side resources (JavaScript and CSS files). Most common ways are:
* Using the [Bundler & Minifier](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier) Visual Studio extension or the [NuGet package](https://www.nuget.org/packages/BuildBundlerMinifier/).
* Using [Gulp](https://gulpjs.com/)/[Grunt](https://gruntjs.com/) task managers and their plugins.
ABP offers a simple, dynamic, powerful, modular and built-in way.
## Volo.Abp.AspNetCore.Mvc.UI.Bundling Package
> This package is already installed by default with the startup templates. So, most of the time, you don't need to install it manually.
Install the `Volo.Abp.AspNetCore.Mvc.UI.Bundling` nuget package to your project:
````
install-package Volo.Abp.AspNetCore.Mvc.UI.Bundling
````
Then you can add the `AbpAspNetCoreMvcUiBundlingModule` dependency to your module:
````C#
using Volo.Abp.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))]
public class MyWebModule : AbpModule
{
//...
}
}
````
## Razor Bundling Tag Helpers
The simplest way of creating a bundle is to use `abp-script-bundle` or `abp-style-bundle` tag helpers. Example:
````html
<abp-style-bundle name="MyGlobalBundle">
<abp-style src="/libs/bootstrap/css/bootstrap.css" />
<abp-style src="/libs/font-awesome/css/font-awesome.css" />
<abp-style src="/libs/toastr/toastr.css" />
<abp-style src="/styles/my-global-style.css" />
</abp-style-bundle>
````
This bundle defines a style bundle with a **unique name**: `MyGlobalBundle`. It's very easy to understand how to use it. Let's see how it *works*:
* ABP creates the bundle as **lazy** from the provided files when it's **first requested**. For the subsequent calls, it's returned from the **cache**. That means if you conditionally add the files to the bundle, it's executed only once and any changes of the condition will not effect the bundle for the next requests.
* ABP adds bundle files **individually** to the page for the `development` environment. It automatically bundles & minifies for other environments (`staging`, `production`...).
* The bundle files may be **physical** files or [**virtual/embedded** files](../Virtual-File-System.md).
* ABP automatically adds **version query string** to the bundle file URL to prevent browsers from caching when the bundle is being updated. (like ?_v=67872834243042 - generated from last change date of the related files). The versioning works even if the bundle files are individually added to the page (on the development environment).
### Importing The Bundling Tag Helpers
> This is already imported by default with the startup templates. So, most of the time, you don't need to add it manually.
In order to use bundle tag helpers, you need to add it into your `_ViewImports.cshtml` file or into your page:
````
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
````
### Unnamed Bundles
The `name` is **optional** for the razor bundle tag helpers. If you don't define a name, it's automatically **calculated** based on the used bundle file names (they are **concatenated** and **hashed**). Example:
````html
<abp-style-bundle>
<abp-style src="/libs/bootstrap/css/bootstrap.css" />
<abp-style src="/libs/font-awesome/css/font-awesome.css" />
<abp-style src="/libs/toastr/toastr.css" />
@if (ViewBag.IncludeCustomStyles != false)
{
<abp-style src="/styles/my-global-style.css" />
}
</abp-style-bundle>
````
This will potentially create **two different bundles** (one incudes the `my-global-style.css` and other does not).
Advantages of **unnamed** bundles:
* Can **conditionally add items** to the bundle. But this may lead to multiple variations of the bundle based on the conditions.
Advantages of **named** bundles:
* Other **modules can contribute** to the bundle by its name (see the sections below).
### Single File
If you need to just add a single file to the page, you can use the `abp-script` or `abp-style` tag without a wrapping in the `abp-script-bundle` or `abp-style-bundle` tag. Example:
````xml
<abp-script src="/scripts/my-script.js" />
````
The bundle name will be *scripts.my-scripts* for the example above ("/" is replaced by "."). All bundling features are work as expected for single file bundles too.
## Bundling Options
If you need to use same bundle in **multiple pages** or want to use some more **powerful features**, you can configure bundles **by code** in your [module](../Module-Development-Basics.md) class.
### Creating A New Bundle
Example usage:
````C#
[DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))]
public class MyWebModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBundlingOptions>(options =>
{
options
.ScriptBundles
.Add("MyGlobalBundle", bundle => {
bundle.AddFiles(
"/libs/jquery/jquery.js",
"/libs/bootstrap/js/bootstrap.js",
"/libs/toastr/toastr.min.js",
"/scripts/my-global-scripts.js"
);
});
});
}
}
````
> You can use the same name (*MyGlobalBundle* here) for a script & style bundle since they are added to different collections (`ScriptBundles` and `StyleBundles`).
After defining such a bundle, it can be included into a page using the same tag helpers defined above. Example:
````html
<abp-script-bundle name="MyGlobalBundle" />
````
This time, no file defined in the tag helper definition because the bundle files are defined by the code.
### Configuring An Existing Bundle
ABP supports [modularity](../Module-Development-Basics.md) for bundling as well. A module can modify an existing bundle that is created by a dependant module. Example:
````C#
[DependsOn(typeof(MyWebModule))]
public class MyWebExtensionModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBundlingOptions>(options =>
{
options
.ScriptBundles
.Configure("MyGlobalBundle", bundle => {
bundle.AddFiles(
"/scripts/my-extension-script.js"
);
});
});
}
}
````
> It's not possible to configure unnamed bundle tag helpers by code, because their name are not known at the development time. It's suggested to always use a name for a bundle tag helper.
## Bundle Contributors
Adding files to an existing bundle seems useful. What if you need to **replace** a file in the bundle or you want to **conditionally** add files? Defining a bundle contributor provides extra power for such cases.
An example bundle contributor that replaces bootstrap.css with a customized version:
````C#
public class MyExtensionGlobalStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.ReplaceOne(
"/libs/bootstrap/css/bootstrap.css",
"/styles/extensions/bootstrap-customized.css"
);
}
}
````
Then you can use this contributor as like below:
````C#
services.Configure<AbpBundlingOptions>(options =>
{
options
.ScriptBundles
.Configure("MyGlobalBundle", bundle => {
bundle.AddContributors(typeof(MyExtensionGlobalStyleContributor));
});
});
````
> You can also add contributors while creating a new bundle.
Contributors can also be used in the bundle tag helpers. Example:
````xml
<abp-style-bundle>
<abp-style type="@typeof(BootstrapStyleContributor)" />
<abp-style src="/libs/font-awesome/css/font-awesome.css" />
<abp-style src="/libs/toastr/toastr.css" />
</abp-style-bundle>
````
`abp-style` and `abp-script` tags can get `type` attributes (instead of `src` attributes) as shown in this sample. When you add a bundle contributor, its dependencies are also automatically added to the bundle.
### Contributor Dependencies
A bundle contributor can have one or more dependencies to other contributors.
Example:
````C#
[DependsOn(typeof(MyDependedBundleContributor))] //Define the dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{
//...
}
````
When a bundle contributor is added, its dependencies are **automatically and recursively** added. Dependencies added by the **dependency order** by preventing **duplicates**. Duplicates are prevented even if they are in separated bundles. ABP organizes all bundles in a page and eliminates duplications.
Creating contributors and defining dependencies is a way of organizing bundle creation across different modules.
### Contributor Extensions
In some advanced scenarios, you may want to do some additional configuration whenever a bundle contributor is used. Contributor extensions works seamlessly when the extended contributor is used.
The example below adds some styles for prism.js library:
````csharp
public class MyPrismjsStyleExtension : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/prismjs/plugins/toolbar/prism-toolbar.css");
}
}
````
Then you can configure `BundleContributorOptions` to extend existing `PrismjsStyleBundleContributor`.
````csharp
Configure<BundleContributorOptions>(options =>
{
options
.Extensions<PrismjsStyleBundleContributor>()
.Add<MyPrismjsStyleExtension>();
});
````
Whenever `PrismjsStyleBundleContributor` is added into a bundle, `MyPrismjsStyleExtension` will also be automatically added.
### Accessing to the IServiceProvider
While it is rarely needed, `BundleConfigurationContext` has a `ServiceProvider` property that you can resolve service dependencies inside the `ConfigureBundle` method.
### Standard Package Contributors
Adding a specific NPM package resource (js, css files) into a bundle is pretty straight forward for that package. For example you always add the `bootstrap.css` file for the bootstrap NPM package.
There are built-in contributors for all [standard NPM packages](Client-Side-Package-Management.md). For example, if your contributor depends on the bootstrap, you can just declare it, instead of adding the bootstrap.css yourself.
````C#
[DependsOn(typeof(BootstrapStyleContributor))] //Define the bootstrap style dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{
//...
}
````
Using the built-in contributors for standard packages;
* Prevents you typing **the invalid resource paths**.
* Prevents changing your contributor if the resource **path changes** (the dependant contributor will handle it).
* Prevents multiple modules adding the **duplicate files**.
* Manages **dependencies recursively** (adds dependencies of dependencies, if necessary).
#### Volo.Abp.AspNetCore.Mvc.UI.Packages Package
> This package is already installed by default in the startup templates. So, most of the time, you don't need to install it manually.
Standard package contributors are defined in the `Volo.Abp.AspNetCore.Mvc.UI.Packages` NuGet package.
To install it to your project:
````
install-package Volo.Abp.AspNetCore.Mvc.UI.Packages
````
Then add the `AbpAspNetCoreMvcUiPackagesModule` module dependency to your own module;
````C#
using Volo.Abp.Modularity;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpAspNetCoreMvcUiPackagesModule))]
public class MyWebModule : AbpModule
{
//...
}
}
````
### Bundle Inheritance
In some specific cases, it may be needed to create a **new** bundle **inherited** from other bundle(s). Inheriting from a bundle (recursively) inherits all files/contributors of that bundle. Then the derived bundle can add or modify files/contributors **without modifying** the original bundle.
Example:
````c#
services.Configure<AbpBundlingOptions>(options =>
{
options
.StyleBundles
.Add("MyTheme.MyGlobalBundle", bundle => {
bundle
.AddBaseBundles("MyGlobalBundle") //Can add multiple
.AddFiles(
"/styles/mytheme-global-styles.css"
);
});
});
````
## Themes
Themes uses the standard package contributors to add library resources to page layouts. Themes may also define some standard/global bundles, so any module can contribute to these standard/global bundles. See the [theming documentation](Theming.md) for more.
## Best Practices & Suggestions
It's suggested to define multiple bundles for an application, each one is used for different purposes.
* **Global bundle**: Global style/script bundles are included to every page in the application. Themes already defines global style & script bundles. Your module can contribute to them.
* **Layout bundles**: This is a specific bundle to an individual layout. Only contains resources shared among all the pages use the layout. Use the bundling tag helpers to create the bundle as a good practice.
* **Module bundles**: For shared resources among the pages of an individual module.
* **Page bundles**: Specific bundles created for each page. Use the bundling tag helpers to create the bundle as a best practice.
Establish a balance between performance, network bandwidth usage and count of many bundles.
## See Also
* [Client Side Package Management](Client-Side-Package-Management.md)
* [Theming](Theming.md)

116
docs/en/UI/AspNetCore/Client-Side-Package-Management.md

@ -0,0 +1,116 @@
## ASP.NET Core MVC Client Side Package Management
ABP framework can work with any type of client side package management systems. You can even decide to use no package management system and manage your dependencies manually.
However, ABP framework works best with **NPM/Yarn**. By default, built-in modules are configured to work with NPM/Yarn.
Finally, we suggest the [**Yarn**](https://yarnpkg.com/) over the NPM since it's faster, stable and also compatible with the NPM.
### @ABP NPM Packages
ABP is a modular platform. Every developer can create modules and the modules should work together in a **compatible** and **stable** state.
One challenge is the **versions of the dependant NPM packages**. What if two different modules use the same JavaScript library but its different (and potentially incompatible) versions.
To solve the versioning problem, we created a **standard set of packages** those depends on some common third-party libraries. Some example packages are [@abp/jquery](https://www.npmjs.com/package/@abp/jquery), [@abp/bootstrap](https://www.npmjs.com/package/@abp/bootstrap) and [@abp/font-awesome](https://www.npmjs.com/package/@abp/font-awesome). You can see the **list of packages** from the [Github repository](https://github.com/volosoft/abp/tree/master/npm/packs).
The benefit of a **standard package** is:
* It depends on a **standard version** of a package. Depending on this package is **safe** because all modules depend on the same version.
* It contains the gulp task to copy library resources (js, css, img... files) from the **node_modules** folder to **wwwroot/libs** folder. See the *Mapping The Library Resources* section for more.
Depending on a standard package is easy. Just add it to your **package.json** file like you normally do. Example:
````
{
...
"dependencies": {
"@abp/bootstrap": "^1.0.0"
}
}
````
It's suggested to depend on a standard package instead of directly depending on a third-party package.
#### Package Installation
After depending on a NPM package, all you should do is to run the **yarn** command from the command line to install all the packages and their dependencies:
````
yarn
````
Alternatively, you can use `npm install` but [Yarn](https://yarnpkg.com/) is suggested as mentioned before.
#### Package Contribution
If you need a third-party NPM package that is not in the standard set of packages, you can create a Pull Request on the Github [repository](https://github.com/volosoft/abp). A pull request that follows these rules is accepted:
* Package name should be named as `@abp/package-name` for a `package-name` on NPM (example: `@abp/bootstrap` for the `bootstrap` package).
* It should be the **latest stable** version of the package.
* It should only depend a **single** third-party package. It can depend on multiple `@abp/*` packages.
* The package should include a `abp.resourcemapping.js` file formatted as defined in the *Mapping The Library Resources* section. This file should only map resources for the depended package.
* You also need to create [bundle contributor(s)](Bundling-Minification.md) for the package you have created.
See current standard packages for examples.
### Mapping The Library Resources
Using NPM packages and NPM/Yarn tool is the de facto standard for client side libraries. NPM/Yarn tool creates a **node_modules** folder in the root folder of your web project.
Next challenge is copying needed resources (js, css, img... files) from the `node_modules` into a folder inside the **wwwroot** folder to make it accessible to the clients/browsers.
ABP defines a [Gulp](https://gulpjs.com/) based task to **copy resources** from **node_modules** to **wwwroot/libs** folder. Each **standard package** (see the *@ABP NPM Packages* section) defines the mapping for its own files. So, most of the time, you only configure dependencies.
The **startup templates** are already configured to work all these out of the box. This section will explain the configuration options.
#### Resource Mapping Definition File
A module should define a JavaScript file named `abp.resourcemapping.js` which is formatted as in the example below:
````js
module.exports = {
aliases: {
"@node_modules": "./node_modules",
"@libs": "./wwwroot/libs"
},
clean: [
"@libs"
],
mappings: {
}
}
````
* **aliases** section defines standard aliases (placeholders) that can be used in the mapping paths. **@node_modules** and **@libs** are required (by the standard packages), you can define your own aliases to reduce duplication.
* **clean** section is a list of folders to clean before copying the files.
* **mappings** section is a list of mappings of files/folders to copy. This example does not copy any resource itself, but depends on a standard package.
An example mapping configuration is shown below:
````js
mappings: {
"@node_modules/bootstrap/dist/css/bootstrap.css": "@libs/bootstrap/css/",
"@node_modules/bootstrap/dist/js/bootstrap.bundle.js": "@libs/bootstrap/js/",
"@node_modules/bootstrap-datepicker/dist/locales/*.*": "@libs/bootstrap-datepicker/locales/"
}
````
#### Using The Gulp
Once you properly configure the `abp.resourcemapping.js` file, you can run the gulp command from the command line:
````
gulp
````
When you run the `gulp`, all packages will copy their own resources into the **wwwroot/libs** folder. Running `yarn & gulp` is only necessary if you make a change in your dependencies in the **package.json** file.
> When you run the Gulp command, dependencies of the application are resolved using the package.json file. The Gulp task automatically discovers and maps all resources from all dependencies (recursively).
#### See Also
* [Bundling & Minification](Bundling-Minification.md)
* [Theming](Theming.md)

3
docs/en/UI/AspNetCore/Tag-Helpers/Dynamic-Forms.md

@ -0,0 +1,3 @@
## Dynamic Forms
This is not documented yet. You can see a [demo](http://bootstrap-taghelpers.abp.io/Components/DynamicForms) for now.

3
docs/en/UI/AspNetCore/Tag-Helpers/Index.md

@ -0,0 +1,3 @@
## ABP Tag Helpers
"ABP tag helpers" is not documented yet. You can see a [demo of components](http://bootstrap-taghelpers.abp.io/) for now.

3
docs/en/UI/AspNetCore/Theming.md

@ -0,0 +1,3 @@
# Theming
TODO

505
docs/en/UI/AspNetCore/Widgets.md

@ -0,0 +1,505 @@
# Widgets
ABP provides a model and infrastructure to create **reusable widgets**. Widget system is an extension to [ASP.NET Core's ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components). Widgets are especially useful when you want to;
* Have **scripts & styles** dependencies for your widget.
* Create **dashboards** with widgets used inside.
* Define widgets in reusable **[modules](../Module-Development-Basics.md)**.
* Co-operate widgets with **[authorization](../Authorization.md)** and **[bundling](Bundling-Minification.md)** systems.
## Basic Widget Definition
### Create a View Component
As the first step, create a new regular ASP.NET Core View Component:
![widget-basic-files](../../images/widget-basic-files.png)
**MySimpleWidgetViewComponent.cs**:
````csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
Inheriting from `AbpViewComponent` is not required. You could inherit from ASP.NET Core's standard `ViewComponent`. `AbpViewComponent` only defines some base useful properties.
You can inject a service and use in the `Invoke` method to get some data from the service. You may need to make Invoke method async, like `public async Task<IViewComponentResult> InvokeAsync()`. See [ASP.NET Core's ViewComponents](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components) document fore all different usages.
**Default.cshtml**:
```xml
<div class="my-simple-widget">
<h2>My Simple Widget</h2>
<p>This is a simple widget!</p>
</div>
```
### Define the Widget
Add a `Widget` attribute to the `MySimpleWidgetViewComponent` class to mark this view component as a widget:
````csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
## Rendering a Widget
Rendering a widget is pretty standard. Use the `Component.InvokeAsync` method in a razor view/page as you do for any view component. Examples:
````xml
@await Component.InvokeAsync("MySimpleWidget")
@await Component.InvokeAsync(typeof(MySimpleWidgetViewComponent))
````
First approach uses the widget name while second approach uses the view component type.
### Widgets with Arguments
ASP.NET Core's view component system allows you to accept arguments for view components. The sample view component below accepts `startDate` and `endDate` and uses these arguments to retrieve data from a service.
````csharp
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Shared.Components.CountersWidget
{
[Widget]
public class CountersWidgetViewComponent : AbpViewComponent
{
private readonly IDashboardAppService _dashboardAppService;
public CountersWidgetViewComponent(IDashboardAppService dashboardAppService)
{
_dashboardAppService = dashboardAppService;
}
public async Task<IViewComponentResult> InvokeAsync(
DateTime startDate, DateTime endDate)
{
var result = await _dashboardAppService.GetCountersWidgetAsync(
new CountersWidgetInputDto
{
StartDate = startDate,
EndDate = endDate
}
);
return View(result);
}
}
}
````
Now, you need to pass an anonymous object to pass arguments as shown below:
````xml
@await Component.InvokeAsync("CountersWidget", new
{
startDate = DateTime.Now.Subtract(TimeSpan.FromDays(7)),
endDate = DateTime.Now
})
````
## Widget Name
Default name of the view components are calculated based on the name of the view component type. If your view component type is `MySimpleWidgetViewComponent` then the widget name will be `MySimpleWidget` (removes `ViewComponent` postfix). This is how ASP.NET Core calculates a view component's name.
To customize widget's name, just use the standard `ViewComponent` attribute of ASP.NET Core:
```csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget]
[ViewComponent(Name = "MyCustomNamedWidget")]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View("~/Pages/Components/MySimpleWidget/Default.cshtml");
}
}
}
```
ABP will respect to the custom name by handling the widget.
> If the view component name and the folder name of the view component don't match, you may need to manually write the view path as done in this example.
### Display Name
You can also define a human-readable, localizable display name for the widget. This display name then can be used on the UI when needed. Display name is optional and can be defined using properties of the `Widget` attribute:
````csharp
using DashboardDemo.Localization;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(
DisplayName = "MySimpleWidgetDisplayName", //Localization key
DisplayNameResource = typeof(DashboardDemoResource) //localization resource
)]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
See [the localization document](../Localization.md) to learn about localization resources and keys.
## Style & Script Dependencies
There are some challenges when your widget has script and style files;
* Any page uses the widget should also include the **its script & styles** files into the page.
* The page should also care about **depended libraries/files** of the widget.
ABP solves these issues when you properly relate the resources with the widget. You don't care about dependencies of the widget while using it.
### Defining as Simple File Paths
The example widget below adds a style and a script file:
````csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(
StyleFiles = new[] { "/Pages/Components/MySimpleWidget/Default.css" },
ScriptFiles = new[] { "/Pages/Components/MySimpleWidget/Default.js" }
)]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
ABP takes account these dependencies and properly adds to the view/page when you use the widget. Style/script files can be **physical or virtual**. It is completely integrated to the [Virtual File System](../Virtual-File-System.md).
### Defining Bundle Contributors
All resources for used widgets in a page are added as a **bundle** (bundled & minified in production if you don't configure otherwise). In addition to adding a simple file, you can take full power of the bundle contributors.
The sample code below does the same with the code above, but defines and uses bundle contributors:
````csharp
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(
StyleTypes = new []{ typeof(MySimpleWidgetStyleBundleContributor) },
ScriptTypes = new[]{ typeof(MySimpleWidgetScriptBundleContributor) }
)]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
public class MySimpleWidgetStyleBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files
.AddIfNotContains("/Pages/Components/MySimpleWidget/Default.css");
}
}
public class MySimpleWidgetScriptBundleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files
.AddIfNotContains("/Pages/Components/MySimpleWidget/Default.js");
}
}
}
````
Bundle contribution system is very powerful. If your widget uses a JavaScript library to render a chart, then you can declare it as a dependency, so the JavaScript library is automatically added to the page if it wasn't added before. In this way, the page using your widget doesn't care about the dependencies.
See the [bundling & minification](Bundling-Minification.md) documentation for more information about that system.
## RefreshUrl
A widget may design a `RefreshUrl` that is used whenever the widget needs to be refreshed. If it is defined, the widget is re-rendered on the server side on every refresh (see the refresh `method` of the `WidgetManager` below).
````csharp
[Widget(RefreshUrl = "Widgets/Counters")]
public class CountersWidgetViewComponent : AbpViewComponent
{
}
````
Once you define a `RefreshUrl` for your widget, you need to provide an endpoint to render and return it:
````csharp
[Route("Widgets")]
public class CountersWidgetController : AbpController
{
[HttpGet]
[Route("Counters")]
public IActionResult Counters(DateTime startDate, DateTime endDate)
{
return ViewComponent("CountersWidget", new {startDate, endDate});
}
}
````
`Widgets/Counters` route matches to the `RefreshUrl` declared before.
> A widget supposed to be refreshed in two ways: In the first way, when you use a `RefreshUrl`, it re-rendered on the server and replaced by the HTML returned from server. In the second way the widget gets data (generally a JSON object) from server and refreshes itself in the client side (see the refresh method in the Widget JavaScript API section).
## JavaScript API
A widget may need to be rendered and refreshed in the client side. In such cases, you can use ABP's `WidgetManager` and define APIs for your widgets.
### WidgetManager
`WidgetManager` is used to initialize and refresh one or more widgets. Create a new `WidgetManager` as shown below:
````js
$(function() {
var myWidgetManager = new abp.WidgetManager('#MyDashboardWidgetsArea');
})
````
`MyDashboardWidgetsArea` may contain one or more widgets inside.
> Using the `WidgetManager` inside document.ready (like above) is a good practice since its functions use the DOM and need DOM to be ready.
#### WidgetManager.init()
`init` simply initializes the `WidgetManager` and calls `init` methods of the related widgets if they define (see Widget JavaScript API section below)
```js
myWidgetManager.init();
```
#### WidgetManager.refresh()
`refresh` method refreshes all widgets related to this `WidgetManager`:
````
myWidgetManager.refresh();
````
#### WidgetManager Options
WidgetManager has some additional options.
##### Filter Form
If your widgets require parameters/filters then you will generally have a form to filter the widgets. In such cases, you can create a form that has some form elements and a dashboard area with some widgets inside. Example:
````xml
<form method="get" id="MyDashboardFilterForm">
...form elements
</form>
<div id="MyDashboardWidgetsArea" data-widget-filter="#MyDashboardFilterForm">
...widgets
</div>
````
`data-widget-filter` attribute relates the form with the widgets. Whenever the form is submitted, all the widgets are automatically refreshed with the form fields as the filter.
Instead of the `data-widget-filter` attribute, you can use the `filterForm` parameter of the `WidgetManager` constructor. Example:
````js
var myWidgetManager = new abp.WidgetManager({
wrapper: '#MyDashboardWidgetsArea',
filterForm: '#MyDashboardFilterForm'
});
````
##### Filter Callback
You may want to have a better control to provide filters while initializing and refreshing the widgets. In this case, you can use the `filterCallback` option:
````js
var myWidgetManager = new abp.WidgetManager({
wrapper: '#MyDashboardWidgetsArea',
filterCallback: function() {
return $('#MyDashboardFilterForm').serializeFormToObject();
}
});
````
This example shows the default implementation of the `filterCallback`. You can return any JavaScript object with fields. Example:
````js
filterCallback: function() {
return {
'startDate': $('#StartDateInput').val(),
'endDate': $('#EndDateInput').val()
};
}
````
The returning filters are passed to all widgets on `init` and `refresh`.
### Widget JavaScript API
A widget can define a JavaScript API that is invoked by the `WidgetManager` when needed. The code sample below can be used to start to define an API for a widget.
````js
(function () {
abp.widgets.NewUserStatisticWidget = function ($wrapper) {
var getFilters = function () {
return {
...
};
}
var refresh = function (filters) {
...
};
var init = function (filters) {
...
};
return {
getFilters: getFilters,
init: init,
refresh: refresh
};
};
})();
````
`NewUserStatisticWidget` is the name of the widget here. It should match the widget name defined in the server side. All of the functions are optional.
#### getFilters
If the widget has internal custom filters, this function should return the filter object. Example:
````js
var getFilters = function() {
return {
frequency: $wrapper.find('.frequency-filter option:selected').val()
};
}
````
This method is used by the `WidgetManager` while building filters.
#### init
Used to initialize the widget when needed. It has a filter argument that can be used while getting data from server. `init` method is used when `WidgetManager.init()` function is called. It is also called if your widget requires a full re-load on refresh. See the `RefreshUrl` widget option.
#### refresh
Used to refresh the widget when needed. It has a filter argument that can be used while getting data from server. `refresh` method is used whenever `WidgetManager.refresh()` function is called.
## Authorization
Some widgets may need to be available only for authenticated or authorized users. In this case, use the following properties of the `Widget` attribute:
* `RequiresAuthentication` (`bool`): Set to true to make this widget usable only for authentication users (user have logged in to the application).
* `RequiredPolicies` (`List<string>`): A list of policy names to authorize the user. See [the authorization document](../Authorization.md) for more info about policies.
Example:
````csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
{
[Widget(RequiredPolicies = new[] { "MyPolicyName" })]
public class MySimpleWidgetViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View();
}
}
}
````
## WidgetOptions
As alternative to the `Widget` attribute, you can use the `WidgetOptions` to configure widgets:
```csharp
Configure<WidgetOptions>(options =>
{
options.Widgets.Add<MySimpleWidgetViewComponent>();
});
```
Write this into the `ConfigureServices` method of your [module](../Module-Development-Basics.md). All the configuration done with the `Widget` attribute is also possible with the `WidgetOptions`. Example configuration that adds a style for the widget:
````csharp
Configure<WidgetOptions>(options =>
{
options.Widgets
.Add<MySimpleWidgetViewComponent>()
.WithStyles("/Pages/Components/MySimpleWidget/Default.css");
});
````
> Tip: `WidgetOptions` can also be used to get an existing widget and change its configuration. This is especially useful if you want to modify the configuration of a widget inside a module used by your application. Use `options.Widgets.Find` to get an existing `WidgetDefinition`.
## See Also
* [Example project (source code)](https://github.com/abpframework/abp/tree/dev/samples/DashboardDemo).

53
docs/en/docs-nav.json

@ -241,43 +241,60 @@
]
},
{
"text": "ASP.NET Core",
"text": "API",
"items": [
{
"text": "API",
"items": [
{
"text": "Auto API Controllers",
"path": "AspNetCore/Auto-API-Controllers.md"
},
{
"text": "Dynamic C# API Clients",
"path": "AspNetCore/Dynamic-CSharp-API-Clients.md"
}
]
"text": "Auto API Controllers",
"path": "API/Auto-API-Controllers.md"
},
{
"text": "User Interface",
"text": "Dynamic C# API Clients",
"path": "API/Dynamic-CSharp-API-Clients.md"
}
]
},
{
"text": "User Interface",
"items": [
{
"text": "ASP.NET Core",
"items": [
{
"text": "Client Side Package Management",
"path": "AspNetCore/Client-Side-Package-Management.md"
"path": "UI/AspNetCore/Client-Side-Package-Management.md"
},
{
"text": "Bundling & Minification",
"path": "AspNetCore/Bundling-Minification.md"
"path": "UI/AspNetCore/Bundling-Minification.md"
},
{
"text": "Tag Helpers",
"path": "AspNetCore/Tag-Helpers/Index.md"
"path": "UI/AspNetCore/Tag-Helpers/Index.md"
},
{
"text": "Widgets",
"path": "AspNetCore/Widgets.md"
"path": "UI/AspNetCore/Widgets.md"
},
{
"text": "Theming",
"path": "AspNetCore/Theming.md"
"path": "UI/AspNetCore/Theming.md"
}
]
},
{
"text": "Angular",
"items": [
{
"text": "Localization",
"path": "UI/Angular/Localization.md"
},
{
"text": "Permission Management",
"path": "UI/Angular/PermissionManagement.md"
},
{
"text": "Component Replacement",
"path": "UI/Angular/ComponentReplacement.md"
}
]
}

Loading…
Cancel
Save