Here is how my structure is setup:
I have many Services (DocType) which hold children Documents (DocType).
The Document DocType can have other Document elements as its children.
This way, it's possible for editors to create that kind of tree:
Service > Document> Document >Document > Document
There's no limits to the amount of levels this can go on for.
I'm trying to find a way to loop recursively through each Document and their descendants, but they need to be nested within eachother. This is where I'm having trouble.
I can't seem to find a simple way to loop recursively through each levels per children to generate the content.
Here is what I have so far:
#{ var selection = Model.Children.Where("Visible");
if (selection.Any()) {
<ul>
#foreach (var item in selection) {
<li>
<div class="ServiceDocCenterDocumentation">
<h4>#item.Name</h4>
<div class="DocCenterDocumentationDescription">#item.GetPropertyValue("bodyText")</div>
</div>
<div>
#foreach (var children in item.Descendants()){
#* This is the part I'm struggling with for the last few days *#
var actualChildren = children;
<h5>#children.Name</h5>
<div class="DocCenterDocumentationDescription">#children.GetPropertyValue("bodyText")</div>
}
</div>
</li>
}
</ul>
}
}
Here's what I would like to achieve (recursively, not manually):
<div class="myService">
<div class="Documents">
<div class="Document_#elem.Id">
#elem.bodyText
foreach (var child in elem.Children){
<div class="Document_#child.Id">
#child.bodyText
foreach (var grandchild in child.Children){
#* It goes on and on for the amount of levels available *#
}
</div>
}
</div>
</div>
</div>
I'm still not very fluent in Razor, and I'm wondering if there's a way of achieving what I would like to achieve.
You should use Razor helpers to create your recursive function.
Apart of that, you shouldn't use Descendants in each children as it would return every node below this one. You just need its children, then the children of each children and so forth. So you will pass each children list to the function to display it.
#{ var selection = Model.Children.Where("Visible");
if (selection.Any()) {
<section class="DocCenter">
#foreach (var item in selection) {
<section class="Service">
<div class="ServiceDocCenterDocumentation">
<h1>#item.Name</h1>
<div class="DocCenterDocumentationDescription">#item.GetPropertyValue("bodyText")</div>
</div>
#DisplayChildren(item.Children().Where("Visible"))
</section>
}
</section>
}
}
#helper DisplayChildren(IEnumerable<IPublishedContent> children){
if(children.Any()){
<section class="children">
foreach(var child in children){
<section class="Document_#child.Id">
<div class="DocumentDetails">
<h1>#children.Name</h1>
<div class="DocCenterDocumentationDescription">#children.GetPropertyValue("bodyText")</div>
</div>
//Recursive call here
#DisplayChildren(child.Children().Where("Visible"))
</section>
}
</section>
}
}
On the other hand, although is not related to the recursive question, I would use the <section> element to separate parents and children, that way you can also use h1 for all the headings as they are created in a section hierarchy:
Using HTML sections and outlines
Related
I implemented the UIkit sortable component and added a stop event. But I can't figure out how to calculate the new order if an item has been dragged. So far the only thing I can think of is giving each item an id then calculating based upon that id, but it doesn't seem like the proper way to do so
There is a quite simple way of achieving this. The element stores originalEvent where you can find also explicitOriginalTarget - our moved element. As it is wrapped in li inside ul, I went up to its parentNode (li), so I am at the level of elements I need, then converted it to jQuery object (you don't have to, I did it just because it was quick), then you can get its index. All of these values can be accessed by console.log(e);
Only problem with this solution is performance, it works, but when you move elements too often, it can show 0 instead of correct index value
EDIT: I realized you're probably asking about the whole set of items and their order, not only the index of currently moved item, so I added also console logging for this as well
Example below:
var indexes = new Array();
$(document).on('moved', '.uk-sortable', function(e) {
var currentLi = e.originalEvent.explicitOriginalTarget.parentNode;
indexes = [];
$(this).find('li').each(function() {
indexes.push($(this).data("index"));
});
alert("New position: " + $(currentLi).index());
console.log(indexes);
});
$('.uk-sortable').find('li').each(function(i) {
$(this).data("index", i);
indexes.push(i);
});
console.log(indexes);
<!-- UIkit CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.35/css/uikit.min.css" />
<!-- UIkit JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.35/js/uikit.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.35/js/uikit-icons.min.js"></script>
<ul class="uk-grid-small uk-child-width-1-4 uk-text-center" uk-sortable="handle: .uk-card" uk-grid>
<li>
<div class="uk-card uk-card-default uk-card-body">Item 1</div>
</li>
<li>
<div class="uk-card uk-card-default uk-card-body">Item 2</div>
</li>
<li>
<div class="uk-card uk-card-default uk-card-body">Item 3</div>
</li>
<li>
<div class="uk-card uk-card-default uk-card-body">Item 4</div>
</li>
<li>
<div class="uk-card uk-card-default uk-card-body">Item 5</div>
</li>
<li>
<div class="uk-card uk-card-default uk-card-body">Item 6</div>
</li>
<li>
<div class="uk-card uk-card-default uk-card-body">Item 7</div>
</li>
<li>
<div class="uk-card uk-card-default uk-card-body">Item 8</div>
</li>
</ul>
I came across this searching for something else and happen to know the answer you're looking for. You don't need jQuery or anything else for this, just UIkit.
<ul id="sortable-element" uk-sortable>
<li class="uk-sortable-item" data-id="1">Content</li>
<li class="uk-sortable-item" data-id="2">Content</li>
<li class="uk-sortable-item" data-id="3">Content</li>
<li class="uk-sortable-item" data-id="4">Content</li>
<li class="uk-sortable-item" data-id="5">Content</li>
</ul>
let sortable = UIkit.sortable("#sortable-element");
UIkit.util.on(sortable.$el, "added moved", function(e, sortable) {
sortable.items.forEach(function(item, index) {
console.log({ item, index});
// Grab data attributes if you need to.
// UIkit.util.data(item, "id");
});
});
The second parameter of the callback references the sortable component and contains the array of item elements. Loop through this array and use the index (0 based) to get the new order of items. It's important to use the .uk-sortable-item or define a different class with the cls-item option for the sortable component to return the items.
You also don't need to define sortable like I have, you can just use the UIkit.util.on with CSS selectors, e.g. UIkit.util.on("#sortable-element", "added moved removed start stop", function(e, sortable) { console.log(e.type); });
UIkit.util is more or less undocumented, but it's extremely well built. Check the repo to see available functions. They are binded to UIkit.util in the dist/uikit.js file. https://github.com/uikit/uikit/tree/develop/src/js/util
The easiest way I have found is to get the list of all the elements and perform a mapping operation that returns an array of unique & identifiable attributes (e.g. the IDs of the sortable elements.
The moved event has a detail property that's an array containing the UIKitComponent and the target element; you can get the items from the UIKitComponent.
const new_order = event.detail[0].items.map(el => el.id);
//["id-1", "id-2", "id-3"];
You can then get the indices after the fact, at least the messy DOM side of things is sorted.
I have simple bootstrap(3) tabs inside a div (class="example").
I have the div saved in a Partial view.
I am using foreach loop in my Razor view, passing the object to the Partial view and outputting it for each iteration.
How can I simply navigate each div's own tabs without affecting other tabs inside the loop?
View:-
#foreach (var pbrModel in Model)
{
#Html.Partial("~/_TestViewPartial.cshtml", pbrModel)
}
Partial View:-
#model PbrViewModel
<div class="example">
<!-- Nav tabs -->
<ul class="nav nav-tabs">
<li class="active">Home</li>
<li>Profile</li>
<li>Messages</li>
<li>Settings</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane active" id="home">...</div>
<div class="tab-pane" id="profile">...</div>
<div class="tab-pane" id="messages">...</div>
<div class="tab-pane" id="settings">...</div>
</div>
</div>
You can use a for loop to iterate over the items in your main view's Model, and then use the i iterator to make each tab's id unique by appending it in the partial view.
You can pass the i value as a "model" to the partial view when you iterate.
Like so:
#for (var i = 0; i < Model.Count; i++)
{
#Html.Partial("~/_TestViewPartial.cshtml", i)
}
Then in the partial view, you can use the "model" passed to it from your for loop - in this case as an int type, so it can print the value into the ids accordingly.
Like so:
#model int
<div class="example">
<!-- Nav tabs -->
<ul class="nav nav-tabs">
<li class="active">Home</li>
<li>Profile</li>
<li>Messages</li>
<li>Settings</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane active" id="home#Model">...</div>
<div class="tab-pane" id="profile#Model">...</div>
<div class="tab-pane" id="messages#Model">...</div>
<div class="tab-pane" id="settings#Model">...</div>
</div>
</div>
Much like my previous (before this edit) answer, passing the i variable to each iteration ensures the ids are unique.
Unfortunately, with this way you will not be able to access any properties of the main view's Model, as you are only passing the int to the Partial view, and nothing else (I explain more below).
A couple of notes to think about:
Your path to the partial doesn't need to be "relatively absolute". In that, I mean you can just use "_TestViewPartial.cshtml" as the first argument (omitting the "~/")
If you do wish to access properties of your main view's pbrModels inside the partial, you will need to pass these to the partial (as per your OP, with PbrViewModel as the #model type) and I would suggest adding a unique indexer property to that type, if possible - so you can then print this in the id/href of the elements within your partial view, like in my example; just use #model.MyUniqueIDProperty or whatever friendly name you have for it
THINK - Do you really need a separate partial view for this? If you're reusing the code elsewhere, then yes. If it's solely for the purpose of the page, then no; I would defer to having the code in the main view's code - you would then still be able to access the main Model of the page, if you need to get properties from the PbrViewModels using the indexer you're at (Model[i])
Any questions, just ask.
Hope this helps! :)
I have a list :
$scope.list = ["test/test1/test2/test3","test3/test5/test6"];
I would like to apply bold style to / characters when displaying the list :
<div ng-repeat="path in list">
<p style="font-weight:bold">{{path}}</p>
</div>
Do you have any ideas how can I achieve this ?
Fiddle
you can do it simply with str.replace http://jsfiddle.net/k18vgtvw/
<p style="font-weight:bold" ng-bind-html-unsafe="csc(path)"></p>
controller
$scope.csc = function(path) {
return path.replace(/\//g, "<span style='color:red'>/</span>");
}
There are a number of ways to do this. First I'd add a function to your controller, let's say it's called boldSlashes.
function boldSlashes(path) {
return path.replace("/","<b>/</b>")
}
Then change your html to be:
<div ng-repeat="path in list" ng-bind-html>
boldSlashes({{path}})
</div>
The ng-bind-html tells angular to treat the contents as html and not escape it.
You also have to inject ngSanitize into you module in order to use ng-bind-html.
So wherever you create your module, add ngSanitize to the dependencies like:
angular.module('myApp',[ngSanitize])
I'm not sure if this is what you are trying to do but I separated out individual elements. Also the jsfiddle font the bold font looks exactly the same on the / character.
http://jsfiddle.net/3a2duqg4/
1. Updated the view to a list
2. Changed the array to have an individual item per section
3. Added styles to the "/" and realized the font bold property with the fiddle default font didn't look any different.
<div ng-controller="MyCtrl">
<ul>
<li class="list" ng-repeat="path in list">{{path}} <span>/</span></li>
</ul>
</div>
Added the items to a list rather than a paragraph and added some styles. I updated your array to have one value per array item as well.
Let me know if this helps! :)
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.list = ["test/test1/test2/test3","test3/test5/test6"];
$scope.updateString = function(s) {
return s.replace(/\//g, '<span class="bold">/</span>');
};
}
.bold {
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<div ng-repeat="path in list">
<p ng-bind-html-unsafe="updateString(path)"></p>
</div>
</div>
</div>
below code working perfectly . but i need to convert it from foreach to for loop .please help me to iteration by for loop. because i have to introduce some if else statement in entire code
#foreach (var item in ViewData["productSl_1"] as IEnumerable<Nazmulkp.Models.Product>)
{
<div class="col-md-3 ">
<div class="thumbnail">
<img src="#Url.Content(item.ImagePath)" alt="Image" style="max-width:100%;">
<div class="caption">
<h5>#item.ProductName</h5>
<h4>Tk.#item.Price</h4>
</div>
</div>
</div>
}
Example:
I have to make sure if index number 2 than execute <div class="col-md-3>"
Thank you
You do not need to convert your foreach to a for loop for adding a conditional css class. Simply add a counter variable to your code and use that for your if condition as needed.
For example, the below code will add the css class "col-md3" to the 3rd item in your collection.
#{ var counter = 0;}
#foreach (var item in YouCollectionHere)
{
<div class="#(counter==2?"col-md-3":"")">
<h2>#item.Name</h2>
</div>
counter++;
}
I am trying to display images in a carousel on my Umbraco from a macro and I am using Library.GetMediaById(child.picture). Here is my code:
#foreach (var child in slider.Children.Where("Visible"))
{
var background = Library.MediaById(child.picture);
if (i == 0)
{
<div id="#i" class="item active" data-id="#i" style="background-image: url(#background.UmbracoFile)">
</div>
}
else
{
<div id="#i" class="item" data-id="#i" style="background-image: url(#background.UmbracoFile)">
</div>
}
i++;
}
However when I do this instead of getting <div id="1" class="item" data-id="1" style="background-image: url('/media/1007/slide-2.png')"></div> like I should I am getting a bunch of extra stuff:
<div id="1" class="item" data-id="1" style="background-image: url({src: '/media/1007/slide-2.png', crops: []})"></div>
How do I just get that media item url and not all the extra stuff?
BTW I am using Umbraco 7.4
If you are using Umbraco 7 or later, I strongly recommend using the new APIs to retrieve property values and content or media nodes, rather than using the Library.MediaById method which is now obsolete.
If you prefer a dynamic view model, you can use the DynamicPublishedContent API, which lets you write dynamic queries using the #CurrentPage model. So your example would be:
#foreach (var child in CurrentPage.AncestorOrSelf(1).Children.Where("isVisible"))
{
var backgroundPicture = Umbraco.Media(child.Picture); //assuming an image property on child
var backgroundPictureUrl = picture.Url;
// your markup here
}
If you instead prefer a strongly typed model, you can use the IPublishedContent API. Please bear in mind that your view must inherit from a suitable type, such as Umbraco.Web.Mvc.UmbracoTemplatePage.
#foreach (var child in Model.AncestorOrSelf(1).Children().Where(c => c.IsVisible))
{
var backgroundPicture = Umbraco.TypedMedia(child.GetPropertyValue<int>("picture");
var backgroupdPictureUrl = backgroundPicture.Url;
// your markup here
}
Also, from your example I'm suspecting that you may be using an image cropper property to store the background image, in which case the property will have a json (JObject) value rather than an int corresponding to the media Id.
In this case, the code retrieving the picture property needs to be adapted, have a look at the documentation for image cropper to see the different ways you can get the image url depending on whether you've specified crops and/or a focal point. As an example, if you're using the dynamic API and you're only interested in the image URL, use the following:
<div style="background-image: url('#child.picture.src')">
So the way I got around this was to, make a partial instead of a macro because partials inherit Umbraco.Web.Mvc.UmbracoTemplatePage so then I could use Umbraco.Media() instead. Here is the working code in case anyone comes across this and needs help:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
var root = Model.Content.AncestorOrSelf(1);
int i = 0;
}
<div class="container">
<div id="photo-carousel" class="carousel slide">
<div class="carousel-inner">
#foreach (var child in root.Children().Where("naviHide == true"))
{
if (child.DocumentTypeAlias.Equals("slider"))
{
foreach (var picture in child.Children.Where("naviHide != true"))
{
var background = Umbraco.Media(picture.GetPropertyValue("picture"));
if (i == 0)
{
<div id="#i" class="item active" data-id="#i" style="background-image: url('#background.Url')">
</div>
}
else
{
<div id="#i" class="item" data-id="#i" style="background-image: url('#background.Url')">
</div>
}
i++;
}
}
}
</div>
<a class="carousel-control left" href="#photo-carousel" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left"></span>
</a>
<a class="carousel-control right" href="#photo-carousel" data-slide="next">
<span class="glyphicon glyphicon-chevron-right"></span>
</a>
</div>
</div>