sitefinitysitefinity-feather

Sitefinity feather multiple content items selectors exception in designer


I am using the sf-list-selector in my designer as shown below. I see my products list and I can select and sort.

<sf-list-selector sf-dynamic-items-selector sf-provider="properties.ProductProviderName.PropertyValue" sf-item-type="properties.ProductType.PropertyValue" sf-multiselect="true" sf-sortable="true" sf-master="true" sf-selected-ids="properties.ProductIds.PropertyValue" />

However I am getting an exception in the log file when I press save in the designer:

Requested URL : https://localhost/Sitefinity/Services/Pages/ControlPropertyService.svc/batch/fc82280c-3055-6fae-9336-ff0000e88380/?pageId=230b270c-3055-6fae-9336-ff0000e88380&mediaType=0&propertyLocalization=0 Inner Exception --------------- Type : System.Xml.XmlException, System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Message : End element 'PropertyValue' from namespace '' expected. Found element 'item' from namespace ''. Source : System.Runtime.Serialization Help link : LineNumber : 0 LinePosition : 0 SourceUri : Data : System.Collections.ListDictionaryInternal TargetSite : Void ThrowXmlException(System.Xml.XmlDictionaryReader, System.String, System.String, System.String, System.String) HResult : -2146232000 Stack Trace : at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3) at System.Xml.XmlExceptionHelper.ThrowEndElementExpected(XmlDictionaryReader reader, String localName, String ns) at System.Xml.XmlBaseReader.ReadEndElement() at System.Xml.XmlBaseReader.ReadElementContentAsString() at ReadWcfControlPropertyFromJson(XmlReaderDelegator , XmlObjectSerializerReadContextComplexJson , XmlDictionaryString , XmlDictionaryString[] ) at System.Runtime.Serialization.Json.JsonClassDataContract.ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson context) at System.Runtime.Serialization.Json.XmlObjectSerializerReadContextComplexJson.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract) at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, String name, String ns) at ReadArrayOfWcfControlPropertyFromJson(XmlReaderDelegator , XmlObjectSerializerReadContextComplexJson , XmlDictionaryString , XmlDictionaryString , CollectionDataContract ) at System.Runtime.Serialization.Json.JsonCollectionDataContract.ReadJsonValueCore(XmlReaderDelegator jsonReader, XmlObjectSerializerReadContextComplexJson context) at System.Runtime.Serialization.Json.XmlObjectSerializerReadContextComplexJson.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader) at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract& dataContract) at System.Runtime.Serialization.XmlObjectSerializerReadContextComplex.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns) at System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName) at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)

I don't have a JSON or JS file for the view. When I use the variation of this for single item select all works good.


Solution

  • It turns out the value for the sf-selected-ids attribute needs to be in JSON array format. e.g. [ productId1, productId2, productId3]. Otherwise the backend service throws that exception. However, the selector by itself creates the string as product1, product2, product3. I.e. without the brackets. (You can see this in the advanced view of the designer).

    So here are the detailed steps:

    Here is the selector in the designer view (DesignerView.Simple.cshtml):

    <sf-list-selector sf-dynamic-items-selector sf-provider="properties.ProductProviderName.PropertyValue" sf-item-type="properties.ProductType.PropertyValue" sf-multiselect="true" sf-sortable="true" sf-master="true" sf-selected-ids="productIds" />
    

    You'll need the designer JS file to do the JSON conversion back and forth. So I save this in the MVC/Scripts/[WidgetName]/designer-simple.json: (Simple is the name of the designer view)

    (function ($) {
    
        var designerModule = angular.module('designer');
        angular.module('designer').requires.push('sfSelectors');
    
        designerModule.controller('SimpleCtrl', ['$scope', 'propertyService', function ($scope, propertyService) {
    
            $scope.feedback.showLoadingIndicator = true;
            propertyService.get().then(function (data) {
                if (data) {
                    $scope.properties = propertyService.toAssociativeArray(data.Items);
                }
            },
            function (data) {
                $scope.feedback.showError = true;
                if (data)
                    $scope.feedback.errorMessage = data.Detail;
            }).finally(function () {
                $scope.feedback.showLoadingIndicator = false;
            });
    
            $scope.$watch('properties.ProductIds.PropertyValue', function (newValue, oldValue) {
                if (newValue) {               
                    $scope.productIds = JSON.parse(newValue);
                }
            });
    
            $scope.$watch('productIds', function (newValue, oldValue) {
                if (newValue) {               
                    $scope.properties.ProductIds.PropertyValue = JSON.stringify(newValue);
                }
            });
    
        }]);
    
    })(jQuery);
    

    Lastly I added a DesignerView.Simple.json file in the same folder as DesignerView.Simple.cshtml:

    {
        "priority": 1,
        "scripts": [     
            "client-components/selectors/common/sf-selected-items-view.js"
        ],
        "components" : ["sf-dynamic-items-selector"]
    }
    

    The widget controller has a ProductIds property. Its values will be in format [productId1, productId2, etc.]. I used a JSON deserializer to get an array of products for the controller Index action:

    public class ProductListController : Controller
       {
           private string productProviderName = WebConfigurationManager.AppSettings["productProviderName"];
           private string productTypeName = WebConfigurationManager.AppSettings["productTypeName"];
    
           public string ProductIds { get; set; }
    
           public string ProductType
           {
               get { return productTypeName; }
               set { productTypeName = value; }
           }
    
           public string ProductProviderName
           {
               get { return productProviderName; }
               set { productProviderName = value; }
           }
    
           public ActionResult Index()
           {
               var selectedProducts = string.IsNullOrEmpty(this.ProductIds) ? new Guid[0] : JsonConvert.DeserializeObject<Guid[]>(this.ProductIds);
    
             // ... rest of your controller index action
    
    
           }        
       }