Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues when used inside LeBlender grid editor #63

Open
bjarnef opened this issue Nov 3, 2017 · 14 comments
Open

Issues when used inside LeBlender grid editor #63

bjarnef opened this issue Nov 3, 2017 · 14 comments

Comments

@bjarnef
Copy link
Contributor

bjarnef commented Nov 3, 2017

I have tried to use the multi url picker as a link property in a LeBlender grid editor.
However I can't really make it work with cast to the strongly typed object.

I have tried with both a Multi Url Picker and Single Url Picker (max items = 1).

Inside LeBlender grid editor.

@if (Model.Items != null && Model.Items.Any())
{
     var item = Model.Items.First();
     
     // both of these return null
     var link1 = item.GetValue<IEnumerable<Link>>("link");
     var link2 = item.GetValue<Link>("link");
}

If I use LeBlenders GetRawValue method I get the raw json, but when I deserialize this to either IEnumerable<Link> or Link it returns Unexpected character encountered while parsing value: l. Path '', line 0, position 0.

var link = item.GetRawValue("link") 
var linkPage = !string.IsNullOrWhiteSpace(link) ? JsonConvert.DeserializeObject<Link>("link") : null;

The raw json value looks like this:

[ { "id": "1076", "name": "Forside", "udi": "umb://document/48a5922997804cb5929074151348071a", "url": "/", "icon": "icon-home", "published": true } ]

If I re-use the variable from GetRawValue:

var link = item.GetRawValue("link");
var linkPage = !string.IsNullOrWhiteSpace(link) ? JsonConvert.DeserializeObject<Link>(link) : null;

then I get this error:

Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'RJP.MultiUrlPicker.Models.Link' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path '', line 1, position 1.
@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 3, 2017

Should it always return an json object with array of objects instead of a json array?

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 3, 2017

I have extracted the json object from the json array.
In the code below in returns this json object:

{
  "id": "1076",
  "name": "Forside",
  "udi": "umb://document/48a5922997804cb5929074151348071a",
  "url": "/",
  "icon": "icon-home",
  "published": true
}

I can cast this to Link object, but when I access the properties it fails.

JArray jArray = JsonConvert.DeserializeObject<JArray>(link);
var jsonObj = jArray.First.ToString();
    
var linkObject = JsonConvert.DeserializeObject<Link>(jsonObj);
@(linkObject != null ? linkObject.Name : string.Empty)

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 3, 2017

I wonder if it is because the Link model only has getters.

If I create my own model class like the following it does work.

public class LinkModel
{
    public int? Id { get; set; }
    public string Name { get; set; }
    public string Target { get; set; }
    public LinkType Type { get; set; }
    public Udi Udi { get; set; }
    public string Url { get; set; }
}
JArray jArray = JsonConvert.DeserializeObject<JArray>(link);
var jsonObj = jArray.First.ToString();

var linkObject = JsonConvert.DeserializeObject<LinkModel>(jsonObj);
if (linkObject != null)
{
    @linkObject.Id<br />
    @linkObject.Udi<br />
    @linkObject.Name<br />
    @(linkObject.Target ?? "_self")<br />
    @linkObject.Type<br />
    @linkObject.Url<br />
}

image

Another way I could make it works is using the following:

var item = Model.Items.First();
var link = item.GetRawValue("link");

JToken jToken = JsonConvert.DeserializeObject<JToken>(link);
Link linkObject = jToken != null && jToken.First != null ? new Link(jToken.First) : null;

@(linkObject != null ? linkObject .Name : string.Empty)

But it would be great to ensure you can cast to value like:

var item = Model.Items.First();
var link = item.GetValue<Link>("link");
//var links = item.GetValue<IEnumerable<Link>>("link");

or

var item = Model.Items.First();
var linkValue = item.GetRawValue("link");

var link = !string.IsNullOrEmpty(linkValue) ? JsonConvert.DeserializeObject<Link>(linkValue) : null;
//var links = !string.IsNullOrEmpty(linkValue) ? JsonConvert.DeserializeObject<IEnumerable<Link>>(linkValue) : null;

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 3, 2017

This is the fix I ended up with for now to make it work inside LeBlender grid editor as a Single Url Picker (max items = 1).

var item = Model.Items.First();
var link = item.GetRawValue("link");

// A temp fix to get Single Url Picker value (might need a few changed for Multi Url Picker).
JToken jToken = JsonConvert.DeserializeObject<JToken>(link);
Link linkPage = jToken != null && jToken.First != null ? new Link(jToken.First) : null;

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 7, 2017

It seems to be an issue in Nested Content as well.

image

@{
    var advertisers = Model.Content.GetPropertyValue<IEnumerable<IPublishedContent>>("advertisements") ?? Enumerable.Empty<IPublishedContent>();
}

@if (advertisers.Any())
{
    <div class="section row suppliers border-top">
        <div class="col-xs-10">
            <span class="headline">Annoncører</span>
        </div>
        @foreach (var banner in advertisers.OfType<Advertisement>())
        {
            DateTime startTime = banner.StartTime;
            DateTime endTime = banner.EndTime;

            var image = banner.Image;
            var link = banner.Link;
            var summaryLink = banner.ProductSummary;

            if (DateTime.Now > startTime && DateTime.Now < endTime && image != null)
            {
                <div class="col-sm-2">
                    ...
                </div>
            }
        }
    </div>
}

Update: The issue with Nested Content was because I switched from LinkPicker (https://our.umbraco.org/projects/backoffice-extensions/link-picker/) to Single Url Picker (max items = 1). So I guess the property had an empty string value and the property value converter for Multi Url Picker didn't handle empty string? It was fixed after re-publishing the page.

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 7, 2017

@rasmusjp
Copy link
Owner

rasmusjp commented Nov 7, 2017

Thanks for the very detailed report(s).

I'm not familiar with LeBlender, but I've just tried setting it up on a clean Umbraco 7.7.4 and LeBlender 1.0.8.2 and I cannot reproduce the problem using your code samples.

For a new data type configured with max items 1, @(item.GetValue<Link>("link")) correctly returns the link, and the same with multiple links. So I'm not sure why it's not working for you.

Regarding the exception when switching from LinkPicker to MultiUrlPicker is because I always store the data as an array and by looking at the source for LinkPicker it's stored as an object.
When you publish the node, the data that's stored is removed and replaced with an array which the value converter expects.

The string null or empty and null checks should be fine since Umbraco always calls ConvertDataToSource and passes the return data to ConvertSourceToObject, see https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs#L50 for reference

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 7, 2017

Okay, I am using LeBlender 1.0.8.4 and Umbraco 7.6.11

Do you have the core property value converters enabled?

I have noticed some issues with some property editors inside LeBlender, when either using @Jeavon 's core property value converter or the core property value converters included in Umbraco 7.6+

Jeavon/Umbraco-Core-Property-Value-Converters#24

The issue regarding switching from LinkPicker to MultiUrlPicker might have been because the property value contained a whitespace or JArrray returned null and it here use .ToString()
https://github.com/rasmusjp/umbraco-multi-url-picker/blob/master/src/RJP.MultiUrlPicker/MultiUrlPickerValueConverter.cs#L43

internal MultiUrls(JArray propertyData)
{
_propertyData = propertyData.ToString();
Initialize(propertyData);
}

@rasmusjp
Copy link
Owner

rasmusjp commented Nov 7, 2017

Yes, the value converters were enabled. I'll try to see if I can reproduce it with 7.6.11

I'll add some error handling if the data stored in the property is not what's expected.

@rasmusjp
Copy link
Owner

rasmusjp commented Nov 8, 2017

Hmm.. I still can't reproduce this with Umbraco 7.6.11, but as @Jeavon pointed out they are "sometimes" returning the source value which they shouldn't.

Also I've noticed they don't call any of the IValueEditor methods (e.g. ConvertEditorToDb or ConvertDbToEditor) when retrieving or saving the data, which could cause the data to be stored wrong.

So I'm pretty sure this is a problem with LeBlender and not MultiUrlPicker

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 9, 2017

Hi @rasmusjp

Yes, there might be some issue with LeBlender as @Jeavon mentions here Lecoati/LeBlender#80

However another similar issue I have, where Multi Url Picker not is used inside LeBlender editor but inside Nested Content. When deploying from local to live on Umbraco Cloud.
I do have a Nested Content inside LeBlender, which seem to fail when deploying:
umbraco/Umbraco.Deploy.Contrib#10

Because of this it fails on frontpage on live enviroment in another partial, which use Multi Url Picker inside Nested Content.
image

It seems to fails in ConvertDataToSource. It did seems to work with the property Multi Url Picker inside Nested Content before adding the other LeBlender editor with Nested Content on the page. So maybe because of the deploy failed, the Multi Url Picker has empty or not valid json?

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 9, 2017

Oh, can you please change this line to using MultiUrlPickerValueConverter class instead? ;)

LogHelper.Error<DropdownListMultipleValueConverter>("Error parsing JSON", ex);

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 9, 2017

Okay, for the last issue I mentioned with Multi Url Picker inside Nested Content it seems to be fixed after upgrading Multi Url Picker to v2.1.0

Now I don't get the ysod although it fails deploying e.g. frontpage with a LeBlender grideditor with Nested Content.

@bjarnef
Copy link
Contributor Author

bjarnef commented Nov 9, 2017

However I still have the issue with the cast inside LeBlender.

JToken jToken = JsonConvert.DeserializeObject<JToken>(link);
Link linkPage = jToken != null && jToken.First != null ? new Link(jToken.First) : null; // works
Link linkPage = item.GetValue<Link>("link"); // doesn't work

I am using Umbraco v7.6.11 and LeBlender v1.0.8.4, but it might be because of this issue:
Lecoati/LeBlender#80

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants