Correct way to access a ContentType Field from code?

Topics: Customizing Orchard
Sep 20, 2012 at 5:24 PM
Edited Sep 20, 2012 at 5:25 PM

Hi!

I'm pretty new in Orchard land but I like this CMS already very much - as an Author at least, it's great to see a .NET based CMS with so many extensions and templates available.

As a developer, I'm just started learning but I'm about to pull my hair out because I'm struggling already at things that seem to me like the most basic tasks.

I added a new Field to the HtmlWidget Content Type. It should be a Link because I want to make headers (titles) of HtmlWidgets linkable. For testing purposes, I now use just a simple text field called "TestText" for debugging.

So I edited Widget.Wrapper.cshtml where the Title is rendered to do something with this new field there. I struggled to find good and current doc on how to access fields from code (found a lot about fields that are part of PARTS but not about simple fields I add directly to a ContentType).

Well, after some investigations and debugging it turns out those fields are at runtime part of the ContentPart.

I found out by doing some LINQ like this:

var obj2 = ((Orchard.ContentManagement.ContentItem)Model.ContentItem).Parts.Where(p => p.Fields.Any(f => f.Name == "TestText")).FirstOrDefault();

So a ContentPart was found here and I thought I found the solution. I didn't. The first try, which, by documentation, is rather old syntax from 1.0 days, failed:

var contentPart = ((IContent)Model.ContentItem).As<ContentPart>();
var testText = contentPart.Fields.First(f => f.Name == "TestText").Storage.Get<string>(null);

It crashes and tells me that the sequence contains no elements. I really don't get it because my earlier query indicated clearly that this field is contained in the ContentPart.

Anyway, I tried, according to documentation, the newer syntax:

var testText = Model.ContentItem.ContentPart.TestText.Value;

 This also crashed telling me that there is no TestText property on ContentPart.

I really don't get it and it may very well be because I have to few understanding of the core concepts of Orchard yet, but hopefully someone can help me out here.

Thank you very much in advance for any help!

 

Coordinator
Sep 21, 2012 at 8:47 PM

Well, do you have a content part named "ContentPart" or maybe you just forgot to replace that with the actual name of the part that has the field?

Sep 21, 2012 at 9:13 PM

Thanks very much for your Response.

Now i added the field manually to the Content Type. You know, if you edit the Content type, there are the "Fields" and the "Parts" and I used "Add field".

So the field is not actually inside a part.

But, if you look at my first query, I go through all parts of the ContentItem and search for the part that has a field with the name I defined in it. This query successfully returns a part, and this is the "ContentPart". As I didn't know where those manually added fields are actually stored, this was my way to find it out.

But, as I said, if I try to access the field like in my other 2 examples postet above, I get exceptions. So I'm confused.

And that's my question ... what is the right way to access fields I added manually to a Content Type?

Thanks again!

Bernhard

Coordinator
Sep 21, 2012 at 9:29 PM

A field is *always* under a part. It's just that if you add the field from the admin, a part with the same name as the type will be created automatically for you.

Sep 21, 2012 at 11:52 PM

Ah ok THIS was the information I searched for and found nowhere, thanks a lot!

The correct syntax, in my case (it's the HtmlWidget), is:

var text = Model.ContentItem.HtmlWidget.TestText.Value

Works fine now.

Althought I think this is not a ideal solution as I have to write code in Widget.Wrapper.cshtml that works for the HtmlWidget only while this cshtml file is used for any kind of widget. I would prefer if those fields are accessible in a more generic way, like Model.ContentItem.Fields.TestText.Value or something like that ...

But anyhow, that works fine now.

Thanks!

Bernhard

Coordinator
Sep 22, 2012 at 6:26 PM

No, that's not an option as different parts may have fields with the same name. We could of course implement a simplification for when there is only one, or we could return the first one we find, but that could become a bug farm pretty fast.

Sep 23, 2012 at 12:25 AM
Edited Sep 23, 2012 at 1:50 AM

That was not what I meant ... of course, fields that are defined in parts should be accessible over the part's name so different parts can have the same field names.

I meant only the fields that are manually added to a ContentType over Admin should not be put into a part that is named after the type.

Edit: After thinking about it, it would be (in my usage scenario) of course the best solution to create a custom part with the field to reuse it on multiple types. Anyhow, I think it's still confusing that fields I add over admin go into a part named after the type as this is not obvious and it confused me. As I said, I would have expected them directly under the ContentType, like Model.ContentItem.Fields.MyField ... only those added over admin of course, not the ones that are attached over parts.

But it's not a real issue now that I know it, just some thoughs that  came on my mind while trying to solve this issue ...

Just wanted to let you know what I meant :)

Thanks again for your help and keep up the great work, after taking this little hurdle I'm more convinced than ever that moving from Umbraco to Orchard was the right choice.

Bernhard