Partial view access different document's type cont...
# help-with-umbraco
d
I'm not very familiar with UmbracoCMS so sorry if the question is very basic. I have the followning setup: Home template -> with content in it, NotFound template -> a not found page in which I render the partial Footer.cshtml which accesses it's content from the Home template Partials/Footer -> It shall get rendered in every view but it accesses the Home template The problem is, when I load the home template footer gets rendered because it inherits from Home all its model content. But I would like to render Footer Partial also in the not found template, and that won't work because it inherits from not found: This is my code Home
Copy code
csharp
@using Umbraco.Cms.Web.Common.PublishedModels;
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Home>

@{
    Layout = null;
}
...
@Html.Partial("Footer")
...
Not found
Copy code
csharp
@using Umbraco.Cms.Web.Common.PublishedModels;
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.NotFound>

@{
    Layout = null;
}
...
@Html.Partial("Footer")
...
Footer.cshtml Partial
Copy code
csharp
@using Umbraco.Cms.Web.Common.PublishedModels;
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Home>

<footer class="w-full absolute bottom-0 md:left-1/2 md:-translate-x-1/2 flex items-center justify-center text-white">© @DateTime.Now.Year - Made with<our-svg class="inline-block w-5 h-5 heart" media-item="@Model.FooterIcon"></our-svg> by Daniel Oberlechner</footer>
Can anyone guide me in the right direction to solve this problem? I allready tried chatgtp but it wasn't really answering like I would excpect it. Kind regards, Daniel
o
Hi @danielhok, I usually create separate ViewModels for partials that are used across pages (Header, Footer etc...) and just instantiate the view model, give it the data it needs and pass it through to the partial. E.g. Footer.cshtml:
Copy code
html
@model MyFooterViewModel
<footer class="w-full absolute bottom-0 md:left-1/2 md:-translate-x-1/2 flex items-center justify-center text-white">© @DateTime.Now.Year - Made with<our-svg class="inline-block w-5 h-5 heart" media-item="@Model.FooterIcon"></our-svg> by Daniel Oberlechner</footer>
FooterViewModel.cs:
Copy code
csharp
public class MyFooterViewModel {
  public string FooterIcon { get; set; }

  public MyFooterViewModel (string footerIcon) {
    FooterIcon = footerIcon;
  }
}
Then in your Home.cshtml:
Copy code
html
// ...
  @ {
    // Instantiatethe view model and give it data
    var footerViewModel = new MyFooterViewModel(Model.FooterIcon);
  }

  // Render the partial
  @Html.Partial("Footer", footerViewModel) // Synchronous way
  @await Html.PartialAsync("Footer", footerViewModel) // Asynchronous way
  <partial name="Footer" model="footerViewModel"/> // Tag helper way

  // You can even instantiate and render it in one line if you want
  @Html.Partial("Footer", new MyFooterViewModel(Model.FooterIcon))
// ...
Hope that helps!
d
@Owain Jones That is what chatgpt told me to do in a bit a different way ... I personally thought I've only to change something with the using or inherit setting to get the right type of data ... If this is the cleanest and easiest way to deal with this problem I will try it out. If anyone thinks this can get done easier I'm open, but I guess this must be the way to do it ... Thank you @Owain Jones for your input! 🙂
o
You could also use
@inherits UmbracoViewPage
without the content type, that way the model that's passed to the partial will be of type IPublishedContent. (the interface all content models use) And then you can use the
.Value<objectType>("propertyAlias")
method to get the property data. E.g.:
Copy code
html
//@inherits UmbracoViewPage<ContentModels.Home>
@inherits UmbracoViewPage

@{
    //var socialLinks = Model.FooterIcon;
    var socialLinks = Model.Value<string>("footerIcon");
}
The only reason I would not recommend this way is because it is no longer strongly typed, and it will throw a null reference exception if the content model doesn't have that property. You could null check it using
Model.HasValue("propertyAlias")
, but even then I would advise against it; as, if you rename/remove that property from your models in the future, you won't get any warnings and could potentially forget to update the aliases on this page. (from my experience, working with non strongly typed models is a pain)
d
That is the way I beginningly wanted to go but I didn't know the pro's or cons ... I guess I'll learn it the "right" way and go with the strongly typed model from your first post... thank you for your time and answering a "Noobs" question. Thanks, Daniel! 🙂
o
Anytime mate! 😄
d
@Owain Jones I configured now everything like you showed me, but when I try to access a 404 site, I get the error: ""NotFound" enthält keine Definition für "FooterIcon", und es konnte keine zugängliche FooterIcon-Erweiterungsmethode gefunden werden, die ein erstes Argument vom Typ "NotFound" akzeptiert (möglicherweise fehlt eine using-Direktive oder ein Assemblyverweis)." This is my code:
Copy code
csharp
Footer.cshtml

@using Umbraco.Cms.Web.Common.PublishedModels;
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
@using DanielOberlechner.Views.Partials

@model FooterViewModel
<footer class="w-full absolute bottom-0 md:left-1/2 md:-translate-x-1/2 flex items-center justify-center text-white">© @DateTime.Now.Year - Made with<our-svg class="inline-block w-5 h-5 heart" media-item="@Model.FooterIcon"></our-svg> by Daniel Oberlechner</footer>

FooterViewModel.cs
using Umbraco.Cms.Core.Models;
 
namespace DanielOberlechner.Views.Partials;

public class FooterViewModel
{
    public MediaWithCrops FooterIcon { get; set; }

    public FooterViewModel(MediaWithCrops footerIcon)
    {
        FooterIcon = footerIcon;
    }
}

notFound.cshtml
@using Umbraco.Cms.Web.Common.PublishedModels;
@using DanielOberlechner.Views.Partials
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.NotFound>
...

@Html.Partial("Footer", new FooterViewModel(Model.FooterIcon))

...
Have you and hints were I made a mistake or where do I've to put the inherit statement that the view gets it's supposed to come from the home document type?
It works when I access it via the Home standard page from where the property is, but not if I try to access it in the not found ...
o
From that message, I'm assuming the exception is occurring on
Model.FooterIcon
in the
notFound.cshtml
?
Is that right?
d
yes
I have to say for the normal notFound cshtml use the notFound propertys but for the FooterIcon please use the Home propertys
o
Ok, in that case it looks like your
NotFound
model doesn't contain the
FooterIcon
property, you'll need to add that to your NotFound DocType in the backoffice.
Ahh ok, so you want your footer.cshtml to always use the property from the home page node? In that case, you will need to get the home page node from the content cache to access that property. If your NotFound page is a descendant of your Homepage you can do it like this:
Copy code
csharp
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.NotFound>

@{
    var homePageNode = Model.Root<ContentModels.Home>(); // get the home page node
    var footerIcon= homePageNode.FooterIcon; // get the footer icon, null check this
}
.Root()
will get the Ancestor (or self) of the current node(that is at level 1, aka the root of the content tree) .and the
<ContentModels.Home>
will cast the IPublishedContent to the Home model. (I can't remember if this throws an exception if it's the wrong type) But if your not found is not an ancestor of the Home page node (e.g. it is also at the root of the tree), then you can do something like this:
Copy code
csharp
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.NotFound>

@{
    var homePageNode = Model.SiblingsOfType(ContentModels.Home.ModelTypeAlias)?.FirstOrDefault() as ContentModels.Home;
    var homePageNode2 = Model.Siblings<ContentModels.Home>()?.FirstOrDefault(); // I can't remember if this just casts, or gets all of the type
}
.Siblings()
will return all of the sibling nodes (the nodes that are at the same level / have the same parent as the current node)
d
In this case its the sibling, so I have to use the last part you wrote! I'll try to get it to work... Bye the way, hope you have fun with Starfield, I have watched some players on twitch playing it and it looks like fun! 😄
o
Haha thanks! First time playing tonight, seems pretty good so far 😄 Good luck with the code!
d
Thank you again! 🙂
o
Anytime!
d
Got it now, it works like it should! Thanks ! 🙂
6 Views