Sitecore MVC: pass parameters to placeholders - model-view-controller

As we are switching to sitecore from a java platform, I have some questions regarding parameters. This is easily done in jsp but I can't find a solution for sitecore. (the implementation is done by external partners)
In my cshtml, I include other elements via the placeholder-function: #Html.Sitecore().Placeholder("Placholdername")
The elements included as a placeholder also can include other elements as placeholders.
So the question is: can I pass some parameters along with those placeholders?
Like my parent element has a certain variable set, for example "i = 5", and I want to pass this variable to the elements included as placeholders and also pass it to the elements included as placeholders in the placeholders?
Something like:
A includes B as a placeholder and passes "i=5" and B includes C as a placeholder and passes "i=5" so in C the value of "i" is "5" because "i" was set to "5" in A.
On out current coremedia platform I can simply use something like:
<cm:include self="${self}" view="asdf">
<cm:param name="i" value="5"/>
</cm:include>
Edit:
What I want to achieve is the following: For example I have the following structure: the page frame cshtml with a variable i=5, which then includes a grid as a placeholder and passes the variable to the grid. The grid then does some math like i=i+5 (which should equal 10) and then includes a teaser as a placeholder and passes the new i=10 to the teaser and so on..

You should set the value of i in the model. Then all the different views or partial views should inherit the same model.

You can assign parameters to renderings, but not to placeholders. Placeholders should be seen as a hole. You can dynamically put stuff in it but you can't assign parameters to it. There's a discussion of that here: https://sitecore.stackexchange.com/questions/2098/add-rendering-parameters-to-placeholder/2101
I can think of at least two approaches to solve your problem:
Although your question is a bit lacking in detail, it kind of looks like you don't necessarily need placeholders because you already know what you're going to render inside those spaces. If that's the case, then you can statically bind your MVC views instead of using placeholders. This is not a common practice, but it is mentioned in Sitecore's latest training material and elsewhere as a way to optimize when you don't need the flexibility of placeholders. This is normally done using the #Html.Sitecore.Rendering helper.
You could use a global variable of sorts by leveraging the good old HttpContext.Items collection.

You can add parameters to the ViewData dictionary, in a controller action method:
public ActionResult MyPage()
{
ContextService.Get().GetCurrent().ViewData.Add("MyKey", "MyValue");
return View();
}
Then, any View Renderings can access the parameter from ViewData:
#{
var value = ViewData["MyKey"].Value;
}
Or if you're using Controller Renderings, add some code to fetch the ViewData from parent pages, and add it to the current ViewData instance:
public ActionResult ChildRendering()
{
// Get any ViewData previously added to this ViewContext
var contextViewData = ContextService.Get().GetCurrent().ViewData;
contextViewData.ToList().ForEach(x => ViewData.Add(x.Key, x.Value));
return View();
}
Your ViewData contents will now be available in view files.
This is discussed in a little more detail here: https://chrisperks.co/2017/03/06/a-workaround-for-missing-viewdata-in-sitecore-mvc/

Related

Is it possible to instruct a `Gtk::TreeView` to display a custom type?

There is something I don't understand how to do with Gtkmm 3.
I have a custom business type that I have declared like this:
enum class Eurocents : int {};
I would like to render this type into a Gtk::TreeView which has a Gtk::ListStore as model. So I declare a Gtk::TreeModelColumn<Eurocents>, and add it to the model. I then append_column this model column to the Gtk::TreeView with an appropriate title.
I then append_row to the model and set the value corresponding to the column to (Eurocents)100.
The result I get is that the cell is displayed empty. Understandably so, because I would not expect Gtkmm to know how to render my arbitrary type.
I would like to instruct Gtkmm on how to render my type.
I already know how to display Glib types like Glib::ustring and formatting to Glib::ustring for display is possible, but it is not the subject of the question.
Is it possible to code columns that can display arbitrary types like this? And if so, how? What is required for sorting to work?
The most common, and easiest way, is to use a cell_data_func callback. For instance, you can create your own instance of a Gtk::TreeView::Column (the view column), pack a cell renderer (or more) into your Gtk::TreeView::Column, append your Gtk::TreeView::Column to the TreeView with Gtk::TreeView::append_column(), and call set_cell_data_func() on your Gtk::TreeView::Column():
https://developer.gnome.org/gtkmm/stable/classGtk_1_1TreeViewColumn.html#a3469e1adf42e5932ea123ec33e4ce4e1
You callback would then get the value(s) from the model and set the appropriate values of the properties of the renderer(s).
Here is an example that shows the use of set_cell_data_func(), as well as showing other stuff:
https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview-examples.html.en#sec-editable-cells-example
This link should also be useful:
https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview.html.en#treeview-cellrenderer-details
If you like, Gtk::TreeView::insert_column_with_data_func() makes this a little more concise: https://developer.gnome.org/gtkmm/stable/classGtk_1_1TreeView.html#a595dcc0b503a7c1004c296b82c51ac54
As for the sorting, you should be able to just call set_sort_func() to specify how the column is sorted: https://developer.gnome.org/gtkmm/stable/classGtk_1_1TreeSortable.html#a3a6454bd0a285324c71edb73e403cb1c
Then this regular sorting advice should apply: https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview-sort.html.en

Silverstripe 3: how to sort pages in the CMS sitetree by title, date etc

I'm looking for a working example of how to sort my pages in the sitetree by title by default. Ideally I only want to sort child pages of a certain type. In my case I want all my Gallery pages under the parent Portfolio to be sorted Alphabetically by their title.
This is for easy searching in the backend CMS as I know how to sort them in the template.
I have found these examples but not enough to work this out for SS3.1+
http://www.ssbits.com/tutorials/2011/custom-sorting-in-the-cms-sitetree/
https://github.com/silverstripe/silverstripe-cms/issues/848
Having a look at the examples you gave and the current Silverstripe source, there is a few ways you could go about this. My solution involves using Silverstripe's extension system to manipulate how the hierarchy is generated.
How the SiteTree is loaded
The way the CMS loads the site tree is a little lengthy so I will quickly simplify:
The template CMSPagesController_Content.ss (used for the pages section) has markup to lazy-load the linked tree view
The linked tree view (a function specified in CMSMain) calls a few internal methods to basically load the CMSMain_TreeView template
This template calls the SiteTreeAsUL function back in CMSMain
Note: SiteTreeAsUL allows us to hook in before returning using the extension system in Silverstripe though we don't want to manipulate
the HTML directly.
getSiteTreeFor, a function part of LeftAndMain, is called inside the SiteTreeAsUL.
getSiteTreeFor calls getChildrenAsUL, a function part of Hierarchy, which actually does the HTML building but most importantly, calls the correct "children" method.
I say correct children method as there is a few:
AllChildren
AllChildrenIncludingDeleted
AllHistoricalChildren
Children
Because getSiteTreeFor is called without specifying the children method, it uses a hardcoded default of AllChildrenIncludingDeleted.
Now, time to sort the children...
Calling the function AllChildrenIncludingDeleted does a few calls but what we want to know is that it internally calls the extension method augmentAllChildrenIncludingDeleted.
So, to do what you are wanting to do, you likely will want to write an extension for SiteTree with the extended function augmentAllChildrenIncludingDeleted. First argument is the list of all children which are stored as an ArrayList.
Technical Note: It actually can be an ArrayList OR DataList
because if there are no live children, it returns the raw result of
stageChildren which is a DataList.
While both have sort functions, they may act differently.
ArrayList provides a sort function which would allow you to do what you were intending.
Something like this should work:
class CMSSiteTreeSortingExtension extends Extension
{
public function augmentAllChildrenIncludingDeleted($Children, $Context = null)
{
if ($this->owner->ClassName == 'GalleryPage')
{
//Do your class specific sorting here....
}
$Children = $Children->sort('Title DESC');
}
}
And just set the extension against SiteTree (or Page if you want, should still work).
Disclaimer: I haven't personally tried this however it follows the standard pattern for how Silverstripe works with extensions so you shouldn't have a problem.
I've been searching for a way to achieve this in SS4 when I couldn't get the above code to work. This is what I've come up with.
use SilverStripe\ORM\DB;
class MemberPage extends Page
{
public function onAfterWrite(){
parent::onAfterWrite();
$pages = MemberPage::get()->sort('Title');
$sortIndex = 0;
foreach ($pages as $page){
//sort indexes start at 1
$sortIndex++;
if ($page->Sort != $sortIndex){
//we can't use $page->write() here, otherwise it'll cause infinite loops,
//we'll just have to run the query on the database directly
DB::query("UPDATE SiteTree SET Sort = {$sortIndex} WHERE ID = {$page->ID}");
}
}
}
}
It's not exactly 'the silverstripe way' but it works.

Can we dynamically set the value of "list" attribute of <apex:relatedList> component?

I am trying to design a generalized detail page for an object.
In the controller class I find the list of all child relations of that object.
I then want to create for each child relations found and for accomplishing this I will have to dynamically set the value of list attribute within it.
For example :
<apex:relatedList subject={!ObjName} list="{!relatedListName}" />
But the problem here is that list attribute only accepts String literal, so can't implement it. Please suggest a way for this requirement to be accomplished.
Yes, you can dynamically set the value of the "list" attribute on a relatedlist tag, and you do so via Dynamic Visualforce. This question has since been asked and concisely answered here on the Salesforce Stack exchange for any future browsers:
https://salesforce.stackexchange.com/questions/7531/apexrelatedlist-list-dontexistinallorgs-c-only-solveable-with-dynamic
Here is the general solution:
In a custom controller, add a function to dynamically generate the RelatedList markup. I will assume from your wording that you have already accessed the full list of child relationships in your controller, so in order to spit out all the lists in one block, I would use something like this:
public Component.Apex.OutputPanel getDynamicList()
{
Component.Apex.OutputPanel outPanel = new Component.Apex.OutputPanel();
for(String id : childNames) {
Component.Apex.RelatedList relList = new Component.Apex.RelatedList();
relList.list = id;
outPanel.childComponents.add(relList);
}
return outPanel;
}
In the middle there, you can dynamically set any string to the "List" value, and by iterating through your list of strings, you are adding related list objects over and over again. (To simply add one list, remove the for loop, and make the "id" string value whatever specific relationship you wish to display).
Then on your visualforce page, you can render this block out using a dynamic visualforce tag:
<apex:dynamicComponent componentValue="{!DynamicList}" />
(as you may know, the formulaic value field will dig up the getter automatically)
Great success!
I would suggest trying apex:dataTable or apex:repeat to build your own list display. You will likely need a wrapper class to handle passing attributes and values from the sObject to the page.

Setting initial value of knockout viewmodel using razor

I'm finding it strange that I can't find any information about how to populate the Knockout viewmodel dynamically. I guess my search terms are incorrect or something.
Anyways as I'm using Asp.Net MVC 3 and am only going to use knockout for one specific page this first time I was hoping I could somehow use the Razor model to insert into the knockout.
#Html.TextBoxFor(m => m.PropertyName, new { data_bind = "value: name" })
I figured that this didn't work because even if the razor populates the field with the PropertyName it happens before the binding so it doesn't force it's value to the knockout viewmodel.
var viewModel = {
name: ko.observable(#GetPoertyNameUsingRazorSomehow)
};
This doesn't work either, at least not in anyway I understand.
As we can easily get RazorModel data using MVC 3 normally I was sure we somehow could inject it into the Knockout viewModel. I haven't seen any tutorials that explain how. What am I missing?
I created a little library on top of Json.NET for just such an occasion:
https://github.com/paultyng/FluentJson.NET
You can create JSON in a Razor view like this (note the Knockout extension methods):
#JsonObject.Create()
.AddProperty("name", "value")
.AddProperty("childObject", c => {
.AddProperty("childProperty", "value2")
})
.AddObservable("knockoutProperty", 123)
This would produce JSON similar to this:
{"name":"value","childObject":{"childProperty":"value2"},"knockoutProperty":ko.observable(123)}
The Knockout methods are added via extension methods and other things can easily extend as well.
The package is on Nu-Get if you want it.
Assuming your property name should be a string, this should work if you enclose #GetPoertyNameUsingRazorSomehow in quotes. Otherwise, you're passing an undefined object into the observable.
name: ko.observable('#GetPoertyNameUsingRazorSomehow')

ASP.NET MVC 3 Model Binding - ViewBag.Title clash with input of id="Title"

There seems to be an issue with the ViewBag dynamic properties. Lets say I have:
#{
ViewBag.Title = #Model.CourseName;
}
And then in a form on the page I have:
#Html.TextBox("Title", null, new {style="width:400px;"})
Where Title is the name of a field in a database table.
When the page first opens, text box with an id of "Title" takes the value of the ViewBag.Title dynamic property.
I am a bit hazy on the exact details of Model Binding, but this does seem to be a bug, or if not, if it is something that occurs naturally as a result of the binding process, then it would be nice to be warned of this.
The work around I found, was to rename the ViewBag property to:
#{
ViewBag.Titulo = #Model.CourseName;
}
(Title changed to Titulo - Always good to know another language to avoid name clashes...)
And the issue went away.
However, the question is:
Is this behaviour to be expected? The bug was easy to find (took an hour to figure it out, including writing this question), but I suspect that other bugs might be more, uhmmm, recondite.
EDIT:
Rephrasing the question:
Does the Model Binder automatically bind properties it finds in the ViewBag? Even when an existing property exists in the strongly typed ViewModel I have passed to the page? Surely the ViewModel should take preference?
Html.TextBox checks ViewData/ViewBag values first, then Model. To make sure it takes Model value, you must use Html.TextBoxFor.

Resources