These release notes detail the significant changes made between versions 8.2.2 and 8.6.0. For a complete list of changes, please refer to the Serenity Change Log.
The Serenity core library no longer depends on jQuery or any packages that use jQuery. The following script entries in appsettings.bundles.json
can be safely removed unless explicitly used in some of your pages:
"~/Serenity.Assets/jquery/jquery.js",
"~/Serenity.Assets/Scripts/jquery.autoNumeric.js",
"~/Serenity.Assets/Scripts/jquery.colorbox.js",
"~/Serenity.Assets/Scripts/jquery.maskedinput.js",
"~/Serenity.Assets/Scripts/jquery.validate.js",
"~/Serenity.Assets/Scripts/select2.js",
Select2, Autonumeric, and Validate are now integrated into the core library with jQuery dependencies removed. Images will open in a new tab if jquery.colorbox
is not available. We currently do not have a recommendation for maskedinput
due to its limited use; you may add a validation rule to check for the expected format.
If you still need jQuery or any libraries that depend on it, it is okay to leave them as is. Serenity will work fine with jQuery loaded, and Fluent will redirect events through jQuery's event system for interoperability.
The base Select2Editor
class, which LookupEditorBase
and others are based on, has been renamed to ComboboxEditor
to abstract the dependency on a specific dropdown type. Currently, ComboboxEditor
uses the integrated version of select2
, but we may replace it or implement other combobox types via external components in the future.
- Support for
Template.html
and.ts.html
template files has been removed. TemplatedWidget
is deprecated; useWidget
instead.TemplatedPanel
is deprecated; useBasePanel
.TemplatedDialog
is deprecated; useBaseDialog
.
These changes reflect the deprecation of the legacy template mechanism in version 8.2.0. Use the renderContents
method to return markup via TSX/JSX-DOM or Fluent(...).getNode()
. While the legacy getTemplate()
will still be supported internally, it is not recommended.
Serenity uses several decorators like @Decorators.registerClass()
similarly to C# attributes. In the past, enabling decorator support in TypeScript required using an experimental flag in tsconfig.json
:
"experimentalDecorators": true
As of TypeScript 5, experimental decorator support is no longer required. Serenity still supports both the experimental and modern versions, but it is recommended to remove the line above from your tsconfig.json
after updating Serenity and TypeScript (e.g., your Visual Studio version).
Since the new version of TypeScript decorators is only supported by ESBuild 0.21+, update the @serenity-is/tsbuild
package reference to 8.6.0
or later in package.json
:
"devDependencies": {
"@serenity-is/tsbuild": "8.6.0"
}
If you have a separate esbuild
entry in your package.json
, it is recommended to remove it as tsbuild
already includes a reference.
The @Decorators.option()
decorator, used to mark a property as an option, is deprecated. Use constructor options/props instead, as JSX components require options to be passed via the constructor. Setting some options via properties when a widget is created with jsx-dom
is not possible.
Serenity's service-related methods, like serviceCall
, have switched from jQuery Ajax (XMLHTTPRequest)
to fetch
. Since fetch
uses promises, errors thrown from promises should be caught, unlike with jQuery Ajax calls. The following simple service call might log an unhandled rejection error in the browser console if the server returns an error:
SomeService.Delete({
EntityId: 5;
})
// Uncaught (in promise) ...
With promises, it is expected to add a catch statement:
SomeService.Delete({
EntityId: 5;
}).catch((error) => {
// handle error
})
We could add a catch inside the serviceCall
method to make it compatible with the jQuery version, but this would prevent the caller from catching the exception and performing some action based on the error.
To avoid such errors in the console, add the following line in errorhandling-init.ts
(or ScriptInit.ts
if not available in your app):
window.addEventListener("unhandledrejection", ErrorHandling.unhandledRejectionHandler);
Due to differences between System.Text.Json and Newtonsoft.Json, change the return type of the HandleUploadRequest
method in FilePage.cs
to UploadResponse
from ServiceResponse
:
[NonAction]
private UploadResponse HandleUploadRequest(HttpContext context)
{
If this change is not made, upload editors will not work properly, resulting in errors like Uploaded file is not an image
even if the file is an image, as System.Text.Json does not serialize properties of UploadResponse
otherwise.
The Process
method of the IUploadProcessor
used to return an object with the Success
property, which was true
when upload validation was successful or false
when it was not, and the ErrorMessage
property contained information about the problem.
This is no longer the case, and it simply throws an exception in case of an error. Although the Success
property is still in the returned object, it is deprecated and will always be true
.
The if (uploadInfo.success)
line and its else
block in FilePage.cs
are no longer necessary. Please see the latest version of FilePage.cs
in the StartSharp/Serene
repository.
We have introduced a new ILocalTextInitializer
abstraction, which should be used instead of the InitializeLocalTexts
method in Startup.cs
.
Unless you have custom code in InitializeLocalTexts
, please remove it and use AddLocalTextInitializer()
in the ConfigureServices
method:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddLocalTextInitializer();
}
and call app.InitializeLocalTexts()
in the Configure
method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
RowFieldsProvider.SetDefaultFrom(app.ApplicationServices);
app.InitializeLocalTexts();
//...
}
This interface allows us to reinitialize local texts when necessary, for example, after saving texts in the Translation screen.
If you have custom code in the InitializeLocalTexts
method, implement the ILocalTextInitializer
interface (you may subclass DefaultLocalTextInitializer
) and register it in Startup.cs
.
A new IPermissionKeyLister
abstraction has been introduced to get a list of permission keys.
public interface IPermissionKeyLister
{
IEnumerable<string> ListPermissionKeys(bool includeRoles);
}
The code block in UserPermissionRepository
for discovering permission keys has been moved to the AppServices.PermissionKeyLister
class, which implements this interface and is registered in Startup.cs
:
services.AddSingleton<IPermissionKeyLister, AppServices.PermissionKeyLister>();
Currently, there is no default implementation for this abstraction in Serenity itself, as it may change from application to application. For example, some apps might need to load available permission keys from a database table in addition to attributes.
Using this abstraction, we removed the direct dependency on UserPermissionRepository
from several classes like PermissionKeysDataScript
, UserPermissionEndpoint
, etc. Please see the latest code in the StartSharp repository.
Default Implementations of IPermissionService
and IUserAccessor
with Transient Grant/Impersonation Support
The ITransientGrantor
interface allows transiently (temporarily in memory) granting permissions to the current request user before executing an operation.
Similarly, the IImpersonator
interface allows transiently (temporarily in memory) impersonating another user before executing an operation.
This permits a user to perform actions they would not normally be allowed to, such as calling a service only available to admins, given the necessary security checks are in place.
For transient granting to work, the IPermissionService
implementation must also implement the ITransientGrantor
interface. Similarly, the IUserAccessor
implementation must also implement the IImpersonator
interface.
As the default implementations in StartSharp do not support this, we had to wrap our implementations in Startup.cs
to enable this feature:
services.AddSingleton<IPermissionService>(services =>
new TransientGrantingPermissionService(
ActivatorUtilities.CreateInstance<AppServices.PermissionService>(services),
services.GetRequiredService<IHttpContextItemsAccessor>()));
services.AddSingleton<IUserAccessor>(services =>
new ImpersonatingUserAccessor(
ActivatorUtilities.CreateInstance<AppServices.UserAccessor>(services),
services.GetRequiredService<IHttpContextItemsAccessor>());
The new AppServices.PermissionService
already implements the ITransientGrantor
interface, so wrapping is no longer necessary:
public class PermissionService( /* ... */ ) : IPermissionService, ITransientGrantor {
}
It uses the TransientGrantingPermissionService
internally to easily implement the interface. Please see the latest code of PermissionService.cs
in the StartSharp repository.
Similarly, the default UserAccessor
implementation implements the IImpersonator
interface:
public class UserAccessor( /* ... */ ) : IUserAccessor, IImpersonator {
}
As transient granting/impersonation support might be necessary for some operations like reporting callbacks, it is recommended to apply this change to your permission and user accessor services, even if you don't directly use these features in your application.
Wrapping services in Startup.cs
might be necessary to add extra abilities to some services, like transient granting, impersonation, etc.
For example, to enable impersonation support for our IUserAccessor
service, we would need to make the following changes in Startup.cs
:
services.AddSingleton<IUserAccessor>(services =>
new ImpersonatingUserAccessor(
ActivatorUtilities.CreateInstance<AppServices.UserAccessor>(services), services.GetRequiredService<IHttpContextItemsAccessor>());
With the new AddSingletonWrapper
interface, it is much simpler:
services.AddSingletonWrapped<IUserAccessor, ImpersonatingUserAccessor, AppServices.UserAccessor>();
Note that in the current StartSharp version, the default IUserAccessor
implementation already has built-in impersonation support, so the above line won't be necessary. It is used in Startup.cs
for other wrapping, like OpenIddict.
To improve compatibility with jQuery (note that we don't target 100% compatibility) and make porting code easier, the following methods have been added:
Fluent.byId
: a shortcut forFluent(document.getElementById)
Fluent.each
andFluent.style
: execute a callback with the node or CSS style declaration if the element is not nullFluent.matches
: similar to$.is
but doesn't support any custom selectors and callselement.matches
Fluent.class
: directly sets theclassName
property, preferred toaddClass
for element creationFluent.isDefaultPrevented
: helps overcome issues when jQuery exists or not, checking bothe.defaultPrevented
ande.isDefaultPrevented?.()
Fluent.eventProp
: reads a property from the event,event.originalEvent
, orevent.detail
, as jQuery does not pass custom properties likeroute
to the synthetic eventFluent.getWidget
andFluent.tryGetWidget
: get a reference to a widget on the elementFluent.findEach
: executes a callback for each element found with a Fluent objectFluent.after
andFluent.before
: similar to jQuery methods
As jsx-dom
has recently fixed an issue (before version 8.1.4) where ref callbacks were not executed for React-style class components like Serenity Widgets, we will no longer call the ref callback from the Widget itself to avoid double execution. This might cause problems when using an older version of jsx-dom
. After updating to Serenity 8.4.4+, please update jsx-dom
in your package.json
to version 8.1.4+ and run npm install
.
Category links on top of forms, hidden since version 8.4.8, are now completely removed. They were generally confusing for end users and were not particularly useful unless the form was very long.
The following package references in StartSharp/Serene have been updated:
"@preact/signals": "1.2.3",
"jsvectormap": "1.6.0",
"jsx-dom": "8.1.4",
"moment": "2.30.1",
"preact": "10.22.0",
"chart.js": "4.4.3"
Although there are no known breaking changes, it is recommended to keep these packages in sync in your application.
The following package references in StartSharp/Serene have been updated:
<PackageReference Include="FluentMigrator.Runner" Version="5.2.0" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.7" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" />
<PackageReference Include="Microsoft.TypeScript.MSBuild" Version="5.5.3"
PrivateAssets="all" IncludeAssets="runtime; build; native; contentfiles; analyzers" />
There are other transitive package dependencies that are referenced by Serenity NuGet packages but they will be updated when you update Serenity packages, so the above ones are the ones you should manually update.
During publish you may sometimes get errors about files not being found, especially under "~/wwroot/esm/".
Those errors are usually caused by MSBuild trying to copy these files to publish directory while ESBuild clean plugin deleting them before the publish starts.
Please check latest StartSharp.Web.csproj
file for a workaround to such issues.
You should delete CreateTSCInputs target, replace CompileTSCBefore with RunTSBuild target and replace CompileTSCAfter with TranformRunTSBuild target.
SlickGrid adds some legacy jQuery UI classes like ui-state-default
and for compatibility we used to also add these classes with our SleekGrid.
The flag that controls this is now false
by default.
Serene users should add following line in ScriptInit.ts
as SlickGrid's default CSS still depends on those legacy classes.
gridDefaults.useCssVars = false;
gridDefaults.useLegacyUI = true; // add this line
StartSharp users don't need this as the premium theme does not use SlickGrid's default css.
Once we update the common style and slick.grid.css files in Serenity.Assets, Serene users may remove that line.