I have a very huge unordered list items in my masterpage. say it contains 60+ list items. depending on some condition i want hide that list items (hidden items could be 1 to 59 )
My Master File Code Snippet :
<li>Authorization
<ul>
<li><span>Card Request</span></li>
<li><span>Card Issue</span></li>
<li><span>Card Reload</span></li>
<li><span>Close Card</span></li>
<li><span>Card Replacement</span></li>
<li><span>Card Status Change</span></li>
<li><span>Upgrade/DownGrade</span></li>
</ul>
</li>
Condition : -
My DataTable returns values like
cardIssueAuth.aspx
Distributor.aspx
CardStatuschangeAuth.aspx
UpgradeDowngradeAuth.aspx
So i want to hide only those page which came in DataTable
I am aware of ID & runat attribute of <li> & then make it visible : false
But how can i use it efficiently/dynamically ? by using some for loop ...!!
I personally donot like the 'visibility' hack. You could selectively render the 'li' elements on the server-side itself (via code-behind or scriptlets) based on the entries on DataTable.
On the code-behind, you could have a static dictionary that contains all the link details, grouped by sections. Plus the filtering logic:
var sections = new List<Section>()
{
new Section()
{
Header = "Authorization",
SubLinkDetails = new Dictionary<string, string>()
{
{"NewCardGeneration.aspx", "Card Request"},
{"cardIssueAuth.aspx", "Card Issue"},
//.. and so on
}
}
//.. other sections follow
};
//filter subLinkDetails depending on the DataTable entries
sections.ForEach(s => s.SubLinkDetails.RemoveWhere(k => DataTable.Contains(k)));
Here, the Section is a convenience class and RemoveWhere is an extension method on IDictionary:
class Section
{
public string Header { get; set; }
public IDictionary<string,string> SubLinkDetails { get; set; }
}
public static class IDictionaryX
{
public static void RemoveWhere<K,V>(this IDictionary<K,V> dictionary, Predicate<K> condition)
{
IEnumerable<K> keysToRemove = dictionary.Keys.Where(k => condition(k));
foreach (var k in keysToRemove)
{
dictionary.Remove(k);
}
}
}
In your aspx, access the sections and render the ul/li elements:
<%foreach (var section in sections)
{%>
<li><%=section.Header %>
<%foreach (var filteredLink in section.SubLinkDetails)
{%>
<li><span>"<%= filteredLink.Value>"</span></li>
<%}%>
</li>
<%}%>
Provide id to your <li> according to page names.
And then,
You can do this by
if (listItem.selectedItem == 'pagename.aspx')
this.hide.style.Add("display", "none");
Related
I have a model with 34 numbered properties in it as shown below
Public Class ViewModel
{
public string RatingCategory01 { get; set; }
public string RatingCategory02 { get; set; }
public string RatingCategory03 { get; set; }
//...and so on until category #34
}
Rather than code an input for each category in Razor Pages, I would like to use a loop to iterate through all the categories and generate the appropriate control groups. I have tried the code below:
<tbody>
#for (var i = 1; i < 35; i++)
{
string n;
#if (i > 0 && i < 10)
{
n = "RatingCategory0" + i.ToString();
}
else
{
n = "RatingCateogry" + i.ToString();
}
<tr>
<td>
<label asp-for="#string.Format("RatingCategory" + n)" class="control-label"></label>
</td>
<td>
<select asp-for="#string.Format("RatingCategory" + n)" asp-items="Model.CategoryRatingSelectList">
<option value="">Select</option>
</select>
</td>
<td>
<input asp-for="#string.Format("RemedialTime" + n)" class="form-control" />
</td>
</tr>
}
</tbody>
When I build the project and navigate to the page, I get this error:
InvalidOperationException: Templates can be used only with field
access, property access, single-dimension array index, or
single-parameter custom indexer expressions.
I'm not sure if I am on the right track here. I would really like to create a loop to generate these inputs so make future maintenance and changes easier. It's probably pretty obvious from my code/question that I am pretty new to this, so any help is appreciated.
EDIT TO ADD SOLUTION:
I used the solution provided by Ed Plunkett which I have checked below. I altered it a bit and ended up creating a new class called 'Rating' because I found that in practice I needed a more complex object. Inside my view is now
public List<Rating> Ratings = { get; set; }
In the controller, I use a loop to add as many empty ratings as I need to the list depending on the number I need.
for (var i = 0; i < 34; i++)
{
vm.Ratings.Add(new Rating());
}
Though this will likely be updated to use something other than a hard-coded number as the application evolves.
Finally, I used a loop in the view to create a group of controls for every Rating in my List. In this case it is a TableRow containing different controls in different columns:
#for (var i = 0; i < Model.Ratings.Count; i++)
{
<tr>
<td>
#Html.DisplayFor(model => model.Ratings[i].Category)
</td>
<td>
<div class="form-group">
<select asp-for="Ratings[i].RatingValue" asp-items="Model.CategoryRatingSelectList">
<option value="">Select</option>
</select>
</div>
</td>
<td>
<input asp-for="Ratings[i].RemediationMinutes" class="form-control" />
</td>
</tr>
}
I've found that the data in this group of inputs can be bound as a List by simply including
List<Rating> Ratings
in the parameters on whichever method runs when the form is submitted.
This is what you want instead of those 34 properties and their implied 34 RemedialTime siblings:
public List<String> RatingCategory { get; set; } = new List<String>();
public List<String> RemedialTime { get; set; } = new List<String>();
If you have 34 of something and the names differ only by an index number, that's a collection, not 34 distinct properties with sequentially numbered names. Then you can enumerate the 34 items with a foreach loop, or index them individually as RatingCategory[0] through RatingCategory[33]. In C#, collection indexes start at zero, so the first one is 0 and the thirty-fourth one is 33. You get used to it.
You should also look up what String.Format() does. String.Format("Foo" + 1) is exactly the same as "Foo" + 1.
You could convert your model class to dictionary;
var viewModel = new ViewModel()
{
RatingCategory01 = "a",
RatingCategory02 = "b",
RatingCategory03 = "c"
};
var dictionaryModel = viewModel.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.ToDictionary(prop => prop.Name, prop => prop.GetValue(viewModel, null));
Then you can iterate the dictionary in the view.
I have a panel that is created and filled via a vacancy page I have created. Im doing it as follows:
#{
var root = CurrentPage.AncestorOrSelf(1);
var newsNode = root.Descendants("News").First();
var vacanciesNode = root.Descendants("Vacancies").First();
string shortenedSummary = string.Empty;
}
<ul>
#foreach (var vacancyItem in vacanciesNode.Descendants("Vacancy").Take(3).OrderBy("postDate desc"))
{
<p>here we are 2</p>
#vacanciesNode.Count().ToString()
<li>
<h4>#vacancyItem.jobTitle</h4> <span>Posted on #vacancyItem.postDate.ToString("dd/MM/yyyy")</span>
<p>
#if (vacancyItem.jobSummary.Length <= 182)
{
#vacancyItem.jobSummary
}
else
{
shortenedSummary = vacancyItem.jobSummary.Substring(0, 182) + "...";
#shortenedSummary
}
</p>
Read More..
</li>
}
</ul>
However, when there are no vacancy items, my list is empty. Should this be the case, I'm wanting it to read "sorry no vacancies just now" but I don't know how to check if my vacanciesNode has any items in it.
Could someone show me how I could achieve this?
Since the .Descendants() method returns a DynamicContentList (a collection) you can simply do a .Count() on the collection and check whether it's more than or equal to 1.
If there's more than 0 items in the collection, it's not empty.
So, what you need to do is surround your #foreach with an #if statement which checks on this, and an else statement after that prints whatever html you want to show if there's no vacancies
#if( vacanciesNode.Descendants("Vacancy").Take(3).OrderBy("postDate desc").Count() > 0) {
//Do foreach
}
else
{
//Write message about missing vacancies
}
I have news posts within a news page within a homepage on my content structure
Example:
Homepage
- News
-- News Posts
I'm looking to have some of the news feed on my homepage in a foreach statement. In my head it should be as simple as:
#foreach (var homenews in CurrentPage.Children.Children)
{
if (homenews.Name == "News Post")
{
//Do some stuff//
}
}
Obviously that doesn't work so has anybody got any ideas? Thanks
When you're walking the tree you have to remember that a property (or method) like Children or Descendants() will return a collection of objects, so you can't just call Children of a collection. You can only call Children on a single object.
You can find the correct child of the homepage by using something like var newsPage = CurrentPage.Children.Where(x => x.DocumentTypeAlias == "NewsListingPage") and then extract the children of that page.
I ended up getting the news page by its id and then getting it's children from there. The below code worked for me. Thanks guys.
#{
var node = Umbraco.Content(1094);
<p>#node.Id</p> // output is 1094
foreach (var item in node.Children.Where("Visible").Take(3))
{
<p>#item.exampleText</p>
}
}
You need to reference the required node by NodeTypeAlias of it's Document Type.
So assuming the alias of the DocType of your News Posts is “NewsPosts” then...
#foreach (var homenews in #Model.Descendants().Where("NodeTypeAlias == \"NewsPosts\"")).Take(3)
{
<p>#homenews.Name<p>
}
...should return the name of the first 3 news posts.
I had the exact scenario, this is how I got mine working. NodeByID was very useful nad -1 indicates the root
#foreach(var item in Model.NodeById(-1).Children)
{
string itemName = item.Name;
switch(itemName)
{
case "News":
#* News *#
<div id="News">
<h3>#item.Name</h3>
#foreach (var newsPost in item.Children.OrderBy("UpdateDate desc").Take(4).Items)
{
<p>
#newsPost.Title
</p>
}
</div>
}
}
After I run my query I have got the result into a dataTable as the following (this is only a simplified resultset):
food_cat food
-----------------------
vegit carrot
vegit onion
vegit tomato
fruit cherry
fruit banana
fruit orange
I want to list that result grouped by food_cat in an unordered list.
<h3> Vegit </h3>
<ul>
<li>carrot</li>
<li>onion</li>
<li>tomato</ti>
</ul>
<h3>fruit</h3>
<ul>
<li>cherry</li>
<li>banana</li>
<li>orange</li>
</ul>
I have tried some for, if, while controls but could not find a good solution.
since you don't provide table names etc. i will try to give the answer in general way.
string previous_food_cat = '';
bool firstEntrance = true
while(trace resultSet until no elements left)
{
if resultSet.food_cat != previous_food_cat //check if food_cat value changed
{
if (!firstEntrance) //if not first entrance close the <ul> tag before opening new one
{
print </ul>
}
print <h3> resultSet.food_cat </h3> //open new <h3> tag
print <ul> //open new <ul> tag
previous_food_cat = resultSet.food_cat //update previous_food_cat for new food_cat value
firstEntrance = false //set firstEntrance false so that ul tqags should be closed
}
print <li> resultSet.food </li>
}
Thank you #zibidyum, your technic obtained me to reach a solution. But final solution is here:
public bool firstcat; // defined at before Page_Load method
public int temp_cat; // defined at before Page_Load method
for (int i = 0; i < dt.Rows.Count; i++)
{
if (temp_cat != Convert.ToInt32(dt.Rows[i]["food_cat"]))
{
if (i > 0 && !firstcat)
content.InnerHtml += "</ul>"; //solution's most critic point is here
content.InnerHtml += "<ul>"
}
content.InnerHtml += String.Format("<li>{0}</li>", dt.Rows[i]["food"].ToString());
temp_cat = Convert.ToInt32(dt.Rows[i]["food_cat"]);
}
Any better solutions, suggestions and/or ideas are welcome.
I have method that is returning IQuerable given below:
internal IQueryable<TradeLeads> GetLeadsByCategory(int categoryId)
{
return
_context.BuySells.Where(bs => bs.CategoryId == categoryId).OrderBy(bs => bs.CreationDate).Select(
bs => new TradeLeads
{
Id = bs.Id,
BuySellTypeId = Convert.ToInt32(bs.BuySellTypeId.ToString()) ,
Description = bs.Description,
Flag = bs.Company.Country1.Flag,
MembershipType = bs.Company.MembershipType,
IsUsingSmsNotifications = bs.Company.IsUsingSMSNotifications,
IsVerified = bs.Company.IsVerified,
ProductImagePath = bs.ProductImagePath,
ProductName = bs.ProductName,
CompanyName = bs.Company.CompanyName,
CompanyId = Convert.ToInt32(bs.CompanyId.ToString()),
PostedDate = bs.CreationDate
});
}
All fields are having values. I am binding BuySellTypeId in the header template of the repeater control. ASPX is given below, which is in Usercontrol.
<HeaderTemplate>
<div class="grdheader">
<div class="hdrText">
<h3 id="h3TypeName">
</h3> <asp:HiddenField runat="server" ID="HidTypeId" Value='<%#Eval("BuySellTypeId") %>'/>
</div>
<div class="hdrMore">
<a href='<%#string.Format("ViewAll.aspx?t={0}",Eval("BuySellTypeId"))%>'>
<img src="cdn/images/more.png" />
View More </a>
</div>
</div>
</HeaderTemplate>
I am binding repeater from its parent page something like this. First I changed the protection level of the repeater from protected to public, so that I can access it from any where, without casting or finding from parent page.
private void BindAllBuyAndSellLeads(int categoryId)
{
var repo = new LeadsRepository();
var list = repo.GetLeadsByCategory(categoryId);
BindGrid(1, Leads1.LeadsGrid, list);
BindGrid(2, Leads2.LeadsGrid, list);
}
private static void BindGrid(int leadTypeId, Repeater gv, IQueryable<Core.Helper.TradeLeads> list)
{
var query = (from p in list
where p.BuySellTypeId == leadTypeId
select p).ToList();
Common.BindGrid(query, gv);
}
here Leads1 and Leads2 are the user control Leads.ascx. That is same usercontrol is placed at two places on page. But i am getting empty while binding. Please help , where and what i am doing wrong.
Binding in a header will never work. Binding is for ItemTemplate only; you can programmably set the value in the header, but realize the repeater is for binding multiple rows of data; there are multiple items, but only one header. Which BuySellTypeId should be used? The first one? The last one? There is no way for the repeater to tell, so you would have to programmably set the value.