Model to Partial in Umbraco - razor

I have the following snippet on my About Us page (shortened for clarity):
#foreach(var employee in CurrentPage.Employee) {
<img class="img-fluid" src="#Umbraco.Media(Convert.ToString(employee.Photo)).umbracoFile" alt="" />
<h2>#employee.Name</h2>
<h3>#employee.Title</h3>
<span>#employee.Bio</span>
}
The body of the foreach is actually bigger, I specificaly chose to leave the image in the snippet to show the usage of #Umbraco.Media
So I want to extract it to a Partial.
Here is the Code of the Partial:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage<ContentModels.Employee>
#using ContentModels = Umbraco.Web.PublishedContentModels;
#using Umbraco.Web;
<img class="img-fluid" src="#Umbraco.Media(Convert.ToString(Model.Content.Photo)).umbracoFile" alt="" />
<h2>#Model.Content.Name</h2>
<h3>#Model.Content.Title</h3>
#Model.Content.Bio
The page then becomes this:
#foreach(var employee in CurrentPage.Employee) {
#Html.Partial("Employee", new global::Umbraco.Web.Models.RenderModel(employee, Model.CurrentCulture))
}
Problem is that I'm getting
Cannot bind source content type Umbraco.Web.Models.DynamicPublishedContent to model content type Umbraco.Web.PublishedContentModels.Employee.
I think my problem comes from the fact that I loop through my Employees with CurrentPage.Employee rather than going through something like Model.Children.
Anyway, is there a way to get the Model out of the DynamicPublishedContent ?

I found out what my problem was.
Actually and as a rule of thumb, you should not use CurrentPage
I changed the code in the About Us page to this:
#foreach(var employee in Model.Content.Children("Employee")) {
#Html.Partial("Employee", employee)
}
Then my partial view needs to start like this:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage<ContentModels.Employee>
#using ContentModels = Umbraco.Web.PublishedContentModels;
And then I can use #Model.Name or any other DocumentType property on the Employee.
That's it!

Related

React + Next js: Cross json values in component

first I'd like to thank you for your time trying to help. I am a designer and I suck at developing stuff, so I have no other option than to scream for help.
So this is the situation:
I was asked to add an image (country flag) that's gonna be dynamic, in an element that fetches info from a JSON file with, among others, Resorts (one of the elements being its country's long name, i.e. Australia) and Countries (by long name and shortcode, i.e. Australia, au).
I need to get the country shortcode printed in the img src, but the resort array is only containing its long name.
The code:
This is the way the JSON file presents the information:
{
"Countries":[
{"name":"Australia",
"code":"au",
"continent_code":"oc",
"slug":"australia"}],
"Continents":[
{"name":"Oceania",
"code":"oc",
"slug":"oceania"}],
"Resorts":[{
"id":"1",
"resort_name":"Resort Name",
"encoded_name":"resort-name",
...
"country":"Australia",
...}]
}
And this is my file bit:
const DesktopResort = ({resort}) => (
<Link href="/resort/[resort]" as={`/resort/${resort.encoded_name}`}>
<a target='_blank' className='resort-item'>
<div className="resort">
<div className="top">
<div className="title">{resort.resort_name}</div>
<img className="logo" src="/assets/img/resort-logo-sample.png" />
<span className="info">{`${resort.ski_network} - ${resort.region}`}</span>
// Down below is the "dynamic" file call
<img className="flag-icon" src={`/assets/img/flags/${resort.country}.svg`} />
</div>
<div className="arrow"><img src="/assets/img/arrow-link.png" /></div>
</div>
</a>
</Link>
)
I know its badly done right now, for this australian resort my image src is /assets/img/flags/Australia.svg and what I would need to print is of course /assets/img/flags/au.svg
How would you do it?
Thanks again!
I'd write a little helper function to look up a country code based on the country name.
Note: you'll need to handle what should happen if the country is not found, or the code is not there. I'm just defaulting to an empty string here.
const countryCode = name => {
const country = yourData.Countries.find(country => country.name === name);
return country && country.code || '';
};
Then use this when you're passing the src to your img.
<img
className="flag-icon"
src={`/assets/img/flags/${countryCode(resort.country)}.svg`}
/>

c# razor template with #Link.To renders the incorrect link

I have a template for the details view of a single product. In this template it lists the "tags" and "categories" with links to view products of the same tag or category.
I define the links for the tags and categories in the same way but they are rendered differently.
here is my template:
<link rel="stylesheet" href="#App.Path/assets/portfolio.css" data-enableoptimizations="bottom"/>
<div class="sc-element">
<div class="ks-portfolio-detail">
<div class="row">
<div class="col-sm-12 col-md-6">#Edit.Toolbar(Content)
<img src="#Content.Image" alt="#Content.UrlKey" class="img-responsive" />
</div>
<div class="col-sm-12 col-md-6">
<div class="ks-title"><h1>#Content.Title</h1></div>
...
<div class="ks-lable">Categories:</div>
#{ int count=0; }
#foreach(var item in AsDynamic(Content.Categories)){
count++;
#item.Title
#(count < Content.Categories.Count?" | ":"")
}
<br/><br/>
<div class="ks-lable">Tags:</div>
#{ int counter=0; }
#foreach(var item in AsDynamic(Content.Tags)){
counter++;
#item.Name
#(counter < Content.Tags.Count?" | ":"")
}
</div>
</div>
</div>
</div>
Please note the lines where the category and tag links are created:
#item.Title
...
#item.Name
PROBLEM
The tags Link.To renders the link with "slashes" like:
http://dnn804/portfolio/tag/Demo2
but the category renders the link like:
http://dnn804/portfolio?category=Flowers
QUESTION
Can someone help me figure out why these links are rendered differently when using the same function? I want them both to appear like the "tags" link.
Thanks in advance.
The Link.To uses the DNN-internal link resolution. I'm just going to guess that there are some terms that DNN maybe treats differently, causing special links. DNN does sometimes do strange things with links, just like on the home page, which is why we are using it.
That's just a guess though.
You could run some experiments like "cat=" instead of category, to validate this.

Umbraco AncestorOrSelf missing from Model?

I have a news list under which there are a load of News Items. I'm trying to get the page name of the news list to display on each news item but this code isn't cutting it. I get an error saying "Umbraco.Web.Models.RenderModel' does not contain a definition for 'AncestorOrSelf'"
I want this to use levels rather than nodeID so it's reuseble on other pages. This is what I've got so far:-
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
Layout = "BasePage.cshtml";
var sectionTitle = Model.AncestorOrSelf(2).pageName;
}
<div id="contentHeader">
<div class="row contentHeader">
<div class="col-md-6 page-title no-left-pad">
<h1>#sectionTitle</h1>
</div>
<div class="col-md-6 no-right-pad">
Use our CareFinder
</div>
</div>
</div>
#RenderBody()
Any advice appreciated as I can't find any reason for the error anywhere.
Thanks
I think what you should be looking for is:
Model.Content.AncestorOrSelf(2).Name
Model returns a RenderModel object but what you want is the IPublishedContent object which you will find in the Model.Content property.
You should of course perform a null check before attempting to access the name e.g.
if(Model.Content.AncestorOrSelf(2) != null)
{
sectionTitle = Model.Content.AncestorOrSelf(2).Name;
}
Try using
Model.Content.AncestorOrSelf or Model.Content.AncestorsOrSelf
The available input variables are int (level) and string (NodeTypeAlias)

Umbraco Razor newbie syntax issue

I have the following code block that nearly works. It completes the outer #foreach cycle and starts each sub #foreach cycle. It puts out the first image in each pair but then outputs
"} if(countItem==1) (" which can be read in the browser.
Any advice would be appreciated
Craig
Code:-
#inherits umbraco.MacroEngines.DynamicNodeContext
#{
int level = 2;
var subjects = #Model.AncestorOrSelf(level).Children.Where("nodeTypeAlias == \"SideGallerySectionHeading\"");
int countItem = 1;
if (subjects != null) {
<div class="highslide-gallery">
#foreach(var subjectName in subjects){
<h3>#subjectName.Name</h3>
foreach(dynamic image in subjectName.Children) {
if(countItem==1){<div class="picrowdiv">}
<div class="picdiv">
<a href="#image.Media("itemLarge","umbracoFile")" class="highslide" onclick="return hs.expand(this)">
<img src="#image.Media("itemThumbnail","umbracoFile")" width="100" height="100" alt="test"></a>
<div class="highslide-caption">
#image.bodyText
</div>
<p>#image.caption</p>
</div>
if(countItem==1){</div>}
countItem++;
if(countItem>2){countItem=1;}
}
}
</div>
}
}
You may try to rewrite the line
if(countItem==1){</div>}
into
#if(countItem==1){
</div>
}
Razor is quite picky about closing your html tags so you may have to refactor to make this work. I like ternary operators for stuff like this
try
#(countItem==1 ? "</div>")
#countItem++;
#(countItem>2 ? countItem=1)
you can also do else with a :
so
#(countItem==1 ? "</div>" : "")

How to Get Model Data from Partial View?

I am creating a site in which I utilize partial views to display various bits of data about a single Model. Here is a bit of the HTML. (Note, all of these are contained within a single form and the Index page that these partials are rendered in is strongly typed to the main model. The main model contains various lists of data.)
<div id="tab1"><% Html.RenderPartial("Tab1", Model); %></div>
<div id="tab2"><% Html.RenderPartial("Tab2", Model.AnItemList1.FirstOrDefault<AnItemList1>()); %></div>
<div id="tab3"><% Html.RenderPartial("Tab3", Model.AnItemList2.FirstOrDefault()); %></div>
Here is ONE of the partial views headers (for 'tab2'):
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AnItem1>" %>
The pages display correctly. The issue is that, when I enter data into the various parts of the partial pages and then submit the entire form (via POST), the data is not making it back to my data store (MSSQL) - but this only happens for any of the list items (that are contained within the Model). The first partial page does properly have its data set within the data store.
What am I doing wrong here? Should I only be passing the model to Html.RenderPartial and then get the specific model I need on the partial page? Should I pass the entire list and then get the first (right now, I only care about the first item in the list - that will EVENTUALLY change, but not any time soon).
Suggestions or thoughts appreciated.
Update: Here is how I accessing the properties on the partial views.
<div class="data-group">
<%: Html.CheckBoxFor(model => model.Property1) %>
<%: Html.LabelFor(model => model.Property1) %>
</div>
Update 2: Per request...
Controller Action (ScenarioController):
public ActionResult Index(int id = 0)
{
if (id == 0)
{
SavedScenario scenario = new SavedScenario();
scenario.AnItemList1.Add(new AnItem1());
scenario.AnItemList2.Add(new AnItem2());
return View("Index", scenario);
}
else
{
SavedScenario scenario = repository.GetScenario(id);
if (scenario == null)
return View("NotFound");
else
return View("Index", scenario);
}
}
[HttpPost]
public ActionResult Index(SavedScenario scenario)
{
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
Rendered HTML (I can only include parts of it - this is a small sample of what is in the form):
<form action="/Scenario" id="form0" method="post">
<!-- This is the one that works - the basic Scenario. Top level. -->
<fieldset>
<legend>Scenario Information</legend>
<div class="data-group">
<div class="editor-label">
<label for="ScenarioName">Scenario Name</label>
</div>
<div class="option1">
<input class="wide" id="ScenarioName" name="ScenarioName" type="text" value="" />
</div>
<div class="validation">
<div><span class="field-validation-valid" id="ScenarioName_validationMessage"></span></div>
</div>
</div>
</fieldset>
<!-- This does not work or get submitted (as far as I can tell). -->
<div id="tab2">
<fieldset>
<legend>Tab2</legend>
<div class="data-group">
<input id="Property1" name="Property1" type="checkbox" value="true" /><input name="Property1" type="hidden" value="false" />
<label for="Property1" />
</div>
</div>
</fieldset>
</form>
My apologies for having to keep this so generic.
Hard to guess from this much code. However you should make sure that all properties of your models have the same prefix when they are posted back to the server
Edit: form field names should match property names of your model to correctly bind all values. You have two fields with the same name that you can bind in following way
[HttpPost]
public ActionResult Index(SavedScenario scenario, List<bool> Property1)
{
// here you can do with values coming in property1
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
It might be issue with naming the fields on your partial forms. Try naming the fields on your partial views by prefixing it with the name of the Model passed into it...like 'AnItemList1.name' instead of just 'name'..I am just guessing here though...but that's what I did sometimes to fix the problem when I was getting values as null..