Showing properties from another table - Kentico - kentico

we just started with Kentico and are now testing a bit. One thing we're stuck on is showing data in transformations.
We have a custom table like Author. It hase a ID field, FirstName and SurName (both text).
Book is a documenttype and has an ID, Title and a dropdown where we can select an Author.
On a page a have a datalist where i show book with a previewtransformation like this:
<div style="text-align:center;padding: 8px;margin: 4px;border: 1px solid #CCCCCC">
<h2>
<%# Eval("Title") %>
</h2>
Author: <%# Eval("Author.FirstName") %>
</div>
Now we want to show the name of the Author but when using <%# Eval("Author") %> it's showing the ID. We found out that we can use a custom function and return the name, but isn't there another way? Let's say we not only want to show the author's name, but also address, email and so on... Do we really need to create an method for each property we want to show?
Thanks in advance,
Bjorn

No, you can't drill into related tables in this way, because the data of an author is simply not in the data source you are displaying with the data list.
But you don't have to create function for each property of an author you want to display. You may just create a function which will return whole author object, which is in your case CustomTableItem. The function may look like this.
public CustomTableItem GetAuthor(object id)
{
int authorId = ValidationHelper.GetInteger(id, 0);
var pr = new CustomTableItemProvider();
var item = pr.GetItem(authorId, "customtable.author");
return item;
}
Then in a transformation you will use GetValue() method to get the value.
Author: <%# GetAuthor(Eval("AuthorID").GetValue("FirstName")) %>
Be aware of each call of the function will issue a database request, so i would suggest to use some kind of caching. Either output cache for whole page or you may implement some caching mechanism directly inside the function.
The other option you also have is to use CustomQueryRepeater/DataSource and write your own SQL query where you join book data with author data. Then you could use simply <%# Eval("FirstName") %> directly in yout transformation.

Related

How to get values returned by child action method in mvc 5 partial view

I am trying not very successfully to get my head around MVC. My home controller contains an Index method that runs OK, so far so good, but I don't know how to call the ChildAction method Home/TopArticle
Action Method
[ChildActionOnly]
public ActionResult TopArticle()
{
return PartialView(_service.GetTopArticle());
}
In my Index view I have the mark up:
#section featured {
#Html.Partial("_TopItem")
}
_TopItem View
#model IEnumerable<MySite.Models.NewPage>
<section class="featured">
<div id="TopItem">
<div id="TopItemImg">
<a href="http://www.mysite.co.uk/">
<img style="border: 1px solid lightgray" width="320" height="233" style="border:1px solid lightgray;" alt="Model.Title" src="/Img/Model.TopItemImage">
</a>
</div>
<div id="TopContent">
<h2></h2>
<div class="dt">
<div class="dl">
#Html.Label(Model.DatePublished.ToString())
#Html.Label(#Html.Action("TopArticle", "Home", new { // am lost at this point}))
</div>
<div class="tl">
#Html.Label(Model.InfoTags ?? "")
</div>
</div>
</div>
</div>
</section>
The Index view is also using #model IEnumerable and I don't actually know whether that's OK or not. The model itself contains everything needed for both the Index and the _TopItem views, it's just that there will be one record returned for the _TopItem view and many for the Index view. Plus the code that runs in _service.GetTopArticle does some non-query stuff that is relevant only for the top article record.
I need a lie down ... and time to learn this stuff properly.
Firstly, regarding your question about calling the child action from your Index view:
Your featured section is currently calling #Html.Partial which means that it will find the "_TopItem" partial view and render it as an html encoded string in the current view (i.e. your Index view).
You specified that you are trying to call the child action TopArticle() and render the partial view returned as a html string in the view. To do this you would need to use:
#section featured {
#Html.Action("TopArticle", "Home")
}
However, I don't believe this is what you do need as you said that your Index view model contains all of the information for both Index and for the _TopItem partial view (see later).
For more information you should do a google search about the differences of views, partial views and child actions.
To correct the code I would start off by ensuring that the _TopItem partial view is correct. I have identified the following issues with the _TopItem partial view, some of which are beyond the scope of the original question:
The model passed in as an IEnumerable of NewPage but your code does not enumerate over several new page objects, it looks like it should just create the html for a single NewPage model. Therefore, I believe the model declaration should be:
#model MySite.Models.NewPage
The tag contains 2 references to the style attribute rather than 1.
The tag contains the alt attribute of alt="Model.Title" which means that alt="Model.Title" will be written directly as html where I expect you would like alt="#Model.Title" to render the contents of the model in the alt attribute.
Similarily, the tag contains src="/Img/Model.TopItemImage" where I expect this should be src="/Img/#Model.TopItemImage"
All of the label tags appear to be incorrect. For example, #Html.Label(Model.DatePublished.ToString()) - Model.DatePublished.ToString() will return a string and this string will then be attempted to be found on the model and will error as that field name does not exist. Therefore, you probably want to write: #Html.Label("DatePublished") or #Html.Label(m => m.DatePublished). With the second label i'm not sure what your trying to achieve but you may want to look up the appropriate articles.
Once, you have the corrected _TopActicle partial view, you can then return to your Index view to render the partial directly:
#section featured {
#Html.Partial("_TopItem", Model.TopArticle)
}
Note, as you have said that your Index model contains the information to pass to the _TopItem partial view, I have assumed that the Index model contains a property called TopArticle of type NewPage. Regardless, you can pass the model into the partial however you find appropriate through the call to #Html.Partial. If you pass the model through the call to #Html.Partial then you may not need the ChildOnlyAction.

How to extend Orchard navigation module to add images to menu items

UPDATE: I've changed the original question drastically based on Bertrand's suggestions and my own findings. Now it provides an incomplete solution in its text instead of my own blind meanderings and commentary on Orchard, which were completely WRONG!
I need to display a menu using images instead of text, one standard, and another for when hovered/selected. The requirements for the site states that the end-user should be able to manage the menu item images. The standard navigation module now provides an HTML menu item, which is not what the end user wants. The customer wants a very simple, intuitive interface for configuring the sites many menus, and all menus are image-based.
Based on Bertrand's advice, and after realizing that Content Menu Item IS A CONTENT TYPE, I've created a new Content Part in the Admin Interface (not by code, I only want to write code for parts and content types when ultimately needed... I really want to see how far I can go with Orchard just by using the admin interface and templating/CSSing).
So, I've created a Menu Image Part, with two Content Picker fields added to it: Image and Hover Image. Then I've added this part to the Content Menu Item in the Manage Content Items admin interface.
Since I didn't write a Driver for it, the Model passed to the menu item template does not have an easily accessible property like #Model.Href... I've overriden the MenuItemLink-ContentMenuItem.cshtml with the following code so far:
#using Orchard.Core.Common.Models
#using Orchard.ContentManagement
#{
var contentManager = WorkContext.Resolve<IContentManager>();
var itemId = Model.Content.ContentItem.ContentMenuItemPart.Id;
ContentItem contentItem = contentManager.Get(itemId);
ContentField temp = null;
var menuImagePart = contentItem.Parts.FirstOrDefault(p => p.PartDefinition.Name == "MenuImagePart");
if (menuImagePart != null)
{
temp = menuImagePart.Fields.First();
}
}
<span>#temp</span>
#Model.Text
This yields the expected title for the Menu in a link, with a span before it with the following text:
Orchard.Fields.Fields.MediaPickerField
So all the above code (get the current content manager and the id of the ContentItem representing the ContentMenuItemPart, then use the content manager to get ContentItem itself, then linqing over its Parts to find the MenuImagePart (I can't use Get to get it because it requires a type and the MenuImagePart is not a type, it was created in the admin interface), then finally getting the first field for debugging purposes (this should be the Image field of the MenuImagePart I've created...)... all the above code actually got me to the Media Picker Field on my Meny Image Part...
What I'm not being able to do, and what makes me certainly a lot obtuse and stupid, is to find a way to read the MediaPickerField URL property! I've tried casting it to MediaPickerField, but I can't access its namespace from inside my template code above. I don't even know which reference to add to my theme to be able to add the following directive to it:
#using Orchard.Fields.Fields
I've finally succeeded in this task (thanks to Bertrand's direction).
UPDATE: And thanks again to Bertrand I've polished the solution which was running in circles, querying content items from the content manager when they were already available on the Model... now I'm leveraging the dynamic nature of content item, etc. And I'm finally satisfied with this solution.
It was necessary to create a new Content Part called Menu Image, then add this to the Content Type named Content Item Menu, and finally overriding the Content Item Menu template. This last part was the really tricky one. If it was not for Bertrand's directions the code bellow would have been smelly and daunting. The template ended up as follow:
#using Orchard.Utility.Extensions;
#using System.Dynamic
#{
/* Getting the menu content item
***************************************************************/
var menu = Model.Content.ContentItem;
/* Creating a unique CSS class name based on the menu item
***************************************************************/
// !!! for some reason the following code throws: 'string' does not contain a definition for 'HtmlClassify'
//string test = menu.ContentType.HtmlClassify();
string cssPrefix = Orchard.Utility.Extensions.StringExtensions.HtmlClassify(menu.ContentType);
var uniqueCSSClassName = cssPrefix + '-' + Model.Menu.MenuName;
/* Adds the normal and hovered styles to the html if any
***************************************************************/
if (menu.MenuImagePart != null)
{
if (!string.IsNullOrWhiteSpace(menu.MenuImagePart.Image.Url))
{
using(Script.Head()){
<style>
.#uniqueCSSClassName {
background-image: url('#Href(menu.MenuImagePart.Image.Url)');
width: #{#menu.MenuImagePart.Image.Width}px;
height: #{#menu.MenuImagePart.Image.Height}px;
display: block;
}
</style>
}
}
if (!string.IsNullOrWhiteSpace(menu.MenuImagePart.HoverImage.Url))
{
using(Script.Head()){
<style>
.#uniqueCSSClassName:hover {
background-image: url('#Href(menu.MenuImagePart.HoverImage.Url)');
width: #{#menu.MenuImagePart.HoverImage.Width}px;
height: #{#menu.MenuImagePart.HoverImage.Height}px;
}
</style>
}
}
}
}
<a class="#uniqueCSSClassName" href="#Model.Href">#Model.Text</a>
The only thing that I didn't understand is why I can't use HtmlClassify as an extension method with menu.ContentItem.HtmlClassify() and have to resort to calling the method as a standard static method (see the line with the comment `// !!! for some reason the following code throws´...)
Thanks again Bertrand!

Orchard - How to display custom Term fields in a taxonomy field?

I've got a contentType (product) that has a taxonomy field (features). The taxonomy term (product feature term) has been customized to include an image field and a description field.
I'd like for the product detail view to display the image from the term along with the name, but I can't find the property to access it.
I've created the following:
Taxonomy
ProductFeature Taxonomony
Vocabulary: Feat1, Feat2, Feat3
ContentTypes
Product
Fields: Features(Taxonomy)
Product Features Term
Fields: Description(Html), Image(Image)
Views
Fields.Contrib.TaxonomyField-Features.cshtml
<!-- Old Code -->
#if (Model.Terms.Count > 0) {
<p class="taxonomy-field">
<span class="name">#name.CamelFriendly():</span>
#(new HtmlString( string.Join(", ", terms.Select(t => Html.ItemDisplayLink(Html.Encode(t.Name), t.ContentItem ).ToString()).ToArray()) ))
</p>
}
<!-- New Code -->
#if (Model.Terms.Count > 0)
{
<div>
#foreach (var myTerm in Model.Terms)
{
#Display(???)
}
</div>
}
What do replace the question marks with? I'd thought it'd be myTerm.Image but that field doesn't exist on the dynamic object.
I've attached an image of the designer viewer.
If you wanted to use the current dev branch on the module, you could access the TermsPart of the content items, which leads you to all currently applied terms.
If you are using version 0.9 of the module, then you can dynamically have access to the fields by getting a reference to your Content Item, then do contentItem.PARTNAME.FIELDNAME. In the case of a type named Product, and a field name Feature it would be contentItem.Product.Feature. Then if this term has a property named Image, it will be termContentItem.ProductTerm.Image.
I would need more information to give you the exact syntax, like the type of field, exact name of content types. Or you can post the question on the module's codeplex project discussion forum.
As Sebastien helped me figure out over on http://orchardtaxonomies.codeplex.com/discussions/263844
Below is what ended up working.
(The key bit being: contentField = myTerm.ContentItem.Features.TermImage;)
#foreach (var myTerm in Model.Terms)
{
var contentField = myTerm.ContentItem.Features.TermImage;
if (!String.IsNullOrWhiteSpace(contentField.FileName)) {
<p class="image-field">
<img src="#Url.Content(contentField.FileName)" alt="#contentField.AlternateText" width="#contentField.Width" height="#contentField.Height"/>
</p>
}
}

How do you post data to CouchDB both with and without using JavaScript

I have a show which displays a form with fields populated from a document. I'd like to change the values in the field and then save the updated document.
I'm having trouble finding a clear, concise example of how to do this.
Seriously, just finishing this example would work wonders for so many people (I'm going to leave a lot of stuff out to make this concise).
Install Couchapp
This is outside the scope of my question, but here are the instructions for completeness.
Create a couchapp
Again, this is kind outside the scope of my question. Here is a perfectly concise tutorial on how to create a couchapp.
Create a template
Create a folder in the root of your couchapp called templates. Within the templates folder create an HTML page called myname.html. Put the following in it.
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<form method='post' action='#'>
<fieldset>
Hello <input type='text' name='name' value='{{ name }}'>
<input type='submit' name='submit' value='submit'>
</form>
</body>
</html>
Create a show
See the tutorial above for hwo to do this.
Add this code to a show called myname.
function(doc, req) {
if (doc) {
var ddoc = this
var Mustache = require("vendor/couchapp/lib/mustache");
var data = {
title: "The Name",
name: "Bobbert"
}
return Mustache.to_html(ddoc.templates.myname, data)
} else {
return ('nothing here baby')
}
}
Update the document with a new name by ...
So who can complete this step via both the client side and the server side?
Please don't point me to the guide, I need to read it in your words.
Thanks.
Edit:
Although the return value isn't pretty, just posting a form to the update handler will update the document.
You will probably want to look into update handler functions.
An update handler handles granular document transformations. So you can take 1 form, that has one distinct purpose, and only update the relevant fields in your document via the update handler.
Your update handler will need to take a PUT request from your form. A browser can't do this directly, so you'll need some javascript to handle this for you. If you're using jQuery, this plugin can take your form and submit it seamlessly via AJAX using PUT for you.
Inside the function, you can take the fields you are accepting, in this case name and apply that directly to the document. (input validation can be handled via the validate_doc_update function)
Update Handler (in your Design Document)
{
"updates": {
"name": function (doc, req) {
doc.name = req.form.name;
return [doc, "Name has been updated"];
}
}
}
HTML
<form id="myForm" action="/db/_design/ddoc/_update/name/doc_id">...</form>
JavaScript
$(document).ready(function() {
$('#myForm').ajaxForm({
type: "PUT",
success: function () {
alert("Thank you");
}
});
});
Once you've gotten this basic example up and running, it's not much more difficult to add some more advanced features to your update handlers. :)

how do you pass in a collection to an MVC 2 partial view?

how do you pass in a collection to an MVC 2 partial view?
I saw an example where they used the syntax;
<% Html.RenderPartial("QuestionPartial", question); %>
this passes in only ONE question object..
what if I want to pass in several questions into the partial view and , say, I want to list them out.
How would I pass in SEVERAL questions?
Because your partial view will usually be placed in some other (main) view, you should strongly-type your main view to a composite ViewData object that looks something like this:
public class MyViewData
{
public string Interviewee { get; set }
// Other fields here...
public Question[] questions { get; set }
}
In your controller:
var viewData = new MyViewData;
// Populate viewData object with data here.
return View(myViewData);
and in your view:
<% Html.RenderPartial("QuestionPartial", Model.questions); %>
Then use tvanfosson's advice on the partial view.
Instead of passing question, why not pass a collection of questions, for instance List<QuestionType>?
Normally, you'd have an IEnumerable<Question> as a property in your view model -- in reality it might be a list or an array of Question objects. To use it in your partial, just pass that property of the view model as the model to the partial. The partial should be strongly typed to accept an IEnumerable<Question> as it's model.
<% Html.RenderPartial("QuestionPartial", Model.Questions ); %>
Partial:
<%# Page Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Question>>" %>

Resources