I have the following code right now to write a flat list of items with a link to a controller action:
<ul>
#foreach (var item in items)
{
<li>
<a asp-controller="Home" asp-action="Demo" asp-route-itemName="#item.Name">
#item.Name
</a>
</li>
}
</ul>
Now this must become recursive. Items can also contain subitems. For recursion I need some sort of function. I know I could use #functions and define the function in the .cshtml file. Not sure whether such nice inline HTML code with tag helpers would still be allowed there, it didn't seem so. Another option is HTML helpers in a .cs file, no inline HTML here for sure. #helper doesn't seem to be available anymore.
What other options do I have to define a function and keep the inline HTML syntax that Razor offers?
Put the code for rendering a comment inside a partial view, and render it with a call to #Html.Partial("comment", comment).
Then within that comment partial view you'd have something like
#model Comment
Title: #Model.Title
Message: #Model.Message
#if (Model.ChildComments.Any())
{
<ul>
#foreach (var childComment in Model.ChildComments)
{
<li>
#Html.Partial("comment", childComment)
</li>
}
</ul>
}
This will render each comment, plus all its children (if any), recursively.
Related
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'm having a problem and i'm really puzzled by it.
My markup is simple enough:
#foreach (var item in Model.Items)
{
<a class="mapIconUnit" id="pinDelete-#item.PinGuid.ToString()">
#Url.Action("DeletePin") <!-- testing purposes -->
#(Ajax.ActionLink("x", "DeletePin", MapAdministrationController.Routes.DeletePin(item.PinGuid), new AjaxOptions()
{
OnSuccess = "onMapPinDeleted",
Confirm = Resources.Resource.msg_GenericDeleteConfirmationQuestion
}
))
</a>
}
Now what i would expect to render from this is:
<a class="mapIconUnit" id="...">
... rendered url
<a href="..." etc>x</a>
</a>
But what i am getting is:
<a class="mapIconUnit" id="...">
... rendered url
</a>
<a href="..." etc>x</a>
What am i doing wrong here? The markup is too simple for it to be wrong to cause such a thing?
It's illegal to nest an anchor element inside another anchor element, more info can be found in the W3C specs: http://www.w3.org/TR/html401/struct/links.html#h-12.2.2
Links and anchors defined by the A element must not be nested; an A element must not contain any other A elements.
So either razor or the webbrowser renders the elements correctly (i.e. place them next to each other).
I am using Meteor 0.7.0.1 with Spark and I have the following template that used to popover content, and the template is inserted at each post items.
<template name="postLinks">
<a href="#" id="ui-popover-container-{{id}}" class="popover-list-trigger dropdown-toggle" data-toggle="dropdown">
<i class="icon-calendar"></i>
</a>
{{#if poppedUp}}
<div id="link-popover-wrapper" style="display:none">
<ul class="link-popover">
{{#each linkOptions}}
<li><a tabindex="-1" class="link-action" id="link-{{value}}" href="#">{{label}}</a>
</li>
{{/each}}
</ul>
</div>
{{/if}}
</template>
I want to print the div "link-popover-wrapper" only once the first time the template loaded. Subsequent template inserts in post items the div won't inserted so only one hidden div is in the page. I handled it using a template variable in following way.
var _poppeUp = true;
Template.postLiks.helpers({
poppeUp : function () {
if (_poppeUp) {
_poppeUp = false;
return true;
}
return false;
}
});
It is working for the first time the page is loaded and only one instance of the div "link-popover-wrapper" is in the page. The problem is when a post is updated and page is re-rendered the template variable still set to false and the div is not printed in the page. I wanted to reset that template variable when a page is re-rendered.
Is there a way to overcome this issue in Meteor ?
Just add code that does the opposite on page render:
Template.postLinks.rendered = function() {
if (!_poppedUp)
_poppedUp = true;
You didn't ask, but this strikes me as an odd way to implement this. Rather than {{#if poppeUp}}, why not {{#with getFirstLinkPopoverWrapper}} and then define a getFirstLinkPopoverWrapper helper that returns just the one link-popover-wrapper div that you want shown? Then you never need to worry about re-renders or tracking some variable.
EDIT: Alternate implementation:
You could simply use jQuery to tell you whether or not an element of id link-popover-wrapper already exists on the page:
Template.postLinks.helpers({
poppedUp: function() {
return $("#link-popover-wrapper").length !== 0;
}
}
That's it. If no such element exists, the length of the jQuery object is zero and the statement evaluates as false and false is returned. Otherwise true is returned and your template adds the element.
I've got a situation where I'm rendering a Handlebars partial recursively based on a Mongodb tree structure, something like this :
<template name='menu'>
<ul class='menu'>
{{#each topLevelChildren}}
{{>menu-item}}
{{/each}}
</ul>
</template>
<template name='menu-item'>
<li>{{name}}
{{#if listChildren.count}}
<ul>
{{#each listChildren}}
{{>menu-item}}
{{/each}}
</ul>
{{/if}}
</li>
</template>
where the mongodb docs look like this :
{ _id : ObjectId("525eb7245359090f41b65106"),
name : 'Foo',
children : [ ObjectId("525eb60c5359090f41b65104"), ObjectId("525eb6ca5359090f41b65105") ]
}
and listChildren just returns a cursor containing the full docs for each element in the children array of the parent.
I want to do a bit of jquery makeup on the rendered tree, but I can't seem to hook into the 'rendered' event for the entire tree, something like
Template.menu-completed.rendered = function(){
// console.log('done!');
}
Trying this
Template.menu.rendered = function(){
console.log($('menu li'));
}
Not only doesn't this return the right results (brackets filled with commas), it also freezes web inspector (but not the app...).
Any help would be much appreciated !
This is an interesting problem :)
In the current version of Meteor (0.7.x) the rendered callback is called once when the template is first rendered, and then again once any partial inside the template is re-rendered. (This behavior will change when Meteor 0.8 - the shark branch or "Meteor UI" - lands.)
I've never tried doing things recursively, but it's going to be very hard to tell which callback you get is actually for the parent. One thing you might want to try is to start the recursive rendering in a template called menu-parent or something. That way, when the rendered callback is called for the first time (and as long as your data is loaded), you know the whole tree is rendered.
However, I suspect there might be a better way to do this. Generally, you should not be using jQuery to modify classes and attributes on DOM elements, as you'll confuse yourself as to what Meteor is doing and what jQuery is doing. Can you elaborate on what you are trying to achieve with the following, and I'll update my answer?
I need to find a specific anchor tag within the fully rendered template and add a class to it... and then add a class to each of its parents
Petrov, your code actually works for me, with just some minor modification to the jquery part. But perhaps I'm misunderstanding. Here is what I did:
Created a brand new project.
Added your template in the .html with minor change: <li><span class="name">{{name}}</span>
Created a new collection List.
Template.menu.topLevelChildren = function() { return List.find(); };
Then I added this handler:
Template.menu.rendered = function(e){
$('.name').each(function(i, e) {
console.log(e);
});
}
Now, upon render, for instance when new data is inserted into the List collection, I get a printout on the console like this:
<span class="name">second</span>
<span class="name">second2</span>
<span class="name">second21</span>
<span class="name">second22</span>
<span class="name">third</span>
<span class="name">third2</span>
<span class="name">third21</span>
<span class="name">third22</span>
<span class="name">third</span>
<span class="name">third2</span>
<span class="name">third21</span>
<span class="name">third22</span>
This is just a flat list of the tree elements I had added (where second2 is nested in second, and second2x is nested in second2, etc.). So from what I understand you could just take the selection coming back from jquery and find the tag you are looking for and change its class, as you wanted. Please let me know if I misunderstood something.
Complete code below.
test.html:
<head>
<title>test</title>
</head>
<body>
{{> menu }}
</body>
<template name='menu'>
<ul class='menu'>
{{#each topLevelChildren}}
{{>menu-item}}
{{/each}}
</ul>
</template>
<template name='menu-item'>
<li><span class="name">{{name}}</span>
<ul>
{{#each children}}
{{>menu-item}}
{{/each}}
</ul>
</li>
</template>
test.js:
List = new Meteor.Collection("List");
if (Meteor.isClient) {
Template.menu.topLevelChildren = function() {
return List.find();
};
Template.menu.rendered = function(e){
$('.name').each(function(i, e) {
console.log(e);
});
}
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
I have the css layout: One column fixed width layout, from maxdesign.com
I have two menu items defined like the following:
<div id="navigation">
<ul>
<li>Data Entry</li>
<li>Reports</li>
</ul>
</div>
Now, let's say that I have two roles: guest and operator, and I want that for example if a user with role guest is logged in, then just the Report item from the menu appear, and in case of a user operator is logged in, then both options appear.
How can I accomplish that?
EDIT:
Based on your responses, I'll go with the server side logic to deal with this:
<div id="navigation">
<ul>
<li><asp:LinkButton ID="lkbDataEntry" runat="server">Data Entry</asp:LinkButton></li>
<li><asp:LinkButton ID="lkbReports" runat="server">Reports</asp:LinkButton></li>
</ul>
</div>
Thanks!
You can put this in the Page_Load..
Dim cs As ClientScriptManager = Page.ClientScript
If Not cs.IsClientScriptBlockRegistered(Me.GetType(), "RoleVariable") Then
Dim js As New String
js = "var _role = " & role & ";"
cs.RegisterStartupScript(Me.GetType(), "RoleVariable", js, True)
End If
And from there, you will have the role in the Javascript realm, where you can manipulate the visibility of the items you want.
So...
<script type="text/javascript">
function hideStuff() {
if (_role === "operator") {
// hide/show your elements here
}
else if (_role === "guest") {
// hide/show your elements here
}
}
</script>
Keep in mind that this approach is all client-side and is therefore easy for another developer to manipulate if they really wanted to. But on the other hand, it's the simplest. Don't use this approach for high-security situations.
You could give your menu elements an ID attribute and then in your codebehind either use RegisterClientSideScriptBlock or use Response.Write to send JavaScript to the client to hide (or show) elements based on some condition.
how about something simple like?
<% if(Page.User.IsInRole("operator") || Page.User.IsInRole("guest")) { %>
<div id="navigation">
<ul>
<% if(Page.User.IsInRole("operator")) { %>
<li>Data Entry</li>
<% } %>
<li>Reports</li>
</ul>
</div>
<% } %>
I don't 100% think you can (or should) be doing logic operations with stylesheets. You may need to have some javascript and then decide based on guest or operator which style to display