Sitecore - render Code if item is NULL - javascript

In Sitecore you can easily render a context item's value by using
#RenderingContext.Current.Rendering.Item["itemname"]
However: when I place a Controller Rendering to a placeholder there is no content assigned yet. therefore nothing will be rendered, and therefore I can't check for null.
My specific problem:
I want to add the value of a database item to the parameters of a call.
var options = {{lat: #RenderingContext.Current.Rendering.Item["lat"],
lng: #RenderingContext.Current.Rendering.Item["lng"]},
zoom: #RenderingContext.Current.Rendering.Item["zoom"]};
As there is simply NOTHING (yet) the partial view fails to load because there is no associated content (yet) ==> javascript error. If there is content it works fine.
So how can I
Add a default associated content item to a controller rendering (mhhhh)
Check if ?NOTHING? is null? (better)
Any idea?
Anyone who's got my problem?
Cheers!

You have a few options available to you, I would suggest a combination of things depending on your needs but you also want to be defensively coding in all aspects since content authors have the potential to do things that they should not!
You can set some default values for your templates so that they always have an initial value. With your template selected and the Builder tab highlighted, select the Options tab from the ribbon and then add Standard Values
A new item called __Standard Values will be added under the template, you can set default values there. Make sure you have set the Datasource Location and Datasource Template fields on your rendering which will cause the prompt to create/select datasource item.
You still need to defensively code though. How you do this is up to you. For example, if certain fields are not set correctly then maybe you do not show the component at all or return a different view to show it is incorrect:
public class WidgetController : GlassController
{
public ActionResult Index()
{
var configItem = GetDataSourceItem<ILocationConfiguration>();
if (configItem.Longitude == null && configItem.Latitude == null)
return PartialView("~/Views/Components/Shared/IncorrectSettings.cshtml");
return PartialView("~/Views/Components/Widget/Index.cshtml", configItem);
}
}
(The above sample is using Glass Mapper, I know you are not using it but I would highly recommend it or to use strongly types models in any case. The example still stands though.)
You can also make some checks in the View itself, although I wouldn't put too much code in there myself. Depending on the component, sometimes we do not show the rendered component in Experience Editor mode. The sample below allows the values to be edited in EE mode but renders the script block and component if values have been set:
#model Sitecore.Mvc.Presentation.RenderingModel
#if (!Model?.Item?.TemplateID=="guid" ?? true)
{
#Html.Raw("<div class=\"error\">Incorrect Datasource</div>")
return;
}
#if (Sitecore.Context.PageMode.IsExperienceEditor)
{
<!-- This allows component settings to be edited in EE mode -->
<div>
Longitude: #Html.Sitecore().Field("lng", Model.Item)
Latitude: #Html.Sitecore().Field("lat", Model.Item)
Zoon: #Html.Sitecore().Field("zoom", Model.Item)
</div>
}
else
{
string lng = Model.Item["lng"],
lat = Model.Item["lat"],
zoom = Model.Item["zoom"];
if (!string.IsNullOrEmpty(lng) && !string.IsNullOrEmpty(lat) && !string.IsNullOrEmpty(zoom))
{
<script>
var options = { lat: #lat, lng: #lng, zoom: #zoom }
</script>
<div>
set up the component in normal mode
</div>
}
}
There are lots of different ways to achieve the above including making checks in the JavaScript itself and how you invoke the code for your component, but I've tried to keep it self-contained and simple for example.

Related

Rendering html content in datatable

I am loading server response in a datatable using js in a asp.net core razor page. Because the data/UI is complex, I need to render different layouts based on current value in each table cell.
Datatable supports a renderer function for each cell, so I could have something like:
...
"data":"somefield",
"renderer":function(data,type,row,meta){
if (data.someId)=="someValue"{
return "<div... some label with somValue</div>"
}else{
return "<div... some label without value</div>"
}
}
}
This works perfectly fine, however when divs get complex with style and many labels it becomes harder to maintain or change.
I did look a bit into Razor's PartialViews as it may seem like a good alternative. Having the UI in a cshtml file, being able to pass parameters from parent #Model and using c# in it to render it based on the parameter received.
While I am able to load the partial view in the parent page, using <partial name=''/> or #Html.Partial(...) I didn't manage to get it's content in js using $.get and return it in the datatable's render function. Probably async wouldn't work in this case? Or it would be too slow?
My question is: what would be a better way to handle this situation? Maybe partial views are not the way. I am looking for a way of easily maintaining/changing the cell content. Thank you for your time.
I'm not really familiar with asp.net but I will try to answer your question.
I don't think PartialViews are going to work in the way you suggest because they appear to be server-side code, and trying to do a GET request for every line in your table could potentially generate a large number of server requests.
I think you have a couple of potential solutions. First is to loop through your data on the server, and for each property that matches the condition generate the partial view and assign it to the property. Then return your data array with one of the properties in each row being a chunk of HTML. As I said, I don't have experience with this language so it's hard for me to provide a code example, but I hope you understand what I'm trying to say. Then in DataTables you just need to output the value
columns: [
{ data: 'someField' }
]
THe second option is to generate the HTML on the client using JavaScript. Since you say that it could be complex it's best if you have a function that returns a HTML string. If it's a large amount of HTML then you could even put this function in a separate file and export it, to make it more manageable. There are a couple of typos in your example, so I'm going to fix them here. Renderer is a table property, the one you want is columns.render. Also in the render function the data argument references the data property that is defined in the line above. If you want to reference a different property, use the row argument.
columns: [
{
data: 'someField',
render: (data, type, row) => renderMyData(data, row)
}
]
function renderMyData(data, row) {
if (row.someId == "someValue") {
return "<div...> some label with somValue</div>"
} else {
return "<div...> some label without value</div>"
}
}

Access property of an object of type [Model] in JQuery

So in my MVC project I have a custom Model called Survey, which contains a handful of properties. In the Survey Controller, I am saving Survey to a Session variable, so that the values of the survey's properties are persisted per session.
I want to be able to manipulate the DOM of a View based on the values of the session survey's properties. But I'm having trouble with how to access those.
I did find this relatively recent question that seems very similar but doesn't have an answer: Cannot access properties of model in javascript
Here's what I have so far: In the View I am getting the session's survey like so:
<input type="hidden" name="activeS" value="#HttpContext.Current.Session("Survey")" />
Then in the Section Scripts at the bottom I have this script to get that value and do something with it:
#Section Scripts
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(function () {
var survey = $("[name=activeS]").val();
$("[name=mddbccu][value=" + survey.mddbccu + "]").prop('checked', true);
})
</script>
End Section
If I insert "alert(survey);" after "var survey..." It does give me an alert that displays the type that the survey object is. So it looks like the survey is being retrieved fine. But if I try "alert(survey.mddbccu);" the alert simply says "undefined".
Note that the line after that ("$([name=mddbccu]...") I know works - having previously set a variable to a specific value, using that the appropriate item is checked. But in attempting to get the value of this particular property of the survey, nothing is checked.
So how do I get the values of the survey's properties here? Thank you!
Your approach would work with some hackery and workarounds but it is not in the spirit of MVC. Here is how you could accomplish it in the MVC way. Basically you move all the heavy lifting (parsing the item from the session) 0 to the controller and store the results in a ViewModel. This keeps the logic out of the view and makes for much cleaner and easier to maintain code.
If you have a ViewModel:
public ActionResult Survey()
{
SurveyViewModel model = new SurveyViewModel();
Survey surveySession = HttpContext.Current.Session("Survey") as Survey; // youll have to do extra null checks and such here
// map other properties from the survey object retrieved from the session to your viewmodel here!
model.mddbccu = surveySession.mddbccu;
model.otherProperty = surveySession.otherProperty
return View(model);
}
If you are just using the Survey object as the model inside the view then its even simpler:
public ActionResult Survey()
{
Survey model = HttpContext.Current.Session("Survey") as Survey;
return View(model);
}
Then, MVC magically selects stuff for you depending on what you have set in the controller. If you are using #RadioButtonFor(m => m.mddbccu, "three") then the radio will be selected if the value "three" was put into the property mddbccu in the controller.

data-win-bind issues: converter only runs once and unable to bind id of element

I have the following html that is bound to an object containing id and status. I want to translate status values into a specific color (hence the converter function convertStatus). I can see the converter work on the first binding, but if I change status in the binding list I do not see any UI update nor do I see convertStatus being subsequently called. My other issue is trying to bind the id property of the first span does not seem to work as expected (perhaps it is not possible to set this value via binding...)
HTML:
<span data-win-bind="id: id">person</span>
<span data-win-bind="textContent: status converter.convertStatus"></span>
Javascript (I have tried using to modify the status value):
// persons === WinJS.Binding.List
// updateStatus is a function that is called as a result of status changing in the system
function updateStatus(data) {
persons.forEach(function(value, index, array) {
if(value.id === data.id) {
value.status = data.status;
persons.notifyMutated(index);
}
}, this);
}
I have seen notifyMutated(index) work for values that are not using a converter.
Updating with github project
Public repo for sample (not-working) - this is a really basic app that has a listview with a set of default data and a function that is executed when the item is clicked. The function attempts to randomize one of the bound fields of the item and call notifyMutated(...) on the list to trigger a visual updated. Even with defining the WinJS.Binding.List({ binding: true }); I do not see updates unless I force it via notifyReload(), which produces a reload-flicker on the listview element.
To answer your two questions:
1) Why can't I set id through binding?
This is deliberately prevented. The WinJS binding system uses the ID to track the element that it's binding to (to avoid leaking DOM elements through dangling bindings). As such, it has to be able to control the id for bound templates.
2) Why isn't the converter firing more than once?
The Binding.List will tell the listview about changes in the contents of the list (items added, removed, or moved around) but it's the responsibility of the individual items to notify the listview about changes in their contents.
You need to have a data object that's bindable. There are a couple of options:
Call WinJS.Binding.as on the elements as you add them to the collection
Turn on binding mode on the Binding.List
The latter is probably easier. Basically, when you create your Binding.List, do this:
var list = new WinJS.Binding.List({binding: true});
That way the List will call binding.as on everything in the list, and things should start updating.
I've found that if I doing the following, I will see updates to the UI post-binding:
var list = new WinJS.Binding.List({binding: true});
var item = WinJS.Binding.as({
firstName: "Billy",
lastName: "Bob"
});
list.push(item);
Later in the application, you can change some values like so:
item.firstName = "Bobby";
item.lastName = "Joe";
...and you will see the changes in the UI
Here's a link on MSDN for more information:
MSDN - WinJS.Binding.as
Regarding setting the value of id.
I found that I was able to set the value of the name attribute, for a <button>.
I had been trying to set id, but that wouldn't work.
HTH
optimizeBindingReferences property
Determines whether or not binding should automatically set the ID of an element. This property should be set to true in apps that use Windows Library for JavaScript (WinJS) binding.
WinJS.Binding.optimizeBindingReferences = true;
source: http://msdn.microsoft.com/en-us/library/windows/apps/jj215606.aspx

Knockout JS Templates makes the UI "flash" when edited

I have a big problem with using Knockout JS. In my view model I have a field, called Method, that is actually an other view model.
This view model can be one of three different things (it is mapped to a polymorphic object in the domain model). To solve this I use templates that checks which type of Method that is selected withing the domain model and then shows the template that binds data for that type.
The function that checks the type of method looks like:
this.getTemplate = function (data) {
var method = data.original.get_Method();
if (method instanceof MyProj.MethodA)
return "methodA";
else if (method instanceof MyProj.MethodB)
return "methodB";
else if (method instanceof MyProj.MethodC)
return "methodC";
}
The markup where I bind the template looks like:
<div data-bind="template: {name: getTemplate($data), data: $data.Method}"></div>
This actually works very nice and when I change the type of method via an dropdown in the UI the domain model updates and the right template is shown. However here comes my problem. Each template contains a number of different fields that are specific for each method type. Whenever I change one of the values in the view model displayed by one of the templates the UI flashes and I think that happens because the template get selected again. This is quite irritating and looks extremly bad.
Any ideas on how to solve this problem? Any help would be greatly appreciated!
Thanks in advance
/Björn
Did you use any observable inside the getTemplate function. Updating the value of that observable makes the template rerender and you get your flash effect.
Checkout this link Part : "Note 5: Dynamically choosing which template is used".

Wicket + Javascript

I'm wrapping up a Javascript widget in a Wicket component. I want to let the JS side talk to the component. What I've got so far:
Component in question goes like
talker = new GridAjaxBehavior();
this.add(talker);
in constructor
and then, later on, puts something like
"var MyGridTalker = new talker(" + this.talker.getCallbackUrl() + ");";
into the JS.
where GridAjaxBehavior extends AbstractDefaultAjaxBehavior. I want GridAjaxBehavior to spit back some XML when the JS calls it.
Am I doing this the right way? What should GridAjaxBehaviour do to spit back the XML?
Thanks
Spit back some XML for what? Presumably to update the model or the view, yes?
The strength of Wicket is that you don't have to worry about the rendered HTML. In Model-View-Controller terms, you set up the Controller to correctly modify the Model, and Wicket takes care of the View.
The separation is not entirely clear: in fact you can show/hide view components, or change then, and that can be seen as altering the View.
But what you generally don't have to do is directly manage the browser or javascript. Wicket takes care of that, if you take care of making your changes in the Java code.
In Wicket, the Ajax will call a method on your AjaxBehavior with an AjaxRequestTarget target.
In that method (or in methods called from it), you do whatever you need to do, updating models or views, and then you add to the target any view component that that has changed. Wicket takes care of updating the browser.
Here's an example. It's taken from some code I did, but heavily altered just to make explication clearer. The idea is simple: "chained" dropdown choices, where the options in the child change when the select option in the parent changes, as in the series of [State] [County] [District].
(In the actual class, the Model change is passed to the child, which decides for itself if it has changed, and adds itself to the target if it has, then passes the target to its child. I've removed most of that to make a clearer example.)
Here's the ctor, which just adds to itself an anonymous subclass of an AjaxBehavior:
public AjaxChildNotifyingDropDownChoice(...code elided for clarity...) {
this.child = child;
// Ajax won't work without this:
setOutputMarkupId(true);
//
add( new OnChangeAjaxBehavior() {
#Override
public void onUpdate(final AjaxRequestTarget target) {
// tell child to update its list
// based on newly selected value
// when the Ajax is called,
// my owning component's model
// is already updated
// note we could just type getModel()
// I'm making explicit that we're calling it
// on the enclosing class
// (which a non-static inner class has a hidden ref to)
child.setNewModelBasedOnSelectionOf(
AjaxChildNotifyingDropDownChoice.this.getModel());
// now add the child to the target
// Wicket javascript will receive the new
// options and re-render the child dropdown
target.add(child);
}
});
}
We could also have hidden or un-hidden components, or added behaviors like CSS styles, or even swapped one Panel for another. As long as for each changed component we:
1) called setOutputMarkupId(true); so that the javascript can find it, and
2) added it to the AjaxRequestTarget
Note that different types (subclases) of Ajax Behavior have different callback functions, so be sure you're overriding the right one (add an #Override annotation so the compiler can complain if you got the name wrong).
But again, the basic wicket idea is that instead of sending raw data for the client to parse and act on, you update your model and view, and tell Wicket to re-render what you've changed, by adding the chnaged components to the target.
The only reason I can think of to send straight XML would to be to feed it to non-Wicket javascript. Let me know if that's your aim, and I completely missed the point. ;)
I don't really know what Wicket is or what it does, but there is a minor bug in your code (as it appears).
This:
"var MyGridTalker = new talker(" + this.talker.getCallbackUrl();
You seem to be missing your end parens:
"var MyGridTalker = new talker(" + this.talker.getCallbackUrl() + ")";
Anyway, not a big deal, but didn't know if it was intentional.

Categories