Get values from Multi node tree picker in Umbraco CLoud project
Hi Our Umbraco.
I have created a trial of Umbraco Cloud, as I need to set up a quite basic information site, and I thought Umbraco Cloud would be great for this.
I need to show some different lists of items, that contains name, image, some texts and the like.
These lists should be created by an editor (just a node called "Page" for each list with a multi node tree picker), and the items on the list are created as some sort of "Shared content" that can be picked.
So on the pages I have a multi node tree picker, and I can select X amount of these shared items.
But I can't get my head around showing properties from the selected items.
So i have a multi node tree picker with the alias of "items" on my page. I can render the node name of the selected items this way:
@{
var typedMultiNodeTreePicker = Model.Value<IEnumerable<IPublishedContent>>("items");
foreach (var item in typedMultiNodeTreePicker)
{
<p>@item.Name</p>
}
}
I actually thought that I should use the following example, as I guess models builder is also enabled on Umbraco Cloud sites, but the below code does also break my site:
@{
var typedMultiNodeTreePicker = Model.FeaturedArticles;
foreach (var item in typedMultiNodeTreePicker)
{
<p>@item.Name</p>
}
}
On my shared items, right now I have two fields (there's going to be more), a textstring with an alias of "headline" and an image picker with the alias of "image".
So I thought that I could show the headline like this:
<h2>@item.Headline</h2>
But this breaks my site. I haven't started looking into showing the selected image yet, but I fear that if I can't even show a basic textstring, the image would be even harder :D
Two (well probably more than two) ways of working with Umbraco.
One is the IPublishedContent approach, where every page is representing generically by an IPublishedContent object, and so doesn't know about what properties it might have... but you do so you can ask for them from the objects Value collection by alias eg
@{
var typedMultiNodeTreePicker = Model.Value<IEnumerable<IPublishedContent>>("items");
foreach (var item in typedMultiNodeTreePicker)
{
var pickedImage = item.Value<MediaWithCrops>("image");
<p>@item.Name</p>
<h2>@item.Value<string>("headline")<h2>
<img src="@pickedImage.Url" alt="@pickedImage.Name" />
}
}
(the image picker depends on what type of Media Picker used, I think my example will work for MediaPicker3)
But hopefully you get the idea, you have to know the alias, and type it as a string (StringlyTypedModels!)
When you work with .Children or .Descendants, these return collections of IPublishedContent by default, so it can be 'good' to work only with IPublishedContent, however there are drawbacks, like remember the alias you need, or typos!
Modelsbuilder will 'generate' a c# model that represents your Document Type, depending on it's configuration, this will either be in memory or on disk - so if you are using visual studio locally you want to set the folder to build them to, and use 'SourceCodeAuto' as the option, then when you change your DocumentTypes the code will update on disk, recompile and you can use intellisense.
So there is an extra faff to working with Modelsbuilder but there are less typos!
If Modelsbuilder is enabled then the bit at the top of your view, the inherits statement will have something like
and if that's the case, then @Model 'should' have the properties of the DocType as properties on the model eg your
Model.FeaturedArticles
(if you look at the location where the modelsbuilder model classes are generated on disk, you'll see they are 'just' wrappers around the .Value
Now what I'm not sure is with Modelsbuilder turned on and the Models built successfully, then your example:
@{
var typedMultiNodeTreePicker = Model.FeaturedArticles;
foreach (var item in typedMultiNodeTreePicker)
{
<p>@item.Name</p>
}
}
Should work! - this is because all the generated Modelsbuilder models DO also implement IPublishedContent and that means they always have a .Name property, so if Model.FeaturedArticles is returning an IEnumerable
Which makes me think possible the @inherits statement at the top of the view isn't the strongly typed generated model, eg Model.FeaturedArticles is erroring as the view doesn't know about the modelsbuilder model OR
the models just aren't generated yet, and if you rebuild the project it will be ok...
If your MultinodeTreePicker can only pick 'SharedPageItems' but your finding that Model.FeaturedArticles is an IEnumerable of IPublishedContent then you can usually use the .OfType extension to tell Umbraco that the picked items are all of a particular Modelsbuilder type eg
@{
var typedMultiNodeTreePicker = Model.FeaturedArticles.OfType<MySharedPageItem>;
foreach (var item in typedMultiNodeTreePicker)
{
<p>@item.Name</p>
<h2>@item.Headline</h2>
}
}
and then that would mean you could use the 'strongly typed' properties in your foreach loop...
Anyway hope that gives you the gist/insight into what is going on!
Thank you very much for your answer. Really appreaciate it!
I have tried some different approaches now, and at least got the models builder to work better by setting ModelsMode to SourceCodeManual in the appSettings.json.
But I still can't get any of the solutions to work for me.
This is my Visual Studio right now, with the correctly generated models and my Page.cshtml file where I'm testing:
The upper code (commented out in screenshot) is the first approach that does not use the Models Builder, but this gives me the following errors:
The Models builder approach gives me these errors:
I'm not quite sure if I'm missing anything, or I'm just totally off here :-/
@{
var typedMultiNodeTreePicker = Model.Value<IEnumerable<IPublishedContent>>("items");
foreach (var item in typedMultiNodeTreePicker)
{
var pickedImage = item.Value<MediaWithCrops>("image");
<p>@item.Name</p>
<h2>@(item.Value<string>("headline"))<h2>
<img src="@pickedImage.Url()" alt="@pickedImage.Name" />
}
}
(apologies as I've not got Visual Studio open!)
and in the second example, it all depends what the FeaturedArticles type is, whether it's an IEnumerable of IPublishedContent or not...
so maybe
@{
var typedMultiNodeTreePicker = Model.FeaturedArticles;
foreach (var itemPublishedContet in typedMultiNodeTreePicker)
{
var item = itemPublishedContent as MySharedPageItem;
if (item!=null){
<p>@item.Name</p>
<h2>@item.Headline</h2>
}}
}
but I can check later to be super sure of the syntax, but maybe my memory has served me better this time?
Is there any of the two methods that performs better that the other, or is it the same in that matter? Just if one way is to be preffered above the other one, besides the pros/cons you mentioned in your first answer.
By the way, I have a warning on the code regarding a null reference check or something like that in the code:
The code works, but if you have a tip for making the code more bulletproof in regards of the above screenshot, I'll take your advice ;)
With the null check, yes I think this is something they are going to change in a later version because really here, you'd just expect it to return an empty IEnumerable rather than be null.
You could fix it along the lines of
var typedMultiNodeTreePicker = Model.Items ?? Enumerable.Empty<IPublishedContent>()
As to whether to use Modelsbuilder or not Modelsbuilder, there are lots of subtle determining factors, but there's not really a noticeable performance difference that would steer you one way or another. (The Modelsbuilder models all call the .Value syntax under the hood)
A lot depends on you, and your background and what feels most natural out of the two approaches.
eg if you have an MVC background, and heavily into C# you are likely to prefer having generated strongly typed models like Modelsbuilder (or you'll create your own ViewModels, and have a 'hijacked' controller for each page...
but if you are used to working with 'magic strings' then the IPublishedContent approach can be the least faff...
... some people have difficulties spelling etc, so any help they can get from intellisense the better!
Get values from Multi node tree picker in Umbraco CLoud project
Hi Our Umbraco.
I have created a trial of Umbraco Cloud, as I need to set up a quite basic information site, and I thought Umbraco Cloud would be great for this.
I need to show some different lists of items, that contains name, image, some texts and the like. These lists should be created by an editor (just a node called "Page" for each list with a multi node tree picker), and the items on the list are created as some sort of "Shared content" that can be picked. So on the pages I have a multi node tree picker, and I can select X amount of these shared items.
But I can't get my head around showing properties from the selected items.
Well, something works. I have had a look in the documentation here: https://our.umbraco.com/Documentation/Fundamentals/Backoffice/property-editors/built-in-property-editors/multinode-treepicker/
So i have a multi node tree picker with the alias of "items" on my page. I can render the node name of the selected items this way:
I actually thought that I should use the following example, as I guess models builder is also enabled on Umbraco Cloud sites, but the below code does also break my site:
On my shared items, right now I have two fields (there's going to be more), a textstring with an alias of "headline" and an image picker with the alias of "image".
So I thought that I could show the headline like this:
But this breaks my site. I haven't started looking into showing the selected image yet, but I fear that if I can't even show a basic textstring, the image would be even harder :D
I hope someone can help me out.
Thank you very much in advance :)
Hi Kim
Two (well probably more than two) ways of working with Umbraco.
One is the IPublishedContent approach, where every page is representing generically by an IPublishedContent object, and so doesn't know about what properties it might have... but you do so you can ask for them from the objects Value collection by alias eg
(the image picker depends on what type of Media Picker used, I think my example will work for MediaPicker3)
But hopefully you get the idea, you have to know the alias, and type it as a string (StringlyTypedModels!)
When you work with .Children or .Descendants, these return collections of IPublishedContent by default, so it can be 'good' to work only with IPublishedContent, however there are drawbacks, like remember the alias you need, or typos!
Modelsbuilder will 'generate' a c# model that represents your Document Type, depending on it's configuration, this will either be in memory or on disk - so if you are using visual studio locally you want to set the folder to build them to, and use 'SourceCodeAuto' as the option, then when you change your DocumentTypes the code will update on disk, recompile and you can use intellisense.
So there is an extra faff to working with Modelsbuilder but there are less typos!
If Modelsbuilder is enabled then the bit at the top of your view, the inherits statement will have something like
and if that's the case, then @Model 'should' have the properties of the DocType as properties on the model eg your
Model.FeaturedArticles
(if you look at the location where the modelsbuilder model classes are generated on disk, you'll see they are 'just' wrappers around the .Value
Now what I'm not sure is with Modelsbuilder turned on and the Models built successfully, then your example:
Should work! - this is because all the generated Modelsbuilder models DO also implement IPublishedContent and that means they always have a .Name property, so if Model.FeaturedArticles is returning an IEnumerable
Which makes me think possible the @inherits statement at the top of the view isn't the strongly typed generated model, eg Model.FeaturedArticles is erroring as the view doesn't know about the modelsbuilder model OR the models just aren't generated yet, and if you rebuild the project it will be ok...
If your MultinodeTreePicker can only pick 'SharedPageItems' but your finding that Model.FeaturedArticles is an IEnumerable of IPublishedContent then you can usually use the .OfType extension to tell Umbraco that the picked items are all of a particular Modelsbuilder type eg
and then that would mean you could use the 'strongly typed' properties in your foreach loop...
Anyway hope that gives you the gist/insight into what is going on!
regards
marc
Hi Marc
Thank you very much for your answer. Really appreaciate it!
I have tried some different approaches now, and at least got the models builder to work better by setting ModelsMode to SourceCodeManual in the appSettings.json.
But I still can't get any of the solutions to work for me. This is my Visual Studio right now, with the correctly generated models and my Page.cshtml file where I'm testing:
The upper code (commented out in screenshot) is the first approach that does not use the Models Builder, but this gives me the following errors:
The Models builder approach gives me these errors:
I'm not quite sure if I'm missing anything, or I'm just totally off here :-/
Hi Kim
In the first approach try
(apologies as I've not got Visual Studio open!)
and in the second example, it all depends what the FeaturedArticles type is, whether it's an IEnumerable of IPublishedContent or not...
so maybe
but I can check later to be super sure of the syntax, but maybe my memory has served me better this time?
regards
marc
Marc, you totally rock!
It works now. Both of the methods :)
Is there any of the two methods that performs better that the other, or is it the same in that matter? Just if one way is to be preffered above the other one, besides the pros/cons you mentioned in your first answer.
By the way, I have a warning on the code regarding a null reference check or something like that in the code:
The code works, but if you have a tip for making the code more bulletproof in regards of the above screenshot, I'll take your advice ;)
Hi Kim
With the null check, yes I think this is something they are going to change in a later version because really here, you'd just expect it to return an empty IEnumerable rather than be null.
You could fix it along the lines of
As to whether to use Modelsbuilder or not Modelsbuilder, there are lots of subtle determining factors, but there's not really a noticeable performance difference that would steer you one way or another. (The Modelsbuilder models all call the .Value syntax under the hood)
A lot depends on you, and your background and what feels most natural out of the two approaches.
eg if you have an MVC background, and heavily into C# you are likely to prefer having generated strongly typed models like Modelsbuilder (or you'll create your own ViewModels, and have a 'hijacked' controller for each page...
but if you are used to working with 'magic strings' then the IPublishedContent approach can be the least faff...
... some people have difficulties spelling etc, so any help they can get from intellisense the better!
regards
Marc
Thank you very much Marc. The null check works as expected as well.
All good. Actually I do not have a heavy C# background at all. I'm more into frontend, but I do like the model builder approach actually :)
I think I'm good to go for now.
Again thank you very much for your help and time!
is working on a reply...