diff --git a/Lombiq.TrainingDemo.Web/Lombiq.TrainingDemo.Web.csproj b/Lombiq.TrainingDemo.Web/Lombiq.TrainingDemo.Web.csproj
index 226153af..49ece610 100644
--- a/Lombiq.TrainingDemo.Web/Lombiq.TrainingDemo.Web.csproj
+++ b/Lombiq.TrainingDemo.Web/Lombiq.TrainingDemo.Web.csproj
@@ -18,11 +18,11 @@
-
-
+
+
-
+
diff --git a/Lombiq.TrainingDemo/Drivers/BookDisplayDriver.cs b/Lombiq.TrainingDemo/Drivers/BookDisplayDriver.cs
index 71745293..1791d8c2 100644
--- a/Lombiq.TrainingDemo/Drivers/BookDisplayDriver.cs
+++ b/Lombiq.TrainingDemo/Drivers/BookDisplayDriver.cs
@@ -20,7 +20,7 @@ public class BookDisplayDriver : DisplayDriver
"StyleCop.CSharp.ReadabilityRules",
"SA1114:Parameter list should follow declaration",
Justification = "Necessary for comments.")]
- public override IDisplayResult Display(Book model) =>
+ public override IDisplayResult Display(Book model, BuildDisplayContext context) =>
// For the sake of demonstration we use Combined() here. It makes it possible to return multiple shapes from a
// driver method - won't necessarily be displayed all at once!
Combine(
diff --git a/Lombiq.TrainingDemo/Drivers/ColorFieldDisplayDriver.cs b/Lombiq.TrainingDemo/Drivers/ColorFieldDisplayDriver.cs
index 7906ef7d..2f4532ad 100644
--- a/Lombiq.TrainingDemo/Drivers/ColorFieldDisplayDriver.cs
+++ b/Lombiq.TrainingDemo/Drivers/ColorFieldDisplayDriver.cs
@@ -5,7 +5,6 @@
using OrchardCore.ContentManagement.Display.ContentDisplay;
using OrchardCore.ContentManagement.Display.Models;
using OrchardCore.ContentManagement.Metadata.Models;
-using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.DisplayManagement.Views;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -56,31 +55,31 @@ public override IDisplayResult Edit(ColorField field, BuildFieldEditorContext co
// NEXT STATION: Settings/ColorFieldSettings
- public override async Task UpdateAsync(ColorField field, IUpdateModel updater, UpdateFieldEditorContext context)
+ public override async Task UpdateAsync(ColorField field, UpdateFieldEditorContext context)
{
+ var updater = context.Updater;
var viewModel = new EditColorFieldViewModel();
// Using this overload of the model updater you can specifically say what properties need to be updated. This
// way you make sure no other properties will be bound to the view model. Instead of this you could put
// [BindNever] attributes on the properties to make the model binder to skip those, it's up to you.
- if (await updater.TryUpdateModelAsync(viewModel, Prefix, f => f.Value, f => f.ColorName))
+ await updater.TryUpdateModelAsync(viewModel, Prefix, viewModel => viewModel.Value, viewModel => viewModel.ColorName);
+
+ // Get the ColorFieldSettings to use it when validating the view model.
+ var settings = context.PartFieldDefinition.GetSettings();
+ if (settings.Required && string.IsNullOrWhiteSpace(viewModel.Value))
{
- // Get the ColorFieldSettings to use it when validating the view model.
- var settings = context.PartFieldDefinition.GetSettings();
- if (settings.Required && string.IsNullOrWhiteSpace(viewModel.Value))
- {
- updater.ModelState.AddModelError(Prefix, T["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]);
- }
+ updater.ModelState.AddModelError(Prefix, T["A value is required for {0}.", context.PartFieldDefinition.DisplayName()]);
+ }
- // Also some custom validation for our ColorField hex value.
- var isInvalidHexColor = !string.IsNullOrWhiteSpace(viewModel.Value) &&
- !RegexExpression().IsMatch(viewModel.Value);
+ // Also some custom validation for our ColorField hex value.
+ var isInvalidHexColor = !string.IsNullOrWhiteSpace(viewModel.Value) &&
+ !RegexExpression().IsMatch(viewModel.Value);
- if (isInvalidHexColor) updater.ModelState.AddModelError(Prefix, T["The given color is invalid."]);
+ if (isInvalidHexColor) updater.ModelState.AddModelError(Prefix, T["The given color is invalid."]);
- field.ColorName = viewModel.ColorName;
- field.Value = viewModel.Value;
- }
+ field.ColorName = viewModel.ColorName;
+ field.Value = viewModel.Value;
return await EditAsync(field, context);
}
diff --git a/Lombiq.TrainingDemo/Drivers/DemoSettingsDisplayDriver.cs b/Lombiq.TrainingDemo/Drivers/DemoSettingsDisplayDriver.cs
index b9eb9c4f..e978970c 100644
--- a/Lombiq.TrainingDemo/Drivers/DemoSettingsDisplayDriver.cs
+++ b/Lombiq.TrainingDemo/Drivers/DemoSettingsDisplayDriver.cs
@@ -14,13 +14,17 @@ namespace Lombiq.TrainingDemo.Drivers;
// Now this display driver abstraction is different from the one you've seen before. In Orchard Core you can connect
// different objects to a master object that will be connected in the database when storing them. Site settings are
// handled this way.
-public class DemoSettingsDisplayDriver : SectionDisplayDriver
+public class DemoSettingsDisplayDriver : SiteDisplayDriver
{
// Since technically we have only one SiteSettings we have separate the editors using editor groups. It's a good
// idea to store the editor group ID in a publicly accessibly constant (would be much better to store it in a static
// class placed in a Constants folder).
public const string EditorGroupId = "Demo";
+ // This abstract property of SiteDisplayDriver has to be overridden. It is used to filter the editor by group name
+ // under the hood, so you don't have to check it manually.
+ protected override string SettingsGroupId => EditorGroupId;
+
private readonly IAuthorizationService _authorizationService;
private readonly IHttpContextAccessor _hca;
@@ -32,10 +36,10 @@ public DemoSettingsDisplayDriver(IAuthorizationService authorizationService, IHt
// Here's the EditAsync override to display editor for our site settings on the Dashboard. Note that it has a sync
// version too.
- public override async Task EditAsync(DemoSettings section, BuildEditorContext context)
+ public override async Task EditAsync(ISite model, DemoSettings section, BuildEditorContext context)
{
- // What you really don't want to is to let unauthorized users update site-level settings of your site so it's
- // really advisable to create a separate permission for managing the settings or the feature related to this
+ // What you really don't want to is to let unauthorized users update site-level settings of your site. So it's
+ // really advisable to create a separate permission for managing the settings or the feature related to these
// settings and use it here. We've created one that you can see in the Permissions/DemoSettingsPermissions.cs
// file.
if (!await IsAuthorizedToManageDemoSettingsAsync())
@@ -54,7 +58,7 @@ public override async Task EditAsync(DemoSettings section, Build
.OnGroup(EditorGroupId);
}
- public override async Task UpdateAsync(DemoSettings section, UpdateEditorContext context)
+ public override async Task UpdateAsync(ISite model, DemoSettings section, UpdateEditorContext context)
{
// Since this DisplayDriver is for the ISite object this UpdateAsync will be called every time if a site
// settings editor is being updated. To make sure that this is for our editor group check it here.
@@ -67,14 +71,11 @@ public override async Task UpdateAsync(DemoSettings section, Upd
}
// Update the view model and the settings model as usual.
- var viewModel = new DemoSettingsViewModel();
-
- await context.Updater.TryUpdateModelAsync(viewModel, Prefix);
-
+ var viewModel = await context.CreateModelAsync(Prefix);
section.Message = viewModel.Message;
}
- return await EditAsync(section, context);
+ return await EditAsync(model, section, context);
}
private async Task IsAuthorizedToManageDemoSettingsAsync()
diff --git a/Lombiq.TrainingDemo/Drivers/PersonPartDisplayDriver.cs b/Lombiq.TrainingDemo/Drivers/PersonPartDisplayDriver.cs
index e41aa70b..28b6885a 100644
--- a/Lombiq.TrainingDemo/Drivers/PersonPartDisplayDriver.cs
+++ b/Lombiq.TrainingDemo/Drivers/PersonPartDisplayDriver.cs
@@ -2,7 +2,7 @@
using Lombiq.TrainingDemo.ViewModels;
using OrchardCore.ContentManagement.Display.ContentDisplay;
using OrchardCore.ContentManagement.Display.Models;
-using OrchardCore.DisplayManagement.ModelBinding;
+using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;
using System.Threading.Tasks;
@@ -27,7 +27,7 @@ public class PersonPartDisplayDriver : ContentPartDisplayDriver
public override IDisplayResult Display(PersonPart part, BuildPartDisplayContext context) =>
// Here you have a shape helper with a shape name possibly and a factory. The Initialize method will instantiate
// a view model from a type given as a generic parameter. It's recommended to use view models for the views like
- // we're doing it here (sometimes you'd want a separate view model for the Display() and Edit(). There are
+ // we're doing it here (sometimes you'd want a separate view model for the Display() and Edit()). There are
// helper methods to generate the shape type. GetDisplayShapeType() in this case will generate "PersonPart" by
// default but this can be overridden form the part's settings under the content type's settings on the admin.
// In the factory we map the content part properties to the view model; if there is any heavy lifting needed to
@@ -73,16 +73,14 @@ public override IDisplayResult Edit(PersonPart part, BuildPartEditorContext cont
// So we had an Edit (or EditAsync) method that generates the editor shape. Now it's time to do the content
// part-specific model binding and validation.
- public override async Task UpdateAsync(PersonPart part, IUpdateModel updater, UpdatePartEditorContext context)
+ public override async Task UpdateAsync(PersonPart part, UpdatePartEditorContext context)
{
- var viewModel = new PersonPartViewModel();
-
// Via the IUpdateModel you will be able to use the current controller's model binding helpers here in the
// driver. The prefix property will be used to distinguish between similarly named input fields when building
// the editor form (so e.g. two content parts composing a content item can have an input field called "Name").
- // By default Orchard Core will use the content part name but if you have multiple drivers with editors for a
+ // By default, Orchard Core will use the content part name but if you have multiple drivers with editors for a
// content part you need to override it in the driver.
- await updater.TryUpdateModelAsync(viewModel, Prefix);
+ var viewModel = await context.CreateModelAsync(Prefix);
// Now you can do some validation if needed. One way to do it you can simply write your own validation here or
// you can do it in the view model class.
@@ -90,7 +88,7 @@ public override async Task UpdateAsync(PersonPart part, IUpdateM
// Go and check the ViewModels/PersonPartViewModel to see how to do it and then come back here.
// Finally map the view model to the content part. By default, these changes won't be persisted if there was a
- // validation error. Otherwise these will be automatically stored in the database.
+ // validation error. Otherwise, these will be automatically stored in the database.
part.BirthDateUtc = viewModel.BirthDateUtc;
part.Name = viewModel.Name;
part.Handedness = viewModel.Handedness;
diff --git a/Lombiq.TrainingDemo/Lombiq.TrainingDemo.csproj b/Lombiq.TrainingDemo/Lombiq.TrainingDemo.csproj
index ade1d604..1e043ca8 100644
--- a/Lombiq.TrainingDemo/Lombiq.TrainingDemo.csproj
+++ b/Lombiq.TrainingDemo/Lombiq.TrainingDemo.csproj
@@ -22,24 +22,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+