mirror of https://github.com/abpframework/abp.git
committed by
GitHub
41 changed files with 584 additions and 223 deletions
@ -0,0 +1,157 @@ |
|||||
|
```json |
||||
|
//[doc-seo] |
||||
|
{ |
||||
|
"Description": "This migration guide provides a comprehensive overview of the necessary code changes when upgrading your ABP solution from Blazorise 1.x to 2.0, ensuring a smooth transition to the latest version." |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
# ABP Blazorise 1.x to 2.0 Migration Guide |
||||
|
|
||||
|
This document summarizes the required code changes when upgrading ABP solutions from Blazorise 1.x to 2.0. |
||||
|
|
||||
|
## 1. Package upgrades |
||||
|
|
||||
|
Upgrade Blazorise-related packages to `2.0.0`. |
||||
|
|
||||
|
- `Blazorise` |
||||
|
- `Blazorise.Components` |
||||
|
- `Blazorise.DataGrid` |
||||
|
- `Blazorise.Snackbar` |
||||
|
- `Blazorise.Bootstrap5` |
||||
|
- `Blazorise.Icons.FontAwesome` |
||||
|
|
||||
|
## 2. Input component renames |
||||
|
|
||||
|
Blazorise 2.0 uses new input component names: |
||||
|
|
||||
|
- `TextEdit` -> `TextInput` |
||||
|
- `MemoEdit` -> `MemoInput` |
||||
|
- `DateEdit` -> `DateInput` |
||||
|
- `TimeEdit` -> `TimeInput` |
||||
|
- `NumericEdit` -> `NumericInput` |
||||
|
- `ColorEdit` -> `ColorInput` |
||||
|
- `FileEdit` -> `FileInput` |
||||
|
|
||||
|
## 3. Binding API normalization to Value/ValueChanged |
||||
|
|
||||
|
Migrate old binding/value APIs to the new `Value` model. |
||||
|
|
||||
|
- `@bind-Text` -> `@bind-Value` |
||||
|
- `Text` / `TextChanged` -> `Value` / `ValueChanged` |
||||
|
- `@bind-Checked` -> `@bind-Value` |
||||
|
- `Checked` / `CheckedChanged` -> `Value` / `ValueChanged` |
||||
|
- `CheckedValue` / `CheckedValueChanged` -> `Value` / `ValueChanged` |
||||
|
- `@bind-Date` / `@bind-Time` -> `@bind-Value` |
||||
|
- `Date` / `DateChanged` -> `Value` / `ValueChanged` |
||||
|
- `Time` / `TimeChanged` -> `Value` / `ValueChanged` |
||||
|
- `@bind-SelectedValue` (for `Select`) -> `@bind-Value` |
||||
|
- `SelectedValue` / `SelectedValueChanged` (for `Select`) -> `Value` / `ValueChanged` |
||||
|
- `@bind-Checked` (for `Switch`) -> `@bind-Value` |
||||
|
- `Checked` / `CheckedChanged` (for `Switch`) -> `Value` / `ValueChanged` |
||||
|
|
||||
|
## 4. DatePicker and Select multiple changes |
||||
|
|
||||
|
### DatePicker range mode |
||||
|
|
||||
|
For `SelectionMode="DateInputSelectionMode.Range"`, the old `Dates` / `DatesChanged` parameters are replaced by the unified `Value` / `ValueChanged`. Use an array or `IReadOnlyList<T>` as `TValue`: |
||||
|
|
||||
|
- `@bind-Dates` -> `@bind-Value` (with `TValue="DateTime[]"` or `TValue="IReadOnlyList<DateTime>"`) |
||||
|
- `Dates` / `DatesChanged` -> `Value` / `ValueChanged` |
||||
|
|
||||
|
### DatePicker single value mode |
||||
|
|
||||
|
For non-range `DatePicker` usage: |
||||
|
|
||||
|
- `Date` / `DateChanged` -> `Value` / `ValueChanged` |
||||
|
|
||||
|
### Select multiple mode |
||||
|
|
||||
|
For `<Select Multiple ...>`, the old `SelectedValues` / `SelectedValuesChanged` parameters are replaced by the unified `Value` / `ValueChanged`. Use an array or `IReadOnlyList<T>` as `TValue`: |
||||
|
|
||||
|
- `@bind-SelectedValues` -> `@bind-Value` (with `TValue="string[]"` or `TValue="IReadOnlyList<string>"`) |
||||
|
- `SelectedValues` / `SelectedValuesChanged` -> `Value` / `ValueChanged` |
||||
|
|
||||
|
Example: |
||||
|
|
||||
|
```razor |
||||
|
<Select TValue="int[]" @bind-Value="Selected" Multiple> |
||||
|
<SelectItem Value="1">One</SelectItem> |
||||
|
<SelectItem Value="2">Two</SelectItem> |
||||
|
</Select> |
||||
|
|
||||
|
@code { |
||||
|
private int[] Selected { get; set; } = new int[] { 1 }; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Empty SelectItem type requirement |
||||
|
|
||||
|
For empty placeholder items, set explicit `TValue`: |
||||
|
|
||||
|
- `<SelectItem></SelectItem>` -> `<SelectItem TValue="string"></SelectItem>` (or another correct type such as `Guid?`) |
||||
|
|
||||
|
## 5. DataGrid migration |
||||
|
|
||||
|
### 5.1 Page parameter rename |
||||
|
|
||||
|
- `CurrentPage` -> `Page` on `DataGrid` |
||||
|
|
||||
|
Important: `AbpExtensibleDataGrid` still uses `CurrentPage` (for example ABP v10.2). Do not rename it to `Page`. |
||||
|
|
||||
|
### 5.2 DisplayTemplate context type change |
||||
|
|
||||
|
Inside `DisplayTemplate`, use `context.Item` instead of directly using `context`. |
||||
|
|
||||
|
Typical updates: |
||||
|
|
||||
|
- `context.Property` -> `context.Item.Property` |
||||
|
- `Method(context)` -> `Method(context.Item)` |
||||
|
- `() => Method(context)` -> `() => Method(context.Item)` |
||||
|
- For custom template variable names, same rule applies: `row.Property` -> `row.Item.Property` |
||||
|
|
||||
|
The same rule also applies to action handlers in `DataGridEntityActionsColumn` and `DataGridCommandColumn` (such as `Clicked`, `Visible`, and `ConfirmationMessage`): |
||||
|
|
||||
|
- `Clicked="async () => await action.Clicked(context)"` -> `Clicked="async () => await action.Clicked(context.Item)"` |
||||
|
- `Visible="action.Visible(context)"` -> `Visible="action.Visible(context.Item)"` |
||||
|
|
||||
|
Important: This change applies to DataGrid template contexts only (`DisplayTemplate` in `DataGridColumn`, `DataGridEntityActionsColumn`, etc.). In non-DataGrid templates (for example `TreeView` `NodeContent`), `context` is already the item and should remain unchanged (for example `DeleteMenuItemAsync(context)`). |
||||
|
|
||||
|
### 5.3 Width type change (string -> Fluent sizing) |
||||
|
|
||||
|
DataGrid column `Width` moved from plain string to fluent sizing APIs: |
||||
|
|
||||
|
- `Width="30px"` -> `Width="Width.Px(30)"` |
||||
|
- `Width="60px"` -> `Width="Width.Px(60)"` |
||||
|
- `Width="0.5rem"` -> `Width="Width.Px(8)"` (or another equivalent pixel value) |
||||
|
- `Width="50%"` -> `Width="Width.Percent(50)"` or `Width="Width.Is50"` |
||||
|
- `Width="100%"` -> `Width="Width.Is100"` |
||||
|
|
||||
|
For dynamic string widths (for example `column.Width`), ABP introduces `BlazoriseFluentSizingParse.Parse(...)` to convert string values into `IFluentSizingStyle`. |
||||
|
|
||||
|
```csharp |
||||
|
Width="@BlazoriseFluentSizingParse.Parse(column.Width)" // column.Width is a string |
||||
|
``` |
||||
|
|
||||
|
## 6. Modal parameter placement changes |
||||
|
|
||||
|
`Size` and `Centered` should be placed on `<Modal>`, not on `<ModalContent>`. |
||||
|
|
||||
|
- `<ModalContent Size="..." Centered="true">` -> `<Modal Size="..." Centered="true">` + `<ModalContent>` |
||||
|
|
||||
|
## 7. Other component parameter changes |
||||
|
|
||||
|
- `Dropdown RightAligned="true"` -> `Dropdown EndAligned="true"` |
||||
|
- `Autocomplete MinLength` -> `MinSearchLength` |
||||
|
|
||||
|
## 8. Notes from ABP migration implementation |
||||
|
|
||||
|
- Keep component-specific behavior in mind. Not every component follows exactly the same rename pattern. |
||||
|
- `Autocomplete` usage can still involve `SelectedValue` / `SelectedValueChanged`, depending on component API. |
||||
|
- `BarDropdown` and `Dropdown` are different components; align parameter names according to the actual component type. |
||||
|
|
||||
|
# Reference |
||||
|
|
||||
|
This document may not cover all Blazorise 2.0 changes. For completeness, refer to the official migration guide and release notes: |
||||
|
|
||||
|
- [Blazorise 2.0 - Release Notes](https://blazorise.com/news/release-notes/200) |
||||
|
- [Blazorise 2.0 - Migration Guide](https://blazorise.com/news/migration/200) |
||||
@ -0,0 +1,87 @@ |
|||||
|
using Blazorise; |
||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using System.Text.RegularExpressions; |
||||
|
|
||||
|
namespace Volo.Abp.BlazoriseUI; |
||||
|
|
||||
|
public static class BlazoriseFluentSizingParse |
||||
|
{ |
||||
|
private static readonly Regex SizingPattern = new Regex( |
||||
|
@"^(\d+(?:\.\d+)?)(px|rem|em|ch|vw|vh|%)$", |
||||
|
RegexOptions.IgnoreCase | RegexOptions.Compiled); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Parses a CSS size string into an IFluentSizingStyle.
|
||||
|
/// Supported formats (based on Blazorise FluentSizing source):
|
||||
|
/// Fixed units : 10px, 10rem, 10em, 10ch
|
||||
|
/// Viewport units : 10vw, 10vh
|
||||
|
/// Percentage : 25%, 33%, 50%, 66%, 75%, 100% -> maps to SizingSize enum (CSS class)
|
||||
|
/// other % values -> inline style
|
||||
|
/// Keyword : auto -> SizingSize.Auto
|
||||
|
/// CSS variable : var(--my-var), --my-var, or my-var (all handled by WithVariable)
|
||||
|
/// </summary>
|
||||
|
public static IFluentSizingStyle Parse(string value, SizingType sizingType = SizingType.None) |
||||
|
{ |
||||
|
var fluentSizing = new FluentSizing(sizingType); |
||||
|
|
||||
|
if (string.IsNullOrWhiteSpace(value)) |
||||
|
{ |
||||
|
return fluentSizing; |
||||
|
} |
||||
|
|
||||
|
value = value.Trim(); |
||||
|
|
||||
|
// "auto" -> SizingSize.Auto
|
||||
|
if (value.Equals("auto", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
return (IFluentSizingStyle)fluentSizing.WithSize(SizingSize.Auto); |
||||
|
} |
||||
|
|
||||
|
// CSS variable:
|
||||
|
// "var(--my-var)" -> used as-is
|
||||
|
// "--my-var" -> wrapped as var(--my-var)
|
||||
|
// "my-var" -> prepended "--" and wrapped as var(--my-var)
|
||||
|
// All three cases are handled correctly by Blazorise's GetCssVariableValue.
|
||||
|
if (value.StartsWith("var(", StringComparison.Ordinal) || value.StartsWith("--", StringComparison.Ordinal)) |
||||
|
{ |
||||
|
return fluentSizing.WithVariable(value); |
||||
|
} |
||||
|
|
||||
|
var match = SizingPattern.Match(value); |
||||
|
|
||||
|
if (!match.Success) |
||||
|
{ |
||||
|
return fluentSizing; |
||||
|
} |
||||
|
|
||||
|
var number = double.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture); |
||||
|
var unit = match.Groups[2].Value.ToLowerInvariant(); |
||||
|
|
||||
|
if (unit == "%") |
||||
|
{ |
||||
|
// Standard percentages map to SizingSize enum (generates CSS class via class provider)
|
||||
|
var sizingSize = number switch |
||||
|
{ |
||||
|
25 => SizingSize.Is25, |
||||
|
33 => SizingSize.Is33, |
||||
|
50 => SizingSize.Is50, |
||||
|
66 => SizingSize.Is66, |
||||
|
75 => SizingSize.Is75, |
||||
|
100 => SizingSize.Is100, |
||||
|
_ => SizingSize.Default |
||||
|
}; |
||||
|
|
||||
|
if (sizingSize != SizingSize.Default) |
||||
|
{ |
||||
|
return (IFluentSizingStyle)fluentSizing.WithSize(sizingSize); |
||||
|
} |
||||
|
|
||||
|
// Non-standard percentage falls back to inline style
|
||||
|
return fluentSizing.WithSize("%", number); |
||||
|
} |
||||
|
|
||||
|
// px, rem, em, ch, vw, vh -> inline style via WithSize(unit, size)
|
||||
|
return fluentSizing.WithSize(unit, number); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
@typeparam TEntity |
||||
|
@typeparam TResourceType |
||||
|
@using Volo.Abp.BlazoriseUI |
||||
|
@using Volo.Abp.Localization |
||||
|
@using Volo.Abp.ObjectExtending |
||||
|
@inherits ExtensionPropertyComponentBase<TEntity, TResourceType> |
||||
|
|
||||
|
@if (PropertyInfo != null && Entity != null) |
||||
|
{ |
||||
|
<Validation Validator="@Validate" MessageLocalizer="@LH.Localize"> |
||||
|
<Field> |
||||
|
<FieldLabel>@PropertyInfo.GetLocalizedDisplayName(StringLocalizerFactory)</FieldLabel> |
||||
|
<DateInput TValue="DateTimeOffset?" |
||||
|
InputMode="@(PropertyInfo.IsDate() ? DateInputMode.Date : DateInputMode.DateTime)" |
||||
|
Pattern="@PropertyInfo.GetDateEditInputFormatOrNull()" |
||||
|
Disabled="IsReadonlyField" |
||||
|
@bind-Value="@Value"> |
||||
|
<Feedback> |
||||
|
<ValidationError/> |
||||
|
</Feedback> |
||||
|
</DateInput> |
||||
|
</Field> |
||||
|
</Validation> |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using Volo.Abp.Data; |
||||
|
|
||||
|
namespace Volo.Abp.BlazoriseUI.Components.ObjectExtending; |
||||
|
|
||||
|
public partial class DateTimeOffsetExtensionProperty<TEntity, TResourceType> |
||||
|
where TEntity : IHasExtraProperties |
||||
|
{ |
||||
|
protected DateTimeOffset? Value { |
||||
|
get { |
||||
|
var raw = Entity.GetProperty(PropertyInfo.Name); |
||||
|
return raw switch |
||||
|
{ |
||||
|
null => null, |
||||
|
DateTimeOffset dto => dto, |
||||
|
DateTime dt => dt.Kind switch |
||||
|
{ |
||||
|
DateTimeKind.Utc => new DateTimeOffset(dt, TimeSpan.Zero), |
||||
|
DateTimeKind.Local => new DateTimeOffset(dt), |
||||
|
_ => new DateTimeOffset(DateTime.SpecifyKind(dt, DateTimeKind.Utc), TimeSpan.Zero) |
||||
|
}, |
||||
|
_ => null |
||||
|
}; |
||||
|
} |
||||
|
set { |
||||
|
Entity.SetProperty(PropertyInfo.Name, value, false); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue