Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Kyle Goulding 30 posts 192 karma points
    Jun 05, 2017 @ 14:52
    Kyle Goulding
    0

    Umbraco 7.6 Query Builder

    Hi,

    I am new to Umbraco 7.6 and wanted to try it out for my latest project, but it appears there have been some significant changes in the new release.

    I am trying to access the properties of some child nodes but I am struggling. The child nodes don't have a template they are just displayed on the home page.

    @{
        var selection = Umbraco.TypedContent(1079).Children("homePageTiles")
                            .Where(x => x.IsVisible());
    }
    <ul>
        @foreach(var item in selection){
            <li>
                <a href="@item.Url">@item.Name</a>
            </li>
        }
    </ul>
    

    How do you access any of the other properties?

    Usually I would be able to use @item.aliasName but this doesn't seem to work with the new changes.

    I have been able to access the content picker name via @item.GetPropertyValue("contentPicker") but how can I get the URL?

    I found an article about how the query builder has change to strongly typed, is this where the issue lies, and if so aren't the partial view snippets still dynamic?

    Thanks in advance.

  • Søren Kottal 702 posts 4497 karma points MVP 5x c-trib
    Jun 13, 2017 @ 05:59
    Søren Kottal
    0

    Hi Kyle

    You are probably mixing up the naming conventions for dynamics with strongly typed.

    ModelsBuilder usually pascal cases the aliases, meaning that your @item.aliasName would be @item.AliasName.

    A rule of thumb, always start your aliases with an uppercase letter when using strongly typed :)

    For your contentpicker you can go with @item.ContentPicker.Url, but you should check if the selected content exists, so like this:

    if (item.ContentPicker != null) {
      @item.ContentPicker.Url
    }
    
  • Kyle Goulding 30 posts 192 karma points
    Jun 13, 2017 @ 08:13
    Kyle Goulding
    0

    Hi,

    Thanks for coming back... I have just tried what you have suggested and this hasn't worked.

    I have set up another test site and created a homepage and created a 'bsColumns' Document Type which doesn't have a template and is child node of the homepage. I want to use a foreach loop to include them on the page.

    The bsColumn document type has the following Properties:

    Column Title - columnTitle (Text String) Column Body - columnBody (Rich Text Editor) Column Link - columnLink (Text String)

    So using the new query builder in Umbraco 7.6.1 the following code is generated

    @{
        var selection = Model.Content.Site().Children("bSColumns")
                            .Where(x => x.IsVisible());
    }
    <ul>
        @foreach(var item in selection){
            <li>
                <a href="@item.Url">@item.Name</a>
            </li>
        }
    </ul>
    

    This works fine... but how do I get the custom properties i.e. Column Title, Column Body, Column Link?

    I have tried @item.columnTitle etc and this does work, and I have tried as you have suggested @item.ColumnTitle and neither of these work, but in previous versions (7.5) this worked.

    I think it must be to do with the 'selection' query.

    Thanks

  • Søren Kottal 702 posts 4497 karma points MVP 5x c-trib
    Jun 13, 2017 @ 08:30
    Søren Kottal
    0

    Hi Kyle

    When you include other nodes in your view, you must instruct your view which model they use.

    It will look something like this:

    @{
        var selection = Model.Content.Site().Children("bSColumns")
                            .Where(x => x.IsVisible());
    }
    <ul>
        @foreach(BsColumns item in selection){
            <li>
                <a href="@item.Url">@item.Name @item.ColumnTitle @item.ColumnBody</a>
            </li>
        }
    </ul>
    

    Not sure what the model alias for bSColumns would be though, try out both BsColumns, BSColumns and BScolumns :)

  • Kyle Goulding 30 posts 192 karma points
    Jun 13, 2017 @ 08:37
    Kyle Goulding
    0

    Hi Soren,

    Thank you for coming back...I have just tried this and below is the result

    Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately. 
    
    Compiler Error Message: CS0246: The type or namespace name 'BsColumns' could not be found (are you missing a using directive or an assembly reference?)
    
    Source Error:
    
    
    Line 34: }
    Line 35: <ul>
    Line 36:     @foreach(BsColumns item in selection){
    Line 37:         <li>
    Line 38:             <a href="@item.Url">@item.Name @item.ColumnTitle @item.ColumnBody</a>
    

    Isn't the foreach loop creating a variable of the objects within the selection called 'item' then iterating through these? As the selection has already access the objects called bsColumns?

    Trying this code with just the initial properties i.e. @item.Name and @item.Url this still fails.

    Are you using Umbraco 7.6 and are you able to get this set up to work?

    Thanks

  • Søren Kottal 702 posts 4497 karma points MVP 5x c-trib
    Jun 13, 2017 @ 08:39
    Søren Kottal
    0

    Hi again

    Maybe add @using Umbraco.Web.PublishedContentModels just after the @inherits line of your view.

    When you do the Children() stuff, you get a list of IPublishedContent items, so you need to cast them to whichever model you need in order to use its strongly typed properties.

  • Kyle Goulding 30 posts 192 karma points
    Jun 13, 2017 @ 08:54
    Kyle Goulding
    0

    Hi,

    My template already has

    @using ContentModels = Umbraco.Web.PublishedContentModels;

    If I change my 'selection' query to

     var selection = CurrentPage.Site().Children("bSColumns");
    

    This works but then this isn't strongly typed is it, and if they are going to replace dynamic with strongly typed I want to ensure that the site will still work after it has been updated.

    Thanks

  • Douglas Robar 3570 posts 4711 karma points MVP ∞ admin c-trib
    Jun 13, 2017 @ 14:56
    Douglas Robar
    3

    Oh dear, this is going to take a little bit of explaining. There are a few things in play here, each of them adding to the confusion.

    First... starting with 7.6, the goal is to move away from @CurrentPage and dynamics to @Model.Content and strongly-typed views. Using the built-in property value converters (PVCs) you don't have to use .GetPropertyValue("someAlias") you can instead type .SomeAlias and it should to the same thing.

    This works pretty well in simple situations, bridging the gap between the short and reliable dynamics syntax and the previously very long strongly-typed syntax. Often you can just replace @CurrentPage with @Model.Content and all is well.

    Where this falls down is when you are traversing the content tree and end up trying to access a property that doesn't exist on the document type you started with. That's when you'll get an error along the lines of:

    The type or namespace name 'SomeAlias' could not be found (are you missing a using directive or an assembly reference?)
    

    You've got two options at this point...

    The first is to revert to dynamics and @CurrentPage. If you're under the gun I would do it. It'll work. But it isn't the way forward and you should learn the future not rely on the past. (I say this in the same way that we never advise anyone to work on a live site; but sometimes you have to. You shouldn't. I don't really recommend it. I'm just noting that sometimes we do what we have to do. And I think of @CurrentPage in that same way these days.)

    The second is to realize that for ModelBuilder to be able to access a document type's properties and use the built-in PVCs, it needs to know what document type is in play. If you don't tell it, it'll assume it's the same doctype you started with in your view.

    Let's suppose you're on the homepage and want to get properties from a child page that is the BsColumns document type. Your template view would have the following at the top when using Umbraco 7.6. Notice the @inherits and @using lines that activate the ModelsBuilder with the data from the HomePage document type:

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage<ContentModels.HomePage>
    @using ContentModels = Umbraco.Web.PublishedContentModels;
    @{
        Layout = "Master.cshtml";
    }
    

    If you wanted to get properties from the child pages using the @CurrentPage approach you'd do something like this and it would work great:

    @{
        var selection = CurrentPage.Children("bSColumns").Where("Visible");
    }
    <ul>
        @foreach(var item in selection){
            <li>
                <a href="@item.Url">@item.Name</a>
            </li>
        }
    </ul>
    

    In this simple example, because you're dealing only with built-in properties that exist on all document types, including the HomePage that is currently the one ModelsBuilder is dealing with, you can change CurrentPage to Model.Content and it should still work.

    But your OP suggests you also want to get properties unique to those child pages. And those child pages are of the BSColumns document type. In which case, you'll need to force the document type to get access to them (I didn't test this code, I might have a slight typo but right now we're talking concepts so bear with me):

    @{
        var selection = Model.Content.Children<BSColumns>().Where(x => x.IsVisible());
        // you could also use Model.Content.Site().Children<BSColumns>()... }
    <ul>
        @foreach(var item in selection){
            <li>
                <h3>@item.ColumnTitle</h3>
                <div>@item.ColumnBody</div>
                <a href="@item.ColumnLink">Read more...</a>
            </li>
        }
    </ul>
    

    The key to success here is changing .Children("bSColumns") which is the dynamics approach to forcing the type with Children<BSColumns>() instead. That tells the ModelsBuilder which model to use. In this case, the model associated with the BSColumns document type. The result being that you can then get properties directly from children of that document type and still keep it strongly typed.

    Hope this helps you understand the changes and why it isn't quite as smooth sailing as we'd all hoped at this point.

    Background is in this bug report, especially in the comments. http://issues.umbraco.org/issue/U4-9764

    And you're correct, there are still bugs with the Query Builder. Such as http://issues.umbraco.org/issue/U4-9895 and http://issues.umbraco.org/issue/U4-9849.

    And the built-in snippets haven't been updated from dynamics yet. http://issues.umbraco.org/issue/U4-9727

    Finally, let's assume you wanted to use a Partial View and pull the logic out of your template. In that case, be sure to specify all the doctypes in your selection query. As I've done here for a news area and articles:

    @{ var selection = Model.Content.Site()
                        .FirstChild<NewsArea>()
                        .Children<NewsItem>()
                        .Where(x => x.IsVisible())
                        .OrderByDescending(x => x.CreateDate); }
    

    Whew. Hope that helps clear things up and get you on your way to both a functioning solution and an understanding of why it is the way it is.

    cheers,
    doug.

  • Kyle Goulding 30 posts 192 karma points
    Jun 15, 2017 @ 09:24
    Kyle Goulding
    100

    Hi Doug,

    Many thanks for coming back to me with this... I have been trying this out with varying levels of success and failure.

    When attempting to access the Document Type BS Columns (alias: bSColumns) I was still receiving the same error.

    var bsCols = Model.Content.Children<BSColumns>();
    

    The type or namespace name 'BSColumns' could not be found (are you missing a using directive or an assembly reference?

    But when just attempting to access the children of the homepage this all worked!

    var allChildren = Model.Content.Children();
    

    My next steps were thinking - 'Is there something wrong with my document type - BS Columns'?

    So I created a new DocType - Columns... using the example you gave i tried to loop through these...

    var cols = Model.Content.Children<Columns>();
    

    Eureka! This is working!

    So my next step is it something with the name of my original BS Columns (alias bSColumns) perhaps the spaces?

    So I created a new document type called 'New Document Type' (alias newDocumentType)... tried the same query

    var newDocType = Model.Content.Children<NewDocumentType>();
    

    Eureka! This is working too!

    So my last test...could this be something to do with my Document Type having two capitals next to each other?

    I created a new document type called 'DT New' (alias: dTNew), then applied the same query

    var dtNew = Model.Content.Children<DTNew>();
    

    And the result:

    The type or namespace name 'DTNew' could not be found (are you missing a using directive or an assembly reference?)
    

    ...I tried with the 'T' as lower case:

    The type or namespace name 'DtNew' could not be found (are you missing a using directive or an assembly reference?)
    

    My finding are that when a Document Type has two capitals together it isn't recognising the document type. Which is what had caused my issue in the first place with BSColumns.

    To use the built-in query builder to access my custom properties I had to amend the query for my document type 'Columns', alias: columns as follows

     @{
        var selection = Model.Content.Site().Children<Columns>()
                            .Where(x => x.IsVisible());
    }
    <ul>
        @foreach(var item in selection){
            <li>
                <a href="@item.Url">@item.Name</a> 
                <b>Custom Property:</b> @item.PageTitle
            </li>
        }
    </ul>
    

    Thanks again Soren and Doug for your help!!! I hope this helps someone else out in the future too if they have to same issues.

    Thanks

  • Douglas Robar 3570 posts 4711 karma points MVP ∞ admin c-trib
    Jun 15, 2017 @ 09:41
    Douglas Robar
    1

    Woohoo, making good progress!

    The capitalization is important. And not always obvious. It is truly pascal-case, which isn't quite as simplistic as we always think. Especially with multiple capitalized letters and spaces it gets rather un-intuitive.

    You can look in the ~/App_Data/Models/models.generated.cs file. Don't change it, just look at it. Search for "partial class" and you'll see the Models Builder's casing for each document type.

    Here are some examples from a test site I'm running. Most are obvious but those with multiple capitalizations aren't.

    /// <summary>ezSearch</summary>
    [PublishedContentModel("ezSearch")]
    public partial class EzSearch : PublishedContentModel
    
    /// <summary>Text Page</summary>
    [PublishedContentModel("textPage")]
    public partial class TextPage : PublishedContentModel
    
    /// <summary>News Area</summary>
    [PublishedContentModel("newsArea")]
    public partial class NewsArea : PublishedContentModel
    
    /// <summary>FAQ Item</summary>
    [PublishedContentModel("fAQItem")]
    public partial class FAqitem : PublishedContentModel
    

    The partial class is how you identify it in your strongly-typed Razor code.

    cheers,
    doug.

  • Kyle Goulding 30 posts 192 karma points
    Jun 15, 2017 @ 10:25
    Kyle Goulding
    1

    Hi Doug,

    Thanks for this - I have just checked in the 'models.generated.cs' file and can see my Document Type 'BS Columns' is referenced as

    public partial class BScolumns : PublishedContentModel
    

    From trying

    var selection = Model.Content.Children<BScolumns>();
    

    This is now working.

    Thanks again for your help.

    H5YR

Please Sign in or register to post replies

Write your reply to:

Draft