I have a dashboard page made up of several different sections. Each section is rendered via a Html.Action call.
Is it possible to rearrange these into kendo scripts so the whole page can leverage the TileLayout wrapper?
I tried below, but it's throwing all sorts of console errors. The child actions do contain their own javascript scripts which may be the problem perhaps?
<script id="views-corporatedocuments-template" type="text/x-kendo-template">
@Html.Action("CorporateDocuments")
</script>
<script id="views-lowmargin-template" type="text/x-kendo-template">
@Html.Action("LowMarginOrders")
</script>
<script id="views-lateshipments-template" type="text/x-kendo-template">
@Html.Action("LateShipments")
</script>
1 Answer, 1 is accepted
Hello Joe,
Thank you for writing to us.
You can try to use Partials in order to achieve this configuration:
https://www.telerik.com/forums/set-partial-view-as-containers-body-template
Feel free to give it a try and let me know about the result.
Regards,
Eyup
Progress Telerik
Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.
I'm having difficulty getting this to work even with only two partials. I keep getting 'Invalid template' errors in console.
Here's how I'm calling the partial:
<script id="views-lowmargin-template" type="text/x-kendo-template">
@Html.Partial("_LowMarginOrders", Model.LowMarginOrders)
</script>
and the actual partial view is:
@using Kendo.Mvc.UI
@model IList<LowMarginOrderModel>
<div class="ibox float-e-margins">
<div class="ibox-content">
<div class="table-responsive">
<h3>Low Margin Orders</h3>
<div class="ibox-content">
<div class="table-responsive">
@(Html.Kendo().Grid(Model)
.Name("grd_LowMarginOrders")
.Columns(columns =>
{
columns.Bound(p => p.OrderNumber).Title("Order").ClientTemplate(
"<a href='" +
Url.Action("ViewOrder", "Orders") +
"/#= OrderNumber #'" +
" target='_blank'>#= OrderNumber #</a>"
);
columns.Bound(p => p.OrderDate).Title("Date").Format("{0:MM/dd/yyyy}");
columns.Bound(p => p.ProfitMargin).Title("Margin").Format("{0:+#;-#;0} %");
columns.Bound(p => p.ActualVarianceFromProfitTarget).Title("Variance").Format("{0:+#;-#;0} %");
columns.Command(command => { command.Destroy().Text(" "); });
})
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(false)
.Model(model => model.Id(p => p.OrderNumber))
.Destroy(update => update.Action("LowMarginOrders_Destroy", "Dashboard"))
)
)
</div>
</div>
</div>
</div>
</div>
Are my partial views just not compatible with TileLayout? I'd really rather not have to redesign all 9 of my partial views if I don't have it.
I saw another post mention:
As a workaround I instead just decided to put a placeholder in the tile and then use ajax to $.load it afterwards (no modifications needed to the ViewComponent that way). Effectively replicating the "LoadFromUrl" feature you have in many of your other wrapper controls. This works perfectly, I'd highly advise you to add such a feature to your tile control so I don't have to roll my own version of it.
Is this a better solution in my case? If so, can you provide a small example snippet of how this would be done? I am not very good at Javascript.
Hi Joe,
Can you also share CorporateDocuments and LateShipments Partial definitions with me?
I will then create a full working project with these files and send it to you.
Here is Late Shipments:
@using Kendo.Mvc.UI
@model LateShipmentsModel
<div id="late-shipments" class="ibox float-e-margins">
<div class="ibox-title ibox-collapsible">
<h5>Late Shipments</h5>
</div>
<div class="ibox-content">
<div class="table-responsive">
<h3>Custom Orders</h3>
@(Html.Kendo().Grid(Model.LateCustom)
.Name("grd_LateCustom")
.Columns(columns =>
{
columns.Bound(p => p.OrderNumber).Title("Order #")
.ClientTemplate(
"<a href='" +
Url.Action("ViewOrder", "Orders") +
"/#= OrderNumber #'" +
" target='_blank'>#= OrderNumber #</a>");
columns.Bound(p => p.ShipTo).Title("Ship To");
columns.Bound(p => p.WarehouseName).ClientGroupHeaderTemplate("#=value#").Hidden(true);
})
.DataSource(dataSource => dataSource
.Ajax()
.Group(groups => groups.Add(x => x.WarehouseName))
.ServerOperation(false)
)
.Events(events => events
.DataBound("onDataBound")
)
)
<hr/>
<h3>Stock Orders</h3>
@(Html.Kendo().Grid(Model.LateStock)
.Name("grd_LateStock")
.Columns(columns =>
{
columns.Bound(p => p.OrderNumber).Title("Order #")
.ClientTemplate(
"<a href='" +
Url.Action("ViewOrder", "Orders") +
"/#= OrderNumber #'" +
" target='_blank'>#= OrderNumber #</a>");
columns.Bound(p => p.ShipTo).Title("Ship To");
columns.Bound(p => p.WarehouseName).ClientGroupHeaderTemplate("#=value#").Hidden(true);
})
.DataSource(dataSource => dataSource
.Ajax()
.Group( groups => groups.Add( x=> x.WarehouseName))
.ServerOperation(false)
)
.Events(events => events
.DataBound("onDataBound")
)
)
</div>
</div>
</div>
<script>
function onDataBound(arg) {
//var grid = $("#grd_LateCustom");
var grid = this;
$(".k-grouping-row").each(function (arg) {
grid.collapseGroup(this);
});
}
</script>
And here is CorporateDocuments:
@using Kendo.Mvc.UI
@model IList<DocumentData>
<script type="text/javascript">
function CorpDoc_uploadSuccess(e) {
var filename = e.response.Filename;
$("#Filename").val(filename).trigger("change");
}
function ddlDocumentTypeId_Change(e) {
// get the dropdown object
var vendorDropList = $('#DocumentTypeId').data("kendoDropDownList");
// pull in the data object for the selected dropdown choice
var dataItem = vendorDropList.dataItem();
// make sure data is valid
if (dataItem) {
// pull in reason text, populate label
var selectedChoice = dataItem.Value;
updatePopup(selectedChoice);
}
}
function ddlDocumentTypeId_onDataBound() {
// get the dropdown object
var vendorDropList = $('#DocumentTypeId').data("kendoDropDownList");
// pull in the data object for the selected dropdown choice
var dataItem = vendorDropList.dataItem();
updatePopup(dataItem.Value);
}
function updatePopup(docTypeId) {
if (docTypeId) {
if (docTypeId == "0") {
$('#divFile').show();
$('#divCloudUrl').hide();
} else {
$('#divFile').hide();
$('#divCloudUrl').show();
}
}
}
// create returns tab
function CorporateDocuments_onRequestEnd(e) {
//RequestEnd handler code
if (e.type === "create" | e.type === "update" | e.type === "destroy") {
e.sender.read();
toastr.success("Corporate Documents updated", "Success");
}
}
</script>
<div class="ibox float-e-margins">
<div class="ibox-title ibox-collapsible">
<h5>Corporate Documents</h5>
</div>
<div class="ibox-content">
<div class="table-responsive">
@(Html.Kendo().Grid(Model)
.Name("grid")
.Columns(columns =>
{
columns.Bound(p => p.Url).ClientTemplate(
"# if (IsCloudUrl) { #" +
"#= Url #" +
"# } else { #" +
"<a href='#=Url#' target='_blank'>#= DisplayName #</a> #}#").Title("Document");
columns.Bound(c => c.FileDate).Format("{0:d}").Title("File Date");
columns.Command(command => { command.Destroy().Text(" "); });
})
.ToolBar(toolbar => toolbar.Create().Text("Add Document"))
.Editable(editable => editable.Mode(GridEditMode.PopUp))
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(p => p.Filename))
.Events(events => events.RequestEnd("CorporateDocuments_onRequestEnd"))
.Read(read => read.Action("CorpDocs_Read", "Dashboard"))
.Create(update => update.Action("CorpDocs_Create", "Dashboard"))
.Destroy(update => update.Action("CorpDocs_Destroy", "Dashboard"))
.ServerOperation(false))
.Editable(editable => editable.Mode(GridEditMode.PopUp)
.ConfirmDelete("Continue to delete this document/url?").DisplayDeleteConfirmation("Continue to delete this document/url?")
.Window( w=> w.Title("Corporate Documents").Width(600))
.TemplateName("_CorporateDocumentUploadEditor"))
)
</div>
</div>
</div>
In case you want it, here's the upload template:
@using CommerceBuilder.Common
@using CommerceBuilder.Stores
@using Kendo.Mvc.UI
@model DocumentData
<style type="text/css">
div.k-edit-form-container {
width: 100%;
}
div.k-edit-form-container div.editor-field textarea, input.k-textbox input.text-box {
width: 95%;
max-width: none;
}
.form-control, .single-line {
width: 95% !important;
}
</style>
@{
StoreSettingsManager settings = AbleContext.Current.Store.Settings;
string extensions = string.IsNullOrEmpty(settings.FileExt_Assets) ? "Any" : settings.FileExt_Assets;
extensions = extensions.Replace(",", ", ");
string[] validExtensions = Enumerable.ToArray(settings.FileExt_Assets.Replace(", ", ",").Split(','));
int maxUploadSize = settings.MaxRequestLength * 1000;
string maxUploadMessage = (settings.MaxRequestLength * 1000).ToString("N0");
}
<div class="ibox float-e-margins">
<div class="ibox-content">
<div class="form-group">
<label class="tooltip-label" data-content="Specify the asset type">Document Type:</label>
@(Html.Kendo().DropDownList()
.Name("DocumentTypeId")
.OptionLabel("Select asset type...")
.HtmlAttributes(new {style = "width: 80%"})
.BindTo(new List<SelectListItem>() {
new SelectListItem() {
Text = "Physical File",
Value = "0"
},
new SelectListItem() {
Text = "Cloud Url",
Value = "1"
}
})
.Events(e => e
.Change("ddlDocumentTypeId_Change")
.DataBound("ddlDocumentTypeId_onDataBound")
)
)
</div>
<div id="divCloudUrl">
<div class="form-group">
<div class="form-group">
<label class="tooltip-label" data-content="Specify the display name for this resource.">Display Name:</label>
@Html.EditorFor(m => m.DisplayName, new { @class = "form-control", placeholder = "" })
@Html.ValidationMessageFor(m => m.DisplayName)
</div>
<div class="form-group">
<label class="tooltip-label" data-content="Specify the document url for this resource.">Cloud Url:</label>
@Html.EditorFor(m => m.Url, new { @class = "form-control", placeholder = "cloud url" })
@Html.ValidationMessageFor(m => m.Url)
</div>
</div>
</div>
<div id="divFile">
<div class="form-group">
<p>
<strong>Valid File Types:</strong> @extensions<br />
You can upload a file with a maximum size of @maxUploadMessage KB.
</p>
<label class="tooltip-label" data-content="Click the button to upload the resource itself.">Physical File:</label>
@(Html.Kendo().Upload()
.Name("file")
.HtmlAttributes(new {aria_label = "file"})
.Multiple(false)
.Async(a => a
.Save("CorpDocUpload_Save", "Dashboard")
.Remove("CorpDocUpload_Remove", "Dashboard")
.AutoUpload(true))
.Events(e => e.Success("CorpDoc_uploadSuccess"))
.Validation(validation =>
{
validation.AllowedExtensions(@validExtensions);
if (@ViewBag.MaxRequestLength > 0)
{
validation.MaxFileSize(@maxUploadSize);
}
})
)
</div>
<div class="form-group">
<label class="tooltip-label" data-content="File name of the uploaded file.">File Name:</label>
@Html.EditorFor(m => m.Filename, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Filename)
</div>
</div>
</div>
</div>
Hi Joe,
I will create a runnable working MVC project with these snippets and send it to you when ready.
Thank you for your patience in advance.
The Kendo Template is sensitive special symbols like #, :, %, etc. Therefore, in order to resolve the "Invalid Template" error, these symbols need to be escaped:
https://docs.telerik.com/kendo-ui/framework/templates/overview#hash-literals
Here is how to do that in your project:
columns.Bound(p => p.OrderID).Title("Order").ClientTemplate(
"<a href='" +
Url.Action("ViewOrder", "Orders") +
"/\\#= OrderID \\#'" +
" target='_blank'>\\#= OrderID \\#</a>"
);
columns.Bound(p => p.OrderDate).Title("Date").Format("{0:MM/dd/yyyy}");
columns.Bound(p => p.Freight).Title("Margin").Format("{0:p}");
columns.Bound(p => p.Freight).Title("Variance").Format("{0:p}");
And the last requirement is to take the javascript logic outside the Partial page.
The result seems pretty neat:
I am also sending the full working sample. Feel free to check it and let me know if it helps you.
So far, I can only get the Low Margin to render. I'm still getting 'invalid template' on Corporate Documents even after escaping all the # symbols and forcing .ToClientTemplate() for the grid.
Question 1: Is it possible for the data rendered in the grid to also cause the 'invalid template'? The Corporate Documents grid will render urls which can include the ? and # symbol. Maybe I should be encoding those values in the read event for the grid?
Question 2: What is the exact list of symbols I need to be escaping? I am just looking for anything # or %...are there more I need to be finding?
I am glad the provided sample has proven helpful.
As the case is getting more specific, I would kindly ask you to open a formal support ticket and send us a runnable isolated MVC sample which demonstrates the mentioned issue for further investigation.