Element targeting where onclick has occurred in Blazor - c#

I have a HTML section with multiple list elements as such:
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown #DropdownClass" #onclick="dropMenu">
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown #DropdownClass" #onclick="dropMenu">
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown #DropdownClass" #onclick="dropMenu">
The goal is that when one of the elements is clicked on, a new class c-show is added which then show the submenu's underneath.
My code in blazor to add this is:
#code{
bool MenuDroppedDown = true;
string DropdownClass => MenuDroppedDown ? "c-show" : "";
public async void dropMenu()
{
MenuDroppedDown = !MenuDroppedDown;
}
}
But the problem here is that once an element is clicked on, then c-show is added everywhere.
How can I have it so only the element where the onclick event occurred is affected?

Copy the code below into the Index component, add this amazing css class
.c-show {
background-color: red;
}
to the site.css located in the wwwroot folder, and run the code
<ul>
#foreach (var item in items)
{
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown #item.DropdownClass" #onclick="#(() => item.MenuDroppedDown = !item.MenuDroppedDown)" >click me</li>
}
</ul>
#code{
List<LiTag> items = Enumerable.Range(1, 10).Select(i => new LiTag { ID = i }).ToList();
public class LiTag
{
public int ID { get; set; }
public bool MenuDroppedDown { get; set; }
public string DropdownClass => MenuDroppedDown ? "c-show" : "";
}
}
Please don't hesitate to ask questions, as this code is rather self-explanatory, I did not bother to explain what I was doing.

Related

Getting error foreach statement cannot operate on variables of type 'xyz' because 'xyz' does not contain a public definition for 'GetEnumerator'

I have created a Model based on an EpiServer LinkItemCollection:
namespace ProjectX.Site.Models.Blocks
{
[SiteContentType(
GUID = "b9978bf2-f3da-4164-8fa2-3694c2ce0377",
AvailableInEditMode = false)]
[SiteImageUrl]
public class CustomTopNavigationItemtBlock : SiteBlockData
{
[CultureSpecific]
[MinItemCount(0)]
[MaxItemCount(2)]
[Display(Order = 10)]
public virtual LinkItemCollection CustomTopNavigationLinks { get; set; }
public override int WordCount
{
get => throw new System.NotImplementedException();
set => throw new System.NotImplementedException();
}
}
}
And I am trying to create a shared view for it:
#model CustomTopNavigationItemtBlock
#if (Model != null)
{
#foreach (var linkItem in Model)
{
<li>
<a class=""
href="#Url.PageUrl(linkItem.Href)"
target="#linkItem.Target"
title="#linkItem.Title"
tabindex="1"
data-toggle=""
role="button">
#linkItem.Text
</a>
</li>
}
}
Unfortunatley I don't understand what I am doing wrong.
Here, you are attempting to iterate over Model:
foreach (var linkItem in Model)
However, Model is of type CustomTopNavigationItemtBlock, which cannot be iterated using foreach, as it does not implement IEnumerable.
It seems that you are trying to loop over the CustomTopNavigationLinks property, which can be done like this:
foreach (var linkItem in Model.CustomTopNavigationLinks)

How to combine two Unordered list using for-each

I have two lists in my page which display similar data, i used for-each to display the list. I it possible to display in a single list
My first List
<ul>
#foreach (var item in rpInfo)
{
<li data-id="#item.ID">
#item.PartyName
<ul>
<li> #item.spName </li>
</ul>
</li>
}
My Second List
<ul>
#foreach (var dRPs in deletedRpHistoryInfo)
{
<li data-id="#dRPs.ID">
#dRPs.PartyName
<ul>
<li>#dRPs.spName</li>
</ul>
</li>
}
</ul>
I need your help creating a list by combining the the two?
Thanks!
At its simplest you don't need two lists, you can simply have the two foreach statements inside the one list...
<ul>
foreach(...){
<li>....</li>
}
foreach(...){
<li>....</li>
}
</ul>
As an aside I'm not sure what value you get embedding the second ul inside the li either.
I take it your datatypes are similar but not identical, right? Do they inherit from the same parent - or is it maybe worth making them inherit from same parent(if it brings any other benefits). Then you can create new temporary list, typed with the common parent, and merge in both lists.
When foreaching a list with different datatypes in, when accessing a non-common datamember, you need to check what type the current item is.
Classes:
abstract internal class EmergencyEmployees
{
internal string Name { get; set; }
internal int Age { get; set; }
}
internal class Police : EmergencyEmployees
{
internal int PoliceLicence { get; set; }
}
internal class Firestarter : EmergencyEmployees
{
internal int FirestarterLicence { get; set; }
}
Program:
class Program
{
static void Main(string[] args)
{
var officers = new List<Police>();
var firestarters = new List<Firestarter>();
officers.Add(new Police { Name = "Kjell", Age = 45, PoliceLicence = 12345 });
officers.Add(new Police { Name = "Linda", Age = 42, PoliceLicence = 54321 });
firestarters.Add(new Firestarter { Name = "Arne", Age = 32, FirestarterLicence = 4322 });
firestarters.Add(new Firestarter { Name = "Anna", Age = 35, FirestarterLicence = 3322 });
var templist = new List<EmergencyEmployees>();
templist.AddRange(officers);
templist.AddRange(firestarters);
foreach(var ee in templist)
{
Console.WriteLine("Name: " + ee.Name);
Console.WriteLine("Age: " + ee.Age.ToString());
if(ee is Police)
{
var p = ee as Police;
Console.WriteLine("Police Licence: " + p.PoliceLicence.ToString());
}
if (ee is Firestarter)
{
var p = ee as Firestarter;
Console.WriteLine("Firestarter Licence: " + p.FirestarterLicence.ToString());
}
Console.WriteLine("--------------------");
}
}
}
Output:
Here is an article showing several ways that you can do this.
Given your example you could use the .Concat() as that way you keep the original data untouched and keep this all in the foreach loop. This will only work if the type used in both Lists are the same:
#foreach (var item in rpInfo.Concat(deletedRpHistoryInfo))
{
// do stuff
}

Lamba expression in a razor view

Can somebody help me on the Lambda expression.
My ModelVM looks like :
namespace MReports.Models
{
public class FullDetailVM
{
public FullDetailVM()
{
DetailSet = new List<FullDetailSet>();
}
........
public List<FullDetailSet> DetailSet { get; set; }
}
public class FullDetailSet
{
public FullDetailSet(){ }
public string Mnum { get; set; }
public string Label { get; set; }
public string LabelValue { get; set; }
}
}
Data in the above model will be :
DetailSet[0] = {1,"MCity","LosAngeles"}
DetailSet[0] = {1,"MState","California"}
DetailSet[0] = {1,"MZip","90045"}
DetailSet[0] = {1,"MStreet","Cardiff"}
DetailSet[0] = {1,"MHouse No","1234"}
DetailSet[0] = {1,"MApt","1"}
View(Razor) :
#model MReports.Models.FullDetailVM
#if(Model != null)
{
<div class="row contentHeaderInfo">
<ul class="list-inline">
<li> City :
</li>
<li>
//Display LabelValue corresponding to Mcity
Model.DetailSet.select(LabelValue).Where(Label== "Mcity");
</li>
<li> State:
</li>
<li>
//Display LabelValue corresponding to MState
Model.DetailSet.select(LabelValue).Where(Label== "MState");
</li>
</ul>
</div>
}
Model.DetailSet.Where(x=>x.Label == "Mcity").Select(x=>x.LabelValue)
or if you have just one recors Label == Mcity
Model.DetailSet.SingleOrDefault(x=>x.Label == "Mcity").LabelValue
Lambda expressions can be used to create delegate types. I've found the easiest way to explain this to someone is to show them a list of items, such as your List<FullDetailSet> DetailSet, and ask them what items from that list do you want based on a specific condition?
If you wanted all the items with label "Dog" you would do something like this:
Model.DetailSet.Where(d => d.Label == "Dog").Select(d => d.Value);
This will go over the items in DetailSet and check if each item has a Label of "Dog". For lack of a better understanding on the correct terminology, you are iterating over that list and grabbing what you need based on your conditions. This is why I used d as the placeholder, to me it looks as though d is a singluar representation of DetailSet.
If you needed only one record from that DetailSet you would use Single over Where.
Model.DetailSet.Single(d => d.Label == "Dog").Select(d => d.Value);
If you didn't need just the Value of those records that met your conditions you can grab the entire list like this:
Model.DetailSet.Where(d => d.Label == "Dog").ToList();
You need to select one record using Single, then just get the property.
Model.DetailSet.Single(m => m.Label == "MState").LabelValue

How to save nested sortable menus in database in mvc 4 razor?

How to save nested sortable menus in database i had followed this http://www.mikesdotnetting.com/Article/219/Saving-jQuery-Sortables-In-ASP.NET-Razor-Web-Pages
i have searched in google but no help...i want to know what will be the logic and how i can update with nested menus structure
-My Database structure
Table1:Menus
[Columns] Menuid, MenuName, Parent, orderId
Table2:MenuItems
[columns]:Menuitemid, MenuitemName, Menuid(foreignkey), orderId
My Razor View
<div>
<input type="button" id="getTech" style="width:100px;height:30px" value="Get Order" />
<div>
<ol class="example">
#if (ViewData["Menu"] != null)
{
foreach (var menu in ViewData["Menuitem"] as List<DB.Entities.MenuItem>)
{
<li class="item" id="#menu.MenuItemId">
#menu.MenuItemName
<ul></ul>
</li>
}
}
</ol>
</div>
</div>
<div class="row">
<div class="col-md-2">
<button>#Html.ActionLink("Save Order", "Index")"</button>
</div>
</div>
<script>
$(function () {
$("ol.example").sortable();
$('button').on('click', function() {
var ids = [];
$('.item').each(function(index, value) {
var id = $(value).prop('id');
ids.push(id);
});
$.post(
'/Sort',
{ Ids: JSON.stringify(ids) },
function(response) {
if ($('#message').hasClass('alert-info')) {
$('#message').text('Items re-ordered successfully.')
.removeClass('alert-info')
.addClass('alert-success');
} else {
$('#message').effect("highlight", {}, 2000);
}
}
);
});
})
My Controller Action
public ActionResult Index()
{
try
{
string menu = (Request["ids"]);
var ids = new JavaScriptSerializer().DeserializeObject(menu) ;
}
catch (Exception ex)
{
}
return View();
}
Below is a example of what I was suggesting in my comments.
It does not include the actual menu logic/plugin as that is assumed to be provided already. It is pretty much everything you would need to add to a fresh MVC application step-by-step. If it is missing anything you need just ask. I will post this as a tutorial on my website at some point.
1. MenuItem class/table
You only need a single table to hold both menu items and sub menus. The only difference is whether they actually have any child items. Menu items are just are id's and text (you could add hyperlinks etc if you wanted).
Using Code-first I added the MenuItems table with this class:
public class MenuItem
{
// Unique id of menu item
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int MenuItemId { get; set; }
// Tesxt to display on menu item
[Required]
public virtual string MenuItemName { get; set; }
// Sequential display order (within parent item)
public virtual int DisplayOrder { get; set; }
// Foreign key value
public virtual int? ParentMenuItemId { get; set; }
[ForeignKey("ParentMenuItemId")]
// Parent menu item
public virtual MenuItem ParentMenuItem { get; set; }
// Child menu items
public virtual ICollection<MenuItem> ChildItems { get; set; }
}
2. Add reorder action
This is the one that actually reorders the items. It is called via Ajax on the page using a URL like /menu/reorder/2?after=3 which will move item id 2 to after item id 3 in the database.
In the first version of my reordering I used to pass a position, item id and parent id, but found in production, due to complex parent relationships, that was not as useful as simply saying "which item id you want to move", and "which item id you want it placed after" (0 meaning place it first).
/// <summary>
/// Reorder the passed element , so that it appears after the specific element
/// </summary>
/// <param name="id">Id of element to move</param>
/// <param name="after">Id of element to place after (or 0 to place first)</param>
/// <returns>Unused</returns>
public ContentResult Reorder( int id, int after )
{
var movedItem = this.context.MenuItem.Find(id);
// Find all the records that have the same parent as our moved item
var items = this.context.MenuItem.Where(x => x.ParentMenuItemId == movedItem.ParentMenuItemId).OrderBy(x => x.DisplayOrder);
// Where to insert the moved item
int insertionIndex = 1;
// Display order starts at 1
int displayOrder = 1;
// Iterate all the records in sequence, skip the insertion value and update the display order
foreach (var item in items)
{
// Skip the one to move as we will find it's position
if (item.MenuItemId != id)
{
// Update the position
item.DisplayOrder = displayOrder;
if (item.MenuItemId == after)
{
// Skip the insertion point for subsequent entries
displayOrder++;
// This is where we will insert the moved item
insertionIndex = displayOrder;
}
displayOrder++;
}
}
// Now update the moved item
movedItem.DisplayOrder = insertionIndex;
this.context.SaveChanges();
return Content(insertionIndex.ToString());
}
3. View holding the top level menu
Index Action
//
// GET: /Menu/
public ActionResult Index()
{
// Return the top level menu item
var rootMenu = context.MenuItem.SingleOrDefault(x => x.ParentMenuItemId == null);
return View(rootMenu);
}
Index.cshtml
This contains the code for the sortable reordering via an Ajax call.
#model jQuery.Vero.Menu.MvcApplication.Models.MenuItem
<h2>Test Menu gets rendered below</h2>
<ul id="menu" class="menu">
#foreach (var menuItem in Model.ChildItems.OrderBy(x=>x.DisplayOrder))
{
#Html.Action("Menu", new { id = menuItem.MenuItemId })
}
</ul>
#section scripts{
<script type="text/javascript">
$(function () {
var $menu = $("#menu");
// Connection menu plugin here
...
// Now connect sortable to the items
$menu.sortable({
update: function(event, ui)
{
var $item = $(ui.item);
var $itemBefore = $item.prev();
var afterId = 0;
if ($itemBefore.length)
{
afterId = $itemBefore.data('id');
}
var itemId = $item.data('id');
$item.addClass('busy');
$.ajax({
cache: false,
url: "/menu/reorder/" + itemId + "?after=" + afterId,
complete: function() {
$item.removeClass('busy');
}
});
}
});
});
</script>
}
4. Recursive Partial View
To display the menu item, I use a recursive action and view.
Menu Action
/// <summary>
/// Render one level of a menu. The view will call this action for each child making this render recursively.
/// </summary>
/// <returns></returns>
public ActionResult Menu(int id)
{
var items = context.MenuItem.Find(id);
return PartialView(items);
}
Menu Partial View - Menu.cshtml
#model jQuery.Vero.Menu.MvcApplication.Models.MenuItem
<li id="Menu#(Model.MenuItemId)" class="menuItem" data-id="#(Model.MenuItemId)">
#Model.MenuItemName
#if (Model.ChildItems.Any())
{
<ul class="menu">
#foreach (var menuItem in Model.ChildItems.OrderBy(x => x.DisplayOrder))
{
#Html.Action("Menu", new { id = menuItem.MenuItemId })
}
</ul>
}
</li>
5. Additional Styles
These are the only styles I added to the test application. Note I add and remove a busy class on the dropped item so it can show progress etc while the Ajax call is being processed. I use a progress spinner in my own apps.
.menu {
list-style: none;
}
.menu .menuItem{
border: 1px solid grey;
display: block;
}
.menu .menuItem.busy{
background-color: green;
}
6. Sample data
This is an example of the hierarchical menu items I entered (produces screenshot below).
7. Onscreen example of hierarchy
This shows the hierarchy the above code renders. You would apply a menu plugin to the top level UL.

Access Unordered list control id's in codebehind

I have ul elements in usercontrol like below
<u><ul>
<li id="liMiddle" class="off">
<a href="#">One
</a>
<ul id="One" runat="server">
</ul>
</li>
<li id="liMiddle" class="off">
<a href="#">Two
</a>
<ul id="Two" runat="server">
</ul>
</li>
<li id="liMiddle" class="off">
<a href="#">Three
</a>
<ul id="Three" runat="server">
</ul>
foreach (SPWeb supersubsite in subsites)
{
int i = 0;
HtmlGenericControl li = new HtmlGenericControl("li");
CurrentTab.Controls.Add(li);
HtmlGenericControl anchor = new HtmlGenericControl("a");
anchor.Attributes.Add("href", supersubsite.Url);
anchor.InnerText = supersubsite.Title;
li.Controls.Add(anchor);
} </u>
Here in each loop Current tab should be changed to corresponding next ul id. How to access it?
I have to generate 'li' elements dynamically under above ul's. So I need to access all the 'ul' id's one by one in the codebehind.
Can anybody tell me the solution?
I would implement a collection in codebehind where you can add listitems by presenter/controller or in page_load/click_events/..
And then simple looping in an ASP.NET MVC style..
// YourPage.aspx.cs:
private readonly ICollection<string> items = new Collection<string> { "one", "two" };
// YourPage.aspx
<ul>
<% foreach (var stringItem in this.items) { %>
<li><%= stringItem %></li>
<% } %>
</ul>
You could make your own ListBuilder class with an Add method that takes a custom UserListItem class/struct. Inside of these classes you could use TagBuilder to create the LI and UL tags using the custom class/struct you built for the UserListItem. You could store a static dictionary of current Lists you're building in that custom ListBuilder class, using an user defined key.
That way if you needed to get at your List, or ListItems dynamically from the code behind, you could just use your ListBuilder and reference them by ID.
Code below is a bit rough, but here's the general idea:
using System.Collections.Generic;
using System.Web.Mvc;
using System;
public class UserList
{
public List<UserListItem> UserListItems = new List<UserListItem>();
public void Add(UserListItem item)
{
UserListItems.Add(item);
}
}
public class UserListItem
{
public string Name { get; set; }
}
public class ListBuilder
{
static Dictionary<string, UserList> userLists = new Dictionary<string, UserList>();
public ListBuilder(string listId)
{
UserList newList = new UserList();
newList.Add(new UserListItem() { Name = "Item1" });
newList.Add(new UserListItem() { Name = "Item2" });
userLists.Add(listId, newList);
}
public static UserList GetList(string listId)
{
return userLists[listId];
}
public static string BuildList(string listId)
{
UserList list = userLists[listId];
TagBuilder listTagBuilder = new TagBuilder("ul");
list.UserListItems.ForEach(listItem =>
{
TagBuilder listItemTagBuilder = new TagBuilder("li") { InnerHtml = listItem.Name };
listTagBuilder.InnerHtml += listItemTagBuilder.ToString(TagRenderMode.Normal);
});
return listTagBuilder.ToString(TagRenderMode.Normal);
}
}

Categories