Skip to content

Commit 1fd2f78

Browse files
committed
Rewriting code samples for custom property editor support in headless for a modern approach that replaces IPublishedSnapshotAccessor
1 parent f629495 commit 1fd2f78

File tree

2 files changed

+156
-162
lines changed

2 files changed

+156
-162
lines changed
Lines changed: 78 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,31 @@
11
---
22
description: >-
3-
Discover how to customize the Content Delivery API's response for your custom
4-
property editors.
3+
Discover how to customize the Content Delivery API's response for custom property editors.
54
---
65

76
# Custom property editors support
87

9-
Out of the box, the Delivery API supports custom property editors, ensuring they are rendered alongside the built-in ones in Umbraco. However, if the output generated by your property editor isn't optimal for a headless context, you have the ability to customize the API response. This customization won't impact the Razor rendering, allowing you to tailor the Content Delivery API response according to your specific requirements.
8+
Out of the box, the Delivery API supports custom property editors, ensuring they are rendered alongside the built-in ones in Umbraco. However, if the output generated by a property editor isn't optimal for a headless context, developers can customize the API response. This customization won't impact the Razor rendering, allowing developers to tailor the Content Delivery API response according to their specific requirements.
109

11-
In this article, we'll look into how you can work with the `IDeliveryApiPropertyValueConverter` interface and implement custom [property expansion](./property-expansion-and-limiting.md) for your custom property editors.
10+
This article will demonstrate how to work with the `IDeliveryApiPropertyValueConverter` interface and implement custom [property expansion](./property-expansion-and-limiting.md) for custom property editors.
1211

1312
## Prerequisite
1413

1514
The examples in this article revolve around the fictional `My.Custom.Picker` property editor. This property editor stores the key of a single content item and is backed by a property value converter.
1615

17-
We will not dive into the details of creating a custom property editor for Umbraco in this article. If you need guidance on that, please refer to the [Creating a Property Editor](../../tutorials/creating-a-property-editor/README.md) and [Property Value Converters](../../customizing/property-editors/property-value-converters.md) articles.
16+
This article will not dive into the details of creating a custom property editor for Umbraco. Guidance on that can be found in these articles: [Creating a Property Editor](../../tutorials/creating-a-property-editor/README.md) and [Property Value Converters](../../customizing/property-editors/property-value-converters.md).
1817

1918
## Implementation
2019

21-
To customize the output of a property value editor in the Delivery API, we need to opt-in by implementing the `IDeliveryApiPropertyValueConverter` interface.
20+
To customize the output of a property value editor in the Delivery API, opt-in by implementing the `IDeliveryApiPropertyValueConverter` interface.
2221

23-
The code example below showcases the implementation of this interface in the property value converter for `My.Custom.Picker`. Our focus will be on the methods provided by the `IDeliveryApiPropertyValueConverter`, as they are responsible for customizing the Delivery API response.
22+
The code example below showcases the implementation of this interface in the property value converter for `My.Custom.Picker`. The focus will be on customizing the methods provided by the `IDeliveryApiPropertyValueConverter`, as they are responsible for customizing the Delivery API response.
2423

25-
Towards the end of the example, you will find the response models that we will be using.
24+
Response model classes can be found near the end of this article.
2625

2726
The `IsConverter()` and `GetPropertyValueType()` methods are inherited from the `PropertyValueConverterBase` class, which is covered in the [Property Value Converters](../../customizing/property-editors/property-value-converters.md) article.
2827

29-
{% include "../../.gitbook/includes/obsolete-warning-snapshot.md" %}
30-
3128
{% code title="MyCustomPickerValueConverter.cs" lineNumbers="true" %}
32-
3329
```csharp
3430
using Umbraco.Cms.Core.DeliveryApi;
3531
using Umbraco.Cms.Core.Models.DeliveryApi;
@@ -38,21 +34,11 @@ using Umbraco.Cms.Core.PropertyEditors;
3834
using Umbraco.Cms.Core.PropertyEditors.DeliveryApi;
3935
using Umbraco.Cms.Core.PublishedCache;
4036

41-
namespace Umbraco.Docs.Samples;
42-
43-
public class MyCustomPickerValueConverter : PropertyValueConverterBase, IDeliveryApiPropertyValueConverter
37+
public class MyCustomPickerValueConverter(
38+
IPublishedContentCache publishedContentCache,
39+
IApiContentRouteBuilder apiContentRouteBuilder):
40+
PropertyValueConverterBase, IDeliveryApiPropertyValueConverter
4441
{
45-
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
46-
private readonly IApiContentRouteBuilder _apiContentRouteBuilder;
47-
48-
public MyCustomPickerValueConverter(
49-
IPublishedSnapshotAccessor publishedSnapshotAccessor,
50-
IApiContentRouteBuilder apiContentRouteBuilder)
51-
{
52-
_publishedSnapshotAccessor = publishedSnapshotAccessor;
53-
_apiContentRouteBuilder = apiContentRouteBuilder;
54-
}
55-
5642
public override bool IsConverter(IPublishedPropertyType propertyType)
5743
=> propertyType.EditorAlias.Equals("My.Custom.Picker");
5844

@@ -86,38 +72,30 @@ public class MyCustomPickerValueConverter : PropertyValueConverterBase, IDeliver
8672

8773
private DeliveryApiCustomPicker? BuildDeliveryApiCustomPicker(object inter, bool expanding)
8874
{
89-
if (!_publishedSnapshotAccessor.TryGetPublishedSnapshot(out IPublishedSnapshot? publishedSnapshot) ||
90-
publishedSnapshot?.Content is null)
75+
if (!Guid.TryParse(inter as string, out Guid id))
9176
{
9277
return null;
9378
}
79+
80+
var content = publishedContentCache.GetById(id);
9481

95-
if (!Guid.TryParse(inter as string, out Guid id))
82+
if (content is null)
9683
{
9784
return null;
9885
}
99-
86+
10087
return new DeliveryApiCustomPicker { Id = id };
10188
}
10289
}
103-
104-
public class DeliveryApiCustomPicker
105-
{
106-
public Guid Id { get; set; }
107-
108-
public DeliveryApiItemDetails? ItemDetails { get; set; }
109-
}
110-
111-
public class DeliveryApiItemDetails
112-
{
113-
public string? Name { get; set; }
114-
115-
public IApiContentRoute? Route { get; set; }
116-
}
11790
```
118-
11991
{% endcode %}
12092

93+
{% hint style="info" %}
94+
Umbraco developers will need to inject `IPublishedContentCache`, `IPublishedMediaCache`, `IPublishedMemberCache` and `IPublishedContentTypeCache` dependencies individually, instead of injecting the `IPublishedSnapshotAccessor` as would have been done previously.
95+
{% endhint %}
96+
97+
{% include "../../.gitbook/includes/obsolete-warning-snapshot.md" %}
98+
12199
The Implementation of the `IDeliveryApiPropertyValueConverter` interface can be found in the following methods:
122100

123101
* `GetDeliveryApiPropertyCacheLevel()`: This method specifies the cache level used for our property representation in the Delivery API response.
@@ -129,14 +107,12 @@ In the given example, the content key (`Guid` value) is used when rendering with
129107

130108
The following example request shows how our custom implementation is reflected in the resulting API response. In this case, our custom property editor is configured under the alias `"pickedItem"`.
131109

132-
**Request**
133-
110+
**Sample Request**
134111
```http
135112
GET /umbraco/delivery/api/v2/content/item/blog
136113
```
137114

138-
**Response**
139-
115+
**Sample Response**
140116
```json
141117
{
142118
"name": "Blog",
@@ -163,57 +139,60 @@ GET /umbraco/delivery/api/v2/content/item/blog
163139

164140
## Property expansion support
165141

166-
Property expansion allows us to conditionally add another level of detail to the Delivery API output. Usually, these additional details are "expensive" to retrieve (for example, requiring database access to populate). By applying property expansion, we provide the option for the caller of the API to opt-in explicitly to this "expensive" operation. From the caller's perspective, the alternative might be an even more expensive additional round-trip to the server.
142+
Property expansion allows developers to conditionally add another level of detail to the Delivery API output. Usually, these additional details are "expensive" to retrieve (for example, requiring database access to populate). By applying property expansion, Umbraco developers provide the option for the caller of the API to opt-in explicitly to this "expensive" operation. From the caller's perspective, the alternative might be an even more expensive additional round-trip to the server.
167143

168-
In our example, property expansion is implemented within `ConvertIntermediateToDeliveryApiObject()`. By considering the value of the `expanding` parameter, we can modify the `BuildDeliveryApiCustomPicker()` method as follows:
169-
170-
{% include "../../.gitbook/includes/obsolete-warning-ipublishedsnapshotaccessor.md" %}
144+
In this example, property expansion is implemented within `ConvertIntermediateToDeliveryApiObject()`. By considering the value of the `expanding` parameter, the `BuildDeliveryApiCustomPicker()` method can be modified as follows:
171145

146+
{% code title="MyCustomPickerValueConverter.cs" lineNumbers="true" %}
172147
```csharp
173-
private DeliveryApiCustomPicker? BuildDeliveryApiCustomPicker(object inter, bool expanding)
148+
public class MyCustomPickerValueConverter(
149+
IPublishedContentCache publishedContentCache,
150+
IApiContentRouteBuilder apiContentRouteBuilder):
151+
PropertyValueConverterBase, IDeliveryApiPropertyValueConverter
174152
{
175-
if (!_publishedSnapshotAccessor.TryGetPublishedSnapshot(out IPublishedSnapshot? publishedSnapshot) ||
176-
publishedSnapshot?.Content is null)
177-
{
178-
return null;
179-
}
180-
181-
if (!Guid.TryParse(inter as string, out Guid id))
153+
// ...
154+
private DeliveryApiCustomPicker? BuildDeliveryApiCustomPicker(object inter, bool expanding)
182155
{
183-
return null;
184-
}
156+
if (!Guid.TryParse(inter as string, out Guid id))
157+
{
158+
return null;
159+
}
185160

186-
// Property expansion support
187-
if (expanding == false)
188-
{
189-
return new DeliveryApiCustomPicker { Id = id };
190-
}
161+
// Property expansion support
162+
if (!expanding)
163+
{
164+
return new DeliveryApiCustomPicker { Id = id };
165+
}
191166

192-
IPublishedContent? content = publishedSnapshot.Content.GetById(id);
193-
if (content is null)
194-
{
195-
return new DeliveryApiCustomPicker { Id = id };
196-
}
167+
var content = publishedContentCache.GetById(id);
168+
if (content is null)
169+
{
170+
return new DeliveryApiCustomPicker { Id = id };
171+
}
197172

198-
IApiContentRoute? route = _apiContentRouteBuilder.Build(content);
199-
var itemDetails = new DeliveryApiItemDetails { Name = content.Name, Route = route };
173+
var route = apiContentRouteBuilder.Build(content);
174+
var itemDetails = new DeliveryApiItemDetails
175+
{
176+
Name = content.Name,
177+
Route = route
178+
};
200179

201-
return new DeliveryApiCustomPicker { Id = id, ItemDetails = itemDetails };
180+
return new DeliveryApiCustomPicker { Id = id, ItemDetails = itemDetails };
181+
}
202182
}
203183
```
184+
{% endcode %}
204185

205-
If the `expanding` parameter is `false`, the method returns the same shallow representation of the referenced content item as before. Otherwise, we retrieve the corresponding `IPublishedContent` and construct our response object accordingly.
206-
207-
To see the expanded output in the API response, we need to add the `expand` query parameter to our request. We can use either `?expand=properties[$all]` to expand all properties or `?expand=properties[pickedItem]` to expand the specific `'pickedItem'` property.
186+
If the `expanding` parameter is `false`, the method returns the same shallow representation of the referenced content item as before. Otherwise, retrieve the corresponding `IPublishedContent` and construct the response object accordingly.
208187

209-
**Request**
188+
To see the expanded output in the API response, add the `expand` query parameter to the request. This parameter can be applied using either `?expand=properties[$all]` to expand all properties or `?expand=properties[pickedItem]` to expand the specific `'pickedItem'` property.
210189

190+
**Sample Request**
211191
```http
212192
GET /umbraco/delivery/api/v2/content/item/blog?expand=properties[pickedItem]
213193
```
214194

215-
**Response**
216-
195+
**Sample Response**
217196
```json
218197
{
219198
"name": "Blog",
@@ -250,3 +229,21 @@ GET /umbraco/delivery/api/v2/content/item/blog?expand=properties[pickedItem]
250229
```
251230

252231
The `itemDetails` property of the `pickedItem` in the JSON response contains the additional details of the selected content item.
232+
233+
## Supporting Model Classes
234+
235+
{% code title="MyCustomPickerValueConverter.cs" %}
236+
```csharp
237+
public class DeliveryApiCustomPicker
238+
{
239+
public Guid Id { get; set; }
240+
public DeliveryApiItemDetails? ItemDetails { get; set; }
241+
}
242+
243+
public class DeliveryApiItemDetails
244+
{
245+
public string? Name { get; set; }
246+
public IApiContentRoute? Route { get; set; }
247+
}
248+
```
249+
{% endcode %}

0 commit comments

Comments
 (0)