Kentico's webpart control ID in value in a repeater and transformation - twitter-bootstrap-3

I'm building out a bootstrap based accordion. It's almost there, except i need to wrap each accordion with a tab with a unique ID. My thought was to use the repeaters control ID. So how i can access this from a transformation, and also the HTML envelope?
Here is the HTML envelope from the repeater
<div class="accordion" id="askUsAccordion">
</div>
Here is my transformation code
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<%# DataItemIndex + 1 %> <%# Eval("Heading") %>
</h4>
</div>
<div id="accordionPanel<%# DataItemIndex + 1 %>" class="panel-collaspe collapse" role="tabpanel" aria-labeledby="panel<%# DataItemIndex + 1 %>">
<div class="panel-body">
<%# Eval("Panel") %>
</div>
</div>
</div>

Why not just use the Repeater's ClientID?
Try <%# Container.ClientID %> in this case Container should reference the Repeater the transformation is running on.

Mark, not sure this is the best solution, but it should work for you. Add server side function into your transformation like this:
<script runat="server">
protected string GetID()
{
Control parent = this;
while ( (!(parent is CMSWebParts_Viewers_Documents_cmsrepeater)) &&
(parent != null))
{
parent = parent.Parent;
}
return (parent as CMSWebParts_Viewers_Documents_cmsrepeater).WebPartID;
}
</script>
And call this method in your transformation like this:
<%# GetID() %>

While this isn't my preferred method, i wrote a quick js snippet. I try to avoid having too much JS.
/* Accordions */
// we first detect if there is an accordion in the DOM, and if see we ensure that each is with it's own names space
if ($accordion.length){
// we need the ID of each accordion on the page which then becomes the data-parent value, which is needed to ensure we can isolate accordions
$accordion.each(function(i,v){
var $this = $(this),
$id = $this.attr('id');
// loop through each accordion panel
$this.children('.panel').each(function(){
var $that = $(this);
$('.panel-title-link', $that).attr('data-parent', $id);
});
});
}

Related

Piranha CMS block inside and outside container div

From code template we have:
<div class="block #block.CssName()">
<div class="container">
#Html.DisplayFor(m => block, block.GetType().Name)
</div>
</div>
it make my content block always inside container class. How to make a flexible page where we can put a block inside and outside container
You can check the block type and put a different class on the div besides "container" like this:
<!-- language: lang-razor -->
<div class="block #block.CssName()">
<div class="#(block.GetType().Name == "MySpecialBlock" ? "myspecialclass" : "container") ">
#Html.DisplayFor(m => block, block.GetType().Name)
</div>
</div>
or you can remove the div altogether:
<!-- language: lang-razor -->
<div class="block #block.CssName()">
#Html.DisplayFor(m => block, block.GetType().Name)
</div>
with the intention of editing each DisplayTemplate .cshtml file and add a wrapper div there. i.e. /Views/Cms/DisplayTemplates/HtmlBlock.cshtml :
<div class="myWrapperClass">
#Html.Raw(Model)
</div>
Note: If you did this, you'd probably want to edit each of the various block type templates to add a wrapper of some kind.
Another possibility would be to write a helper class that checks the block type and automatically returns a specific class depending on each block type.
using Piranha.Extend;
namespace MyProject.Classes
{
public static class Helper
{
public static string getWrapperCssClassForBlockType (Block block)
{
string blockName = block.GetType().Name;
string className = "";
switch(blockName)
{
case "HtmlBlock":
className = "row";
break;
case "QuoteBlock":
className = "myQuoteClass";
break;
default:
className = "container";
break;
}
return className;
}
}
}
Then just call the helper method from in your template(s):
#using MyProject.Classes;
#foreach (var block in Model.Blocks)
{
var wrapperClass = Helper.getWrapperCssClassForBlockType(block);
<div class="block #block.CssName()">
<div class="#wrapperClass">
#Html.DisplayFor(m => block, block.GetType().Name)
</div>
</div>
}
So finally i've made it by removing containerfrom template:
<div class="block #block.CssName()">
<!--<div class="container">-->
#Html.DisplayFor(m => block, block.GetType().Name)
<!--</div>-->
</div>
It make all standard block sit outside container. Next i made a group block of container. Any block can be inside this container group block except standard group block such as gallery and columns. Luckily it is open source, so we can make our own columns and gallery group block to be inside a container by modify cshtml template. I dont know how piranha.org do this
But i think my solve fit my purpose

How to pass a dynamicy changed model to Partial view?

I have a list of "workbooks" displayed in a table. Each workbook has a "Share" button next to the workbook's title. When the user clicks on the share button a modal dialog is shown containing a form.
The form allows the user to enter a list of the recipient's emails separated by a comma which is validated on the client-side.
As the dialog is located in a partial view _ShareView.cshtml that allows me to pass a modal WorkbookShareModel that has some fields like WorkbookId and Title. The goal here is to pass the details of each workbook when the user presses the share button (i.e. construct a modal and pass it to the already rendered model).
I am not sure how to pass a model to an already rendered view?
The solution have to be done on the client (i.e. dont involve actions on the server that return the partial view provided the parameters are passed). I want to avoid unnesessary calls to the server - we have all the data on the client regarding a workbook and I need to do a POST when the user types in list of emails.
This is my index.cshtml:
#section BodyFill
{
<div id="shareFormContainer">
#{ await Html.RenderPartialAsync("_ShareView", new WorkbookShareModel());}
</div>
<div class="landing-container">
<div class="workbook-container">
<table class="table">
<tbody>
#foreach (var workbook in Model.Workbooks)
{
string trClassName, linkText;
if (workbook.Metadata.SharedBy == null)
{
trClassName = "saved-workbooks";
linkText = workbook.Name;
} else {
trClassName = "shared-with-me";
linkText = string.Format(
BaseLanguage.SharedWithMeWorkbook,
workbook.Name,
workbook.Metadata.SharedBy,
workbook.Metadata.SharedDate.ToShortDateString()
);
}
<tr class="#trClassName">
<td>#Html.ActionLink(linkText, "Open", "OpenAnalytics", new { id = Model.Id, workbook = workbook.Name })</td>
<td class="last-modified-date" title="Last Modified Date">#workbook.ModifiedDate.ToShortDateString()</td>
<td class="share">
<button title="Share" class="share-button" onclick='showSharingView("#workbook.Name", "#workbook.Id", "#Model.Id")'> </button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
#section Scripts
{
<!--Load JQuery 'unobtrusive' validation -->
#await Html.PartialAsync("_ValidationScriptsPartial")
<script type="text/javascript">
// hide the modal as soon as the page loads
$('#shareFormModal').modal("hide");
function showSharingView(title, workbookId, id) {
$('#shareFormModal').modal("show");
// how to pass a WorkbookShareModel to my partial view from here?
}
function hideDialog() {
var form = $("#partialform");
// only hide the dialog if the form is valid
if (form.valid()) {
activateShareButtons();
$('#shareFormModal').modal("hide");
}
}
// Helper method that validates list of emails
function IsEmailValid(emailList, element, parameters) {
var SPLIT_REGEXP = /[,;\s]\s*/;
var EMAIL_REGEXP =
/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+##[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/i;
var emails = emailList.split(SPLIT_REGEXP);
for (var i = emails.length; i--;) {
if (!EMAIL_REGEXP.test(emails[i].trim())) {
return false;
}
}
return true;
}
</script>
}
That is my dialog:
#using DNAAnalysisCore.Resources
#model DNAAnalysisCore.Models.WorkbookShareModel
#* Partial view that contains the 'Share Workbook dialog' modal *#
<!-- Modal -->
<div onclick="activateShareButtons()" class="modal fade" id="shareFormModal" role="dialog">
<div class="modal-dialog modal-md">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Share Workbook - #Model.Title</h4>
</div>
#using (Html.BeginForm("ShareWorkbook", "Home", FormMethod.Post, new { #id = "partialform" }))
{
<div class="modal-body">
<label>#BaseLanguage.Share_workbook_Instruction_text</label>
<div class="form-group">
<textarea class="form-control" asp-for="Emails" rows="4" cols="50" placeholder="#BaseLanguage.ShareDialogPlaceholder"></textarea>
<span asp-validation-for="Emails" class="text-danger"></span>
</div>
<input asp-for="Title" />
<input asp-for="Id" />
<input asp-for="WorkbookId"/>
</div>
<div class="modal-footer">
<button onclick="hideDialog()" type="submit" class="btn btn-primary">Share</button>
<button onclick="activateShareButtons()" id="btnCancelDialog" type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
}
</div>
</div>
</div>
There are two solutions to solve your problem :
Option 1 :
Since you have got the parameters(title, workbookId, id) , you can call server side function using AJAX to render the partial view , then replace the DIV contained in the partial view with the updated contents in the callback function of AJAX .
You can click here for code sample .
Option 2 :
Directly update related input/area using Jquery . For example , the input tag helper :
<input asp-for="<Expression Name>">
generates the id and name HTML attributes for the expression name specified in the asp-for attribute. So you can set the value using Jquery like :
$("#Title").val("Title")
Please click here for Tag Helpers in forms in ASP.NET Core
With Option 2 , you need to clear the Emails area firstly after user click the share button ; With Option 1 , you don't need to care that since the HTML will replace entirely .

Razor's Umbraco.media throws error when no image is uploaded

I'm making a bunch of content boxes to render to the master. They all contain a background picture, a heading and content, all are ment to be optional. so if none = empty divs. -> removed.
it all seemed allright i used the #Umbraco.Field for text and #Umbraco.Media(CurrentPage.myMediaAlias).url for the bg picture. Using 'media picker' as datatype.
No problem leaving the heading and content fields blank.
But the problem started when there was no picture defined for the last box.
i got the:
Object reference not set to an instance of an object.
[No relevant source lines]
Since it is supposed to be optional to put in that picture, i would rather want a null back.
The media picker property is not mandatory. How can i get this to work?
#section contentMid2 {
<div class="c-box-wrapper">
<div class="c-box" id="c-box-3" Style="background-image: url(#Umbraco.Media(CurrentPage.box3Bg).Url )" >
<div class="transbox">
<div class="home-head-1"> #Umbraco.Field("mainHeading3") </div>
<div class="content-text1"><p>#Umbraco.Field("mainContent3")</p></div>
</div>
</div>
}
Untested - but something like this should work. They key being to use HasValue
if(#CurrentPage.HasValue("box3Bg"){
<div class="c-box" id="c-box-3" Style="background-image: url(#Umbraco.Media(CurrentPage.box3Bg).Url )" >
}
else{
<div class="c-box" id="c-box-3">
}
OR
#{
var inlineStyle = "";
if(#CurrentPage.HasValue("box3Bg"){
inlineStyle = background-image: url(#Umbraco.Media(CurrentPage.box3Bg).Url )
}
}
<div class="c-box-wrapper">
<div class="c-box" id="c-box-3" Style="#inlineStyle" >
<div class="transbox">
<div class="home-head-1"> #Umbraco.Field("mainHeading3") </div>
<div class="content-text1"><p>#Umbraco.Field("mainContent3")</p></div>
</div>
</div>
</div>
After some debuging and some help from #marco. i realized how to fix it:
<div class="c-box" id="c-box-3" Style="background-image: url(#(Umbraco.Media(CurrentPage.box3Bg.ToString())!=""?Umbraco.Media(CurrentPage.box3Bg).Url:Umbraco.Media(CurrentPage.box1Bg).Url))" >
It's a Shorthand, and still inside the style url: #(theobj!=empty?nah:yah) works like a charm.
But i had to convert theobj .ToString() before comparing or it or else it seems like .Media cant decide if it's int or string. even if the question was if it's nothing in there at all.

Reuse and pass dynamic object (CurrentPage) to Partial View MVC Umbraco

I'm somewhat (6 months) new to MVC, and I like to use as little as code as possible, especially when it comes to reusable code, etc. I'm using Umbraco v7.2, and I have (3) tabs, all which use the same data type (custom grid v7).
The grid has (4) fields. Basically all (3) sections on my page are the same w/ the exception for the header and the object that is called (the dynamic object is what has the properties in them for the tab, which as I stated earlier, are the same).
How can I call a partial view and reuse the same code? The "foreach" is where I need to have this partial view called, as you can see it uses the same exact code w/ the exception of the object being iterated.
The "CurrentPage.XXXX" is what I need to pass, and I can have the same iterator
#foreach(var XXXX in CurrentPage.XXXX) <---- partial view
View:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
Layout = "Master.cshtml";
}
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
Leadership Team
#foreach (var leadership in CurrentPage.leadershipTeam)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#leadership.contactName</li>
<li>#leadership.contactTitle</li>
</ul>
</div>
<aside class="col-md-6 col-sm-6">
<ul>
<li>#leadership.contactPhone</li>
<li>
#leadership.contactEmail
</li>
</ul>
</aside>
</section>
}
</div>
</article>
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
The Lenders One Team
#foreach (var lenders in CurrentPage.lendersTeam)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#lenders.contactName</li>
<li>#lenders.contactTitle</li>
</ul>
</div>
<aside class="col-md-6 col-sm-6">
<ul>
<li>#lenders.contactPhone</li>
<li>
#lenders.contactEmail
</li>
</ul>
</aside>
</section>
}
</div>
</article>
.... another one here but omitted for brevity
And turn it into:
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
Leadership Team
#Html.Partial( ?????? )
</div>
</article>
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
The Lenders One Team
#Html.Partial( ?????? )
</div>
</article>
Partial ???
#foreach (var contact in ??????)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactName</li>
<li>#contact.contactTitle</li>
</ul>
</div>
<aside class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactPhone</li>
<li>
#contact.contactEmail
</li>
</ul>
</aside>
</section>
}
Appreciate it ;)
EDIT:
To clarify, I've used partial views before in Umbraco. The issue above is I have (3) different objects (grids in u7). How the grid works in Umbraco is you create a new data type, and define certain fields in that data type (textbox, media picker, etc). You can then add properties to document types (in this case I used the custom grid I created). Once a page is created, based off a document type, properties are inherited.
For the contact page, I needed (3) separate grids. However each grid has different data in them. Therefore this is (3) different JSON objects, which I iterate over. In the above code, the (3) JSON objects are:
leadershipTeam
lendersTeam
avisoryBoard (the one omitted for
brevity)
How can I pass (CurrentPage.JSONobjectHere) to the partial view, using only ONE partial view for all THREE sections?
Did something similar to this once.
Call your partial like this:
#Html.Partial("YourPartialName", (object)CurrentPage.lendersTeam)
And then use a dynamic as a model in your partial:
#model dynamic
#foreach (var contact in Model)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactName</li>
<li>#contact.contactTitle</li>
</ul>
</div>
<aside class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactPhone</li>
<li>
#contact.contactEmail
</li>
</ul>
</aside>
</section>
}
This is not too difficult. In the call to the partial, just use the name of the partial. This is the filename of the partial without the extention. The current "Model" will also be available in your partial without you have to pass something to the partial.
<article class="accordion-wrapper">
<div class="accordion-container accordion-contact">
Leadership Team
#Html.Partial("NameOfThePartialWithoutExtention")
</div>
</article>
If you inherit from UmbracoTemplatePage or the UmbracoViewPage, then you can use the model as if you were in the View itself.
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#foreach (var contact in CurrentPage.Children)
{
<section class="clearfix">
<div class="col-md-6 col-sm-6">
<ul>
<li>#contact.contactName</li>
<li>#contact.contactTitle</li>
</ul>
</div>
</section>
}
Thanks Morten OC. Thumbs up. I was using NestedContent and calling Partial view to render IPublishedContent, but also wanted access to CurrentPage.
Simple cast to object first worked. To elaborate, here's some extra code in case someone needs the same -
Html.RenderPartial("~/Views/Partials/MyPartial.cshtml",
item, //for me this is an IPublishedContent
new ViewDataDictionary { { "CurrentPage", (object)CurrentPage } });
Then in my Partial, shortened for brevity -
#inherits Umbraco.Web.Mvc.UmbracoViewPage<IPublishedContent>
#{
dynamic CurrentPage = ViewBag.CurrentPage;
}
<p>#CurrentPage.Name</p>
So my Model is IPublishedContent (since I was iterating through nested contents)
And then in ViewBag you'll have a reference to CurrentPage. I named it CurrentPage just so I could continue using CurrentPage as we typically do in Umbraco.
Cheers Morten H5YR

Angular code is not working in bootstrap modal

I am using angularJs, twitter bootstrap with asp.net mvc4. I didn't use angular-strap. I am trying to load a partial view in bootstrap modal. The partial view contains angular code.
Here is my code where I am calling the bootstrap modal:
#model IEnumerable<IMS.Domain.Model.GetAllRequisitions_Result>
#{
ViewBag.Title = "Requisition List";
Layout = "~/Views/Shared/MasterPage.cshtml";
}
Launch demo modal
<div id="myModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-body">
</div>
</div>
<script type="text/javascript">
function loadCreateForm() {
var content = '#Url.Action("Create", "Requisition", new { area = "IMS" })';
$('div.modal-body').load(content);
}
</script>
Here is my code for 'Create' action of 'Requisition' controller:
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Modal header</h3>
</div>
<div ng-app="">
<label>Sample: <input type="text" ng-model="sample" /></label>
<br />
<label>Show Value: {{sample}}</label>
</div>
<div class="ContainerRowButton">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
For ng-model="sample" when I am trying to show data by {{sample}} it stays {{sample}}. It's like my 'Create' action is not getting angularJs.
I've rendered all necessary scrips in the 'MasterPage.cshtml'. My MasterPage.cshtml contains:
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryui")
#Scripts.Render("~/bundles/others")
And here is the code of my 'Bundle.config.cs' file:
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/jquery-ui-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/others").Include(
"~/Content/bootstrap/js/bootstrap.js",
"~/Scripts/jquery.dataTables.js",
"~/Scripts/dataTables_bootstrap.js",
"~/Scripts/bootstrap-dropdown.js",
"~/Scripts/jquery.dataTables.columnFilter.js",
"~/Scripts/Custom/CustomScript.js",
"~/Scripts/jquery.validationEngine-en.js",
"~/Scripts/jquery.validationEngine.js",
"~/Scripts/angular.js"
));
What wrong am I doing? Need help ...
Adding to what #Muctadir said, you need to tell angular to check your modal partial for angular elements. You do that by calling $compile before injecting the partial into the DOM. The problem is that you have to be able to get access to the $compile service and you can't do that inside of your loadCreateForm(), you can only do that from an angular module. So, you need to organize your code in such a way that you can get what you need. Here's my suggestion:
Create a directive to handle your modal and its contents:
.directive('myModal', function($compile){
return {
replace:false,
link: function(scope, element, attrs){
var content = '#Url.Action("Create", "Requisition", new { area = "IMS" })';
elem.load(content, null, function(){
$compile(elem.contents())(scope);
});
}
};
});
That will load the content and then compile the contents using the current scope, but only after the content has been loaded from the server. Hope that helps.