@ -0,0 +1,53 @@
|
||||
# ABP.IO Platform 4.2 Final Has Been Released!
|
||||
|
||||
[ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) 4.2 versions have been released today.
|
||||
|
||||
## What's New With 4.2?
|
||||
|
||||
Since all the new features are already explained in details with the [4.2 RC Announcement Post](https://blog.abp.io/abp/ABP-IO-Platform-v4-2-RC-Has-Been-Released), I will not repeat all the details again. See the [RC Blog Post](https://blog.abp.io/abp/ABP-IO-Platform-v4-2-RC-Has-Been-Released) for all the features and enhancements.
|
||||
|
||||
## Creating New Solutions
|
||||
|
||||
You can create a new solution with the ABP Framework version 4.2 by either using the `abp new` command or using the **direct download** tab on the [get started page](https://abp.io/get-started).
|
||||
|
||||
> See the [getting started document](https://docs.abp.io/en/abp/latest/Getting-Started) for details.
|
||||
|
||||
## How to Upgrade an Existing Solution
|
||||
|
||||
### Install/Update the ABP CLI
|
||||
|
||||
First of all, install the ABP CLI or upgrade to the latest version.
|
||||
|
||||
If you haven't installed yet:
|
||||
|
||||
```bash
|
||||
dotnet tool install -g Volo.Abp.Cli
|
||||
```
|
||||
|
||||
To update an existing installation:
|
||||
|
||||
```bash
|
||||
dotnet tool update -g Volo.Abp.Cli
|
||||
```
|
||||
|
||||
### ABP UPDATE Command
|
||||
|
||||
[ABP CLI](https://docs.abp.io/en/abp/latest/CLI) provides a handy command to update all the ABP related NuGet and NPM packages in your solution with a single command:
|
||||
|
||||
```bash
|
||||
abp update
|
||||
```
|
||||
|
||||
Run this command in the root folder of your solution.
|
||||
|
||||
## Migration Guide
|
||||
|
||||
Check [the migration guide](https://docs.abp.io/en/abp/latest/Migration-Guides/Abp-4_2) for the applications with the version 4.x upgrading to the version 4.2.
|
||||
|
||||
> It is strongly recommended to check the migration guide for this version. Especially, the new `IRepository.GetQueryableAsync()` method is a core change should be considered after upgrading the solution.
|
||||
|
||||
## About the Next Version
|
||||
|
||||
The next feature version will be 4.3. It is planned to release the 4.3 RC (Release Candidate) on March 11 and the final version on March 25, 2021.
|
||||
|
||||
We decided to slow down the feature development for the [next milestone](https://github.com/abpframework/abp/milestone/49). We will continue to improve the existing features and introduce new ones, sure, but wanted to have more time for the planning, documentation, creating guides and improving the development experience.
|
||||
@ -0,0 +1,103 @@
|
||||
## Using MatBlazor UI Components With the ABP Framework
|
||||
|
||||
Hi, in this step by step article, I will show you how to integrate [MatBlazor](https://www.matblazor.com/), a blazor UI components into ABP Framework-based applications.
|
||||
|
||||

|
||||
|
||||
*(A screenshot from the example application developed in this article)*
|
||||
|
||||
## Create the Project
|
||||
|
||||
> First thing is to create the project. ABP Framework offers startup templates to get into business faster.
|
||||
|
||||
In this article, I will create a new startup template with EF Core as a database provider and Blazor for UI framework. But if you already have a project with Blazor UI, you don't need to create a new startup template, you can directly implement the following steps to your existing project.
|
||||
|
||||
> If you already have a project with the Blazor UI, you can skip this section.
|
||||
|
||||
* Before starting the development, we will create a new solution named `MatBlazorSample` (or whatever you want). We will create a new startup template with EF Core as a database provider and Blazor for UI framework by using [ABP CLI](https://docs.abp.io/en/abp/latest/CLI):
|
||||
|
||||
````bash
|
||||
abp new MatBlazorSample -u blazor
|
||||
````
|
||||
|
||||
This will create new project inside of `aspnet-core`, so:
|
||||
|
||||
````bash
|
||||
cd aspnet-core
|
||||
````
|
||||
|
||||
and
|
||||
|
||||
````bash
|
||||
dotnet restore
|
||||
````
|
||||
|
||||
* Our project boilerplate will be ready after the download is finished. Then, we can open the solution in the Visual Studio (or any other IDE) and run the `MatBlazorSample.DbMigrator` to create the database and seed initial data (which creates the admin user, admin role, permissions etc.)
|
||||
|
||||

|
||||
|
||||
* After database and initial data created,
|
||||
* Run the `MatBlazorSample.HttpApi.Host` to see our server side working and
|
||||
* Run the `MatBlazorSample.Blazor` to see our UI working properly.
|
||||
|
||||
> _Default login credentials for admin: username is **admin** and password is **1q2w3E\***_
|
||||
|
||||
## Install MatBlazor
|
||||
|
||||
You can follow [this documentation](https://www.matblazor.com/) to install MatBlazor packages into your computer.
|
||||
|
||||
### Adding MatBlazor NuGet Packages
|
||||
|
||||
```bash
|
||||
Install-Package MatBlazor
|
||||
```
|
||||
|
||||
### Register MatBlazor Resources
|
||||
|
||||
1. Add the following line to the HEAD section of the `wwwroot/index.html` file within the `MatBlazorSample.Blazor` project:
|
||||
|
||||
```Razor
|
||||
<head>
|
||||
<!--...-->
|
||||
<script src="_content/MatBlazor/dist/matBlazor.js"></script>
|
||||
<link href="_content/MatBlazor/dist/matBlazor.css" rel="stylesheet" />
|
||||
</head>
|
||||
```
|
||||
|
||||
2. In the `MatBlazorSampleBlazorModule` class, call the `AddMatBlazor()` method from your project's `ConfigureServices()` method:
|
||||
|
||||
```csharp
|
||||
public override void ConfigureServices(ServiceConfigurationContext context)
|
||||
{
|
||||
var environment = context.Services.GetSingletonInstance<IWebAssemblyHostEnvironment>();
|
||||
var builder = context.Services.GetSingletonInstance<WebAssemblyHostBuilder>();
|
||||
// ...
|
||||
builder.Services.AddMatBlazor();
|
||||
}
|
||||
```
|
||||
|
||||
3. Register the **MatBlazorSample.Blazor** namespace in the `_Imports.razor` file:
|
||||
|
||||
```Razor
|
||||
@using MatBlazor
|
||||
```
|
||||
|
||||
## The Sample Application
|
||||
|
||||
We have created a sample application with [Table](https://www.matblazor.com/Table) example.
|
||||
|
||||
### The Source Code
|
||||
|
||||
You can download the source code from [here](https://github.com/abpframework/abp-samples/tree/master/MatBlazorSample).
|
||||
|
||||
The related files for this example are marked in the following screenshots.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Conclusion
|
||||
|
||||
In this article, I've explained how to use [MatBlazor](https://www.matblazor.com/) components in your application. ABP Framework is designed so that it can work with any UI library/framework.
|
||||
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 392 KiB |
@ -0,0 +1,101 @@
|
||||
# ABP version 4.2 Migration Guide
|
||||
|
||||
This version has no breaking changes but there is an important change on the repositories that should be applied for your application for an important performance and scalability gain.
|
||||
|
||||
## IRepository.GetQueryableAsync
|
||||
|
||||
`IRepository` interface inherits `IQueryable`, so you can directly use the standard LINQ extension methods, like `Where`, `OrderBy`, `First`, `Sum`... etc.
|
||||
|
||||
**Example: Using LINQ directly over the repository object**
|
||||
|
||||
````csharp
|
||||
public class BookAppService : ApplicationService, IBookAppService
|
||||
{
|
||||
private readonly IRepository<Book, Guid> _bookRepository;
|
||||
|
||||
public BookAppService(IRepository<Book, Guid> bookRepository)
|
||||
{
|
||||
_bookRepository = bookRepository;
|
||||
}
|
||||
|
||||
public async Task DoItInOldWayAsync()
|
||||
{
|
||||
//Apply any standard LINQ extension method
|
||||
var query = _bookRepository
|
||||
.Where(x => x.Price > 10)
|
||||
.OrderBy(x => x.Name);
|
||||
|
||||
//Execute the query asynchronously
|
||||
var books = await AsyncExecuter.ToListAsync(query);
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
*See [the documentation](https://docs.abp.io/en/abp/4.2/Repositories#iqueryable-async-operations) if you wonder what is the `AsyncExecuter`.*
|
||||
|
||||
**Beginning from the version 4.2, the recommended way is using `IRepository.GetQueryableAsync()` to obtain an `IQueryable`, then use the LINQ extension methods over it.**
|
||||
|
||||
**Example: Using the new GetQueryableAsync method**
|
||||
|
||||
````csharp
|
||||
public async Task DoItInNewWayAsync()
|
||||
{
|
||||
//Use GetQueryableAsync to obtain the IQueryable<Book> first
|
||||
var queryable = await _bookRepository.GetQueryableAsync();
|
||||
|
||||
//Then apply any standard LINQ extension method
|
||||
var query = queryable
|
||||
.Where(x => x.Price > 10)
|
||||
.OrderBy(x => x.Name);
|
||||
|
||||
//Finally, execute the query asynchronously
|
||||
var books = await AsyncExecuter.ToListAsync(query);
|
||||
}
|
||||
````
|
||||
|
||||
ABP may start a database transaction when you get an `IQueryable` (If current [Unit Of Work](https://docs.abp.io/en/abp/latest/Unit-Of-Work) is transactional). In this new way, it is possible to **start the database transaction in an asynchronous way**. Previously, we could not get the advantage of asynchronous while starting the transactions.
|
||||
|
||||
> **The new way has a significant performance and scalability gain. The old usage (directly using LINQ over the repositories) will be removed in the next major version (5.0).** You have a lot of time for the change, but we recommend to immediately take the action since the old usage has a big **scalability problem**.
|
||||
|
||||
### Actions to Take
|
||||
|
||||
* Use the repository's queryable feature as explained before.
|
||||
* If you've overridden `CreateFilteredQuery` in a class derived from `CrudAppService`, you should override the `CreateFilteredQueryAsync` instead and remove the `CreateFilteredQuery` in your class.
|
||||
* If you've overridden `WithDetails` in your custom repositories, remove it and override `WithDetailsAsync` instead.
|
||||
* If you've used `DbContext` or `DbSet` properties in your custom repositories, use `GetDbContextAsync()` and `GetDbSetAsync()` methods instead of them.
|
||||
|
||||
You can re-build your solution and check the `Obsolete` warnings to find some of the usages need to change.
|
||||
|
||||
#### About IRepository Async Extension Methods
|
||||
|
||||
Using IRepository Async Extension Methods has no such a problem. The examples below are pretty fine:
|
||||
|
||||
````csharp
|
||||
var countAll = await _personRepository
|
||||
.CountAsync();
|
||||
|
||||
var count = await _personRepository
|
||||
.CountAsync(x => x.Name.StartsWith("A"));
|
||||
|
||||
var book1984 = await _bookRepository
|
||||
.FirstOrDefaultAsync(x => x.Name == "John");
|
||||
````
|
||||
|
||||
See the [repository documentation](https://docs.abp.io/en/abp/4.2/Repositories#iqueryable-async-operations) to understand the relation between `IQueryable` and asynchronous operations.
|
||||
|
||||
## .NET Package Upgrades
|
||||
|
||||
ABP uses the latest 5.0.* .NET packages. If your application is using 5.0.0 packages, you may get an error on build. We recommend to depend on the .NET packages like `5.0.*` in the `.csproj` files to use the latest patch versions.
|
||||
|
||||
Example:
|
||||
|
||||
````xml
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.*" />
|
||||
````
|
||||
|
||||
## Blazorise Library Upgrade
|
||||
|
||||
If you are upgrading to 4.2, you also need also upgrade the following packages in your Blazor application;
|
||||
|
||||
* `Blazorise.Bootstrap` to `0.9.3-preview6`
|
||||
* `Blazorise.Icons.FontAwesome` to `0.9.3-preview6`
|
||||
@ -1,5 +1,6 @@
|
||||
# ABP Framework Migration Guides
|
||||
|
||||
* [3.3.x to 4.0 Migration Guide](Abp-4_0.md)
|
||||
* [2.9.x to 3.0 Migration Guide](../UI/Angular/Migration-Guide-v3.md)
|
||||
* [4.x to 4.2](Abp-4_2.md)
|
||||
* [3.3.x to 4.0](Abp-4_0.md)
|
||||
* [2.9.x to 3.0](../UI/Angular/Migration-Guide-v3.md)
|
||||
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
# Ellipsis
|
||||
|
||||
Text inside an HTML element can be truncated easily with an ellipsis by using CSS. To make this even easier, you can use the `EllipsisDirective` which has been exposed by the `@abp/ng.theme.shared` package.
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
In order to use the `EllipsisDirective` in an HTML template, the **`ThemeSharedModule`** should be imported into your module like this:
|
||||
|
||||
```js
|
||||
// ...
|
||||
import { ThemeSharedModule } from '@abp/ng.theme.shared';
|
||||
|
||||
@NgModule({
|
||||
//...
|
||||
imports: [..., ThemeSharedModule],
|
||||
})
|
||||
export class MyFeatureModule {}
|
||||
```
|
||||
|
||||
or **if you would not like to import** the `ThemeSharedModule`, you can import the **`EllipsisModule`** as shown below:
|
||||
|
||||
|
||||
```js
|
||||
// ...
|
||||
import { EllipsisModule } from '@abp/ng.theme.shared';
|
||||
|
||||
@NgModule({
|
||||
//...
|
||||
imports: [..., EllipsisModule],
|
||||
})
|
||||
export class MyFeatureModule {}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The `EllipsisDirective` is very easy to use. The directive's selector is **`abpEllipsis`**. By adding the `abpEllipsis` attribute to an HTML element, you can activate the `EllipsisDirective` for the HTML element.
|
||||
|
||||
See an example usage:
|
||||
|
||||
```html
|
||||
<p abpEllipsis>
|
||||
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Laboriosam commodi quae aspernatur,
|
||||
corporis velit et suscipit id consequuntur amet minima expedita cum reiciendis dolorum
|
||||
cupiditate? Voluptas eaque voluptatum odio deleniti quo vel illum nemo accusamus nulla ratione
|
||||
impedit dolorum expedita necessitatibus fugiat ullam beatae, optio eum cupiditate ducimus
|
||||
architecto.
|
||||
</p>
|
||||
```
|
||||
|
||||
The `abpEllipsis` attribute has been added to the `<p>` element that containing very long text inside to activate the `EllipsisDirective`.
|
||||
|
||||
See the result:
|
||||
|
||||

|
||||
|
||||
The long text has been truncated by using the directive.
|
||||
|
||||
The UI before using the directive looks like this:
|
||||
|
||||

|
||||
|
||||
### Specifying Max Width of an HTML Element
|
||||
|
||||
An HTML element max width can be specified as shown below:
|
||||
|
||||
```html
|
||||
<div [abpEllipsis]="'100px'">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque, optio!
|
||||
</div>
|
||||
|
||||
<div [abpEllipsis]="'15vw'">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque, optio!
|
||||
</div>
|
||||
|
||||
<div [abpEllipsis]="'50%'">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque, optio!
|
||||
</div>
|
||||
```
|
||||
|
||||
See the result:
|
||||
|
||||

|
||||
@ -0,0 +1,146 @@
|
||||
# Router Events Simplified
|
||||
|
||||
`RouterEvents` is a utility service to provide an easy implementation for one of the most frequent needs in Angular templates: `TrackByFunction`. Please see [this page in Angular docs](https://angular.io/guide/template-syntax#ngfor-with-trackby) for its purpose.
|
||||
|
||||
|
||||
|
||||
|
||||
## Benefit
|
||||
|
||||
You can use router events directly and filter them as seen below:
|
||||
|
||||
```js
|
||||
import {
|
||||
NavigationEnd,
|
||||
NavigationError,
|
||||
NavigationCancel,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
import { filter } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
class SomeService {
|
||||
navigationFinish$ = this.router.events.pipe(
|
||||
filter(
|
||||
event =>
|
||||
event instanceof NavigationEnd ||
|
||||
event instanceof NavigationError ||
|
||||
event instanceof NavigationCancel,
|
||||
),
|
||||
);
|
||||
/* Observable<Event> */
|
||||
|
||||
constructor(private router: Router) {}
|
||||
}
|
||||
```
|
||||
|
||||
However, `RouterEvents` makes filtering router events easier.
|
||||
|
||||
```js
|
||||
import { RouterEvents } from '@abp/ng.core';
|
||||
|
||||
@Injectable()
|
||||
class SomeService {
|
||||
navigationFinish$ = this.routerEvents.getNavigationEvents('End', 'Error', 'Cancel');
|
||||
/* Observable<NavigationCancel | NavigationEnd | NavigationError> */
|
||||
|
||||
constructor(private routerEvents: RouterEvents) {}
|
||||
}
|
||||
```
|
||||
|
||||
`RouterEvents` also delivers improved type-safety. In the example above, `navigationFinish$` has inferred type of `Observable<NavigationCancel | NavigationEnd | NavigationError>` whereas it would have `Observable<Event>` when router events are filtered directly.
|
||||
|
||||
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
You do not have to provide `RouterEvents` at the module or component level, because it is already **provided in root**. You can inject and start using it immediately in your components.
|
||||
|
||||
|
||||
### How to Get Specific Navigation Events
|
||||
|
||||
You can use `getNavigationEvents` to get a stream of navigation events matching given event keys.
|
||||
|
||||
```js
|
||||
import { RouterEvents } from '@abp/ng.core';
|
||||
import { merge } from 'rxjs';
|
||||
import { mapTo } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
class SomeService {
|
||||
navigationStart$ = this.routerEvents.getNavigationEvents('Start');
|
||||
/* Observable<NavigationStart> */
|
||||
|
||||
navigationFinish$ = this.routerEvents.getNavigationEvents('End', 'Error', 'Cancel');
|
||||
/* Observable<NavigationCancel | NavigationEnd | NavigationError> */
|
||||
|
||||
loading$ = merge(
|
||||
this.navigationStart$.pipe(mapTo(true)),
|
||||
this.navigationFinish$.pipe(mapTo(false)),
|
||||
);
|
||||
/* Observable<boolean> */
|
||||
|
||||
constructor(private routerEvents: RouterEvents) {}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### How to Get All Navigation Events
|
||||
|
||||
You can use `getAllNavigationEvents` to get a stream of all navigation events without passing any keys.
|
||||
|
||||
```js
|
||||
import { RouterEvents, NavigationStart } from '@abp/ng.core';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
class SomeService {
|
||||
navigationEvent$ = this.routerEvents.getAllNavigationEvents();
|
||||
/* Observable<NavigationCancel | NavigationEnd | NavigationError | NavigationStart> */
|
||||
|
||||
loading$ = this.navigationEvent$.pipe(
|
||||
map(event => event instanceof NavigationStart),
|
||||
);
|
||||
/* Observable<boolean> */
|
||||
|
||||
constructor(private routerEvents: RouterEvents) {}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### How to Get Specific Router Events
|
||||
|
||||
You can use `getEvents` to get a stream of router events matching given event constructors.
|
||||
|
||||
```js
|
||||
import { RouterEvents } from '@abp/ng.core';
|
||||
import { ActivationEnd, ChildActivationEnd } from '@angular/router';
|
||||
|
||||
@Injectable()
|
||||
class SomeService {
|
||||
moduleActivation$ = this.routerEvents.getEvents(ActivationEnd, ChildActivationEnd);
|
||||
/* Observable<ActivationEnd | ChildActivationEnd> */
|
||||
|
||||
constructor(private routerEvents: RouterEvents) {}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### How to Get All Router Events
|
||||
|
||||
You can use `getEvents` to get a stream of all router events without passing any event constructors. This is nothing different from accessing `events` property of `Router` and is added to the service just for convenience.
|
||||
|
||||
```js
|
||||
import { RouterEvents } from '@abp/ng.core';
|
||||
import { ActivationEnd, ChildActivationEnd } from '@angular/router';
|
||||
|
||||
@Injectable()
|
||||
class SomeService {
|
||||
routerEvent$ = this.routerEvents.getAllEvents();
|
||||
/* Observable<Event> */
|
||||
|
||||
constructor(private routerEvents: RouterEvents) {}
|
||||
}
|
||||
```
|
||||
|
||||
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 46 KiB |