This package aims to create a parity for widget designer generation similar to the autogenerated widget designers in Sitefinity. Due to some limitations in Typescript and Javascript, there are several cases where additional data must be provided (see usage guide). Full API documentation could be found in the api.md in the source.
via NPM:
npm i @progress/sitefinity-widget-designers-sdk --save
via yarn:
yarn add @progress/sitefinity-widget-designers-sdk
The package relies on the Typescript decorators, which requires adding support for decorators to your tsconfig.json file:
{
"compilerOptions": {
...
"experimentalDecorators": true
}
}
The metadata, generated from this package, aims to equal the metadata generated by Sitefinity or the ASP.NET Core external renderer and replace the usage of JSON metadata files in external renderers.
import { EntityMetadataGenerator, MetadataModel } from "@progress/sitefinity-widget-designers-sdk";
const designerMetadata: MetadataModel = EntityMetadataGenerator.extractMetadata(widgetEntity);
The the metadata generator will go through the designer JSON definition, extract and parse the default value for each property is such is set. Normally the return type would match the type definition of the widget entity model.
import { EntityMetadataGenerator } from "@progress/sitefinity-widget-designers-sdk";
const defaultValues: { [key: string]: any } = EntityMetadataGenerator.extractDefaultValues(designerMetadata);
Sitefinity preserves the widget data on the server in stringified JSON form so when it is received, a parsing depending on the property type is necessary to match the entity model properties' types.
import { EntityMetadataGenerator } from "@progress/sitefinity-widget-designers-sdk";
const deserializedValues: { [key: string]: any } = EntityMetadataGenerator.parseValues(valuesFromServerCall, designerMetadata);
The widget model should be decorated using the WidgetEntity decorator.
import { WidgetEntity } from "@progress/sitefinity-widget-designers-sdk";
@WidgetEntity("SitefinitySection", "Section")
export class SectionEntity {
@Category('QuickEdit')
CssSystemGridSize: number = 12;
}
Properties that have complex models as types need their data models explicitly specified (while in C# these types and their subproperties would be generated OOB). For more details on complex objects see here
import { Model, WidgetEntity} from "@progress/sitefinity-widget-designers-sdk";
@WidgetEntity("ComplexWidget", "Complex Widget")
class EntityWithComplexProperties {
@DisplayName("Complex property")
@DataModel(ComplexPropModel)
ComplexProperty?: ComplexPropModel;
}
@Model()
class ComplexPropModel {
@DataType("string")
ChildString: string | null = null;
}
is the same as in C#
public class EntityWithComplexProperties {
[DisplayName("Complex property")]
public ComplexPropModel ComplexProperty { get; set; }
}
public class ComplexPropModel {
public string ChildString { get; set; }
}
The default value of the properties would be taken either from the DefaultValue decorator or a default value set to the property in the class declaration:
@DisplayName("Number prop")
@DefaultValue(42)
@DataType("number")
NumberProperty: number;
// DeafultValue="42" Type="number"
If a default value is set to basic types, the DataType decorator can be omitted. The following code would produce the same output:
@DisplayName("Number prop")
NumberProperty: number = 42;
// DeafultValue="42" Type="number"
Sections can be arraged either manually or via the SectionsOrder class decorator.
Properties can be arranged in sections in the autogenerated designers and ordered by a given index via the ContentSection decorator.
Sections can be separed in categories: Basic, Advanced, QuickEdit. Setting the category to the property will move the property there and create a section for it in that category.
Properties can be given several types of description via the Description and DescriptionExtended decorators.
@WidgetEntity('CustomEntity', 'Custom entity')
@SectionsOrder('First section', 'Second section')
class Entity {
@ContentSection('Second section', 0)
SecondSectionFirstProperty: any;
@ContentSection('Second section', 1)
SecondSectionSecondProperty: any;
@ContentSection('First section', 0)
@Description('simple description')
FirstSectionFirstProperty: any;
@Category('Advanced')
@DescriptionExtended({
Description: '...',
InlineDescription: '...',
InstructionalNotes: '...'
})
FirstPropertyAdvancedCategoryNewSection: any;
}
The property editor field is defined via the DataType decorator. Besides the basic types such as string, number, bool/boolean, there are some predefined types in the KnownFieldTypes enum as well as the several variations in the ComplexType enum. Types that are a variation of choices should have a Choice decorator defined as well.
@DataType('string')
StringProperty: string | null = null;
// @DataType('string') for basic types the decorator can be omitted if there is a default value of the same type set
StringProperty: string = 'default value';
@DataType(KnownFieldTypes.Choices)
@Choice([{Title: 'First choice', Value: 'first'}, {Title: 'Second choice', Value: 'second'}])
ChoiceProperty: string | null = null;
// checkbox
@DataType(KnownFieldTypes.CheckBox)
YesNoProperty: boolean = false;
// chip choice
@DataType(KnownFieldTypes.ChipChoice)
@Choice({Choices: [
{Title:'Yes', Name: 'Yes', Value: 'True', Icon: null},
{Title:'No', Name: 'No', Value: false, Icon: null}
]})
ChipChoiceSingle: boolean = true;
Property values can be validated on the front end based on decorators set in the entity. Most of them can receive and custom validation failed error message.
- Required
- Readonly
- Range
- MinLength
- MaxLength
-
RegularExpression:
- Url (predefined regexp)
- EmailAddress (predefined regexp)
- DecimalPlaces (for floating point numbers)
- StringLength
A property can refer to a content item of any of the types that are found or created in Sitefinity. The autogenerated designer will display a content selector for that content type. The full type name should be used - e.g. for pages: Telerik.Sitefinity.Pages.Model.PageNode
@Content({
Type: 'Telerik.Sitefinity.Pages.Model.PageNode',
AllowMultipleItemsSelection: false
})
SelectedPage: MixedContentContext = null;
// Media item is shorthand for Content
@MediaItem('images', true)
SelectedMedia: MixedContentContext = null;
@Content({
Type: 'Telerik.Sitefinity.Libraries.Model.Image',
AllowMultipleItemsSelection: false
})
FullDecoratorSelectedMedia: MixedContentContext = null;
@TaxonomyContent({Type: 'Taxonomy_Tags'})
SelectedTags: MixedContentContext = null
// or
@TaxonomyContent({Type: 'Tags'})
ShorthandSelectedTags: MixedContentContext = null
Fields can be shown or hidden based on other property values using the ConditionalVisibility decorator.
FirstProperty: string = '';
@ConditionalVisibility({
conditions: [{
fieldName: 'FirstProperty',
operator: 'Equals',
value: 'show second property'
}]
})
SecondProperty: string = '';
In the autogenerated designers in Sitefinity there are several ways to define and to represent properties with complex object types.
@DataModel(PropModel)
// @DataType(ComplexType.Complex)
ComplexObjProp: PropModel = null; // => renders a section with the model
@DataModel(PropModel)
// @DataType(ComplexType.Complex)
@TableView("TableTitle")
ComplextTableProp: PropModel = null; // => renders the model as a table
@DataModel(PropModel)
@DataType(ComplexType.Enumerable)
MultipleComplexObj: PropModel[] = null; // renders editable rows for each instance of the model
@DataModel(PropModel)
// @DataType(ComplexType.Enumerable)
@TableView({ Reorderable: true, Selectable: true, MultipleSelect: true })
MultipleComplexObj: PropModel[] = null; // renders full editable table with a row for each instance of the model
...
@Model()
class PropModel {
stringProp: string = "default-value";
numberProp: number = 13;
...
}
Having a properties of collection types, it should be explicitly specified:
Currently arrays are supported for objects/classes and string types. Other types would be rendered as a simple string field.
import { ComplexType, DataModel, DataType } from "@progress/sitefinity-widget-designers-sdk";
// would receive Type="enumerable"
@DataType(ComplexType.Enumerable, "string")
ValidStringCollection: string[] = null;
// would receive Type="enumerable"
@DataModel("string")
@DataType(ComplexType.Enumerable)
AlsoValidStringCollection: string[] = null;
// would receive Type="enumerable"
// with TypeChildProperties of the DataModel
@DataModel(ObjectModel)
@DataType(ComplexType.Enumerable)
ObjectCollection: ObjectModel[] = null
// invalid properties
// would receive Type=null
@DataType(ComplexType.Enumerable)
InvalidCollection: boolean[] | number[] = null;
Currently a collection of type dictionary is supported only when LengthDependsOn property decorator is also present and the value type is an object/class. Otherwise the field would be rendered as a simple string field. For collections of objects that don't depend on another property, we suggest using an array.
import { ComplexType, DataType, Model } from "@progress/sitefinity-widget-designers-sdk";
@DataModel(DictValue)
@DataType(ComplexType.Dictionary)
@LengthDependsOn("PropName", "PropDisplayName", "PropTitle")
ObjectDictionary: { [key: string]: DictValue } = null;
....
@Model()
class DictValue {
@DataType("number")
Int: number;
@DataType("string")
Str: string;
}