NopCommerce Category Navigation Design - asp.net-mvc

I am trying to edit this category navigation view. What I need is this:
We do not want to see all main categories or all sub categories as well.
When I click a Main Category (ParentCategoryId = 0), I should see only this one and it's sub categories.
When I click one of this sub categories, this sub category should be main category, and this time I should see it's sub categories only.
For ex, navigation system works like this right now:
Women (selected)
Bag
Top
Dress
Sound Systems
Home Decoration
Gardening
or
Women
Bag
Top (selected)
Tshirt
Sweatshirt
Tunic
Dress
Jeans
Suit
Sound Systems
Home Decoration
Gardening
What we want is that:
Women (selected)
Bag
Top
Dress
Jeans
Suit
or
Top (selected)
Tshirt
Sweatshirt
Tunic
#model CategoryNavigationModel
#using Nop.Web.Models.Catalog;
#using Nop.Core.Infrastructure;
#functions{
public bool BreadCrumbContainsCurrentCategoryId(CategorySimpleModel category)
{
if (Model.CurrentCategoryId == 0)
return false;
if (category.Id == Model.CurrentCategoryId)
return true;
foreach (var subCategory in category.SubCategories)
{
if (BreadCrumbContainsCurrentCategoryId(subCategory))
{
return true;
}
}
return false;
}
}
#helper RenderCategoryLine(CategorySimpleModel category)
{
var _categoryService = EngineContext.Current.Resolve<Nop.Services.Catalog.ICategoryService>();
// additional check on whether to set an active class on the parents of the current categories and above. Meaning all categories from the
// breadcrumb will have an "active" class
bool active = false;
if (category.Id == Model.CurrentCategoryId || category.SubCategories.Count(BreadCrumbContainsCurrentCategoryId) > 0)
{
active = true;
}
<li class="#(active ? "active" : "inactive")">
#if (category.ParentCategoryId == Model.CurrentCategoryId || category.Id == Model.CurrentCategoryId)
{
<a href="#Url.RouteUrl("Category", new { SeName = category.SeName })">#category.Name
#if (category.NumberOfProducts.HasValue)
{
<text> </text>#T("Categories.TotalProducts", category.NumberOfProducts.Value)
}
</a>
}
#{
if (category.Id == Model.CurrentCategoryId ||
category.SubCategories.Count(BreadCrumbContainsCurrentCategoryId) > 0)
{
if (category.SubCategories.Count > 0)
{
<ul class="sublist">
#foreach (var subCategory in category.SubCategories)
{
#RenderCategoryLine(subCategory)
}
</ul>
}
}
}
</li>
}
#if (Model.Categories.Count > 0)
{
<div class="block block-category-navigation">
<div class="title">
#if (Model.CurrentCategoryId == 1 || Model.CurrentCategoryId == 3 || Model.CurrentCategoryId == 4 || Model.CurrentCategoryId == 5)
{
<strong>#T("Categories")</strong>
}
else
{
<strong><< Geri Dön</strong>
}
</div>
<div class="listbox">
<ul class="list">
#foreach (var category in Model.Categories)
{
#RenderCategoryLine(category)
}
</ul>
</div>
</div>
}
My code is something like this, but there is a problem. Code is not working when selected category has no sub categories. On this situation it must do something like this:
PS: Sweatshirt has no sub categories.
Top
Tshirt
Sweatshirt (selected)
Tunic
Instead it shows something like this:
Sweatshirt (selected)
I'd hope I can explain myself. How can I solve this problem?
Thanks.

Related

Grouping items according to their id

I'm trying to group items in razor view by there id and put them all in one div
What I did put each one in a div .
<div class="tab-content" id="myTabContent">
#foreach (var item in Model)
{
<div class="tab-pane fade active in" id="genreTab ">
if (item.GenreId == 1)
{
#item.GenreId
}
</div>
if (item.GenreId == 2)
{
<div class="tab-pane fade " style="background-color:red;" id="genreTab ">
#item.GenreId
</div>
}
}
</div>
The classes in short:
public class Genre
{
public int GenreId { get; set; }
public string GenreName { get; set; }
public List<TakeAway> TakeAwys { get; set; }
}
public class TakeAway
{
public int TakeAwayId { get; set; }
public int GenreId { get; set; }
public virtual Genre genre { get; set; }
}
Your best bet is to loop over your genres instead of your items, if possible. You can accomplish this by grouping the items in your model by genres.
Then, loop over your genres in your view, with an inner loop of your items within the current genre.
Something like this:
#foreach (var genre in Model.Genres)
{
<div class="tab-pane fade active in" id="genreTab ">
#foreach (var item in genre.Items)
{
#item.SomeValueHere
}
</div>
}
Alternatively, if you are forced to receive your items flat like your OP, as much as I hate doing code like this in the view, using LINQ to group them in your view may be the lesser of the evils:
#foreach (var genre in Model.GroupBy(i => i.GenreId).Select(i => i.Key))
{
var innerGenre = genre; //to prevent modifed closure
<div class="tab-pane fade active in" id="genreTab ">
#foreach (var item in Model.Where(i => i.GenreId == innerGenre))
{
#item.SomeValueHere
}
</div>
}
An ID (genreTab) should be unique. If you want all in one div, you have to order them first.
When all are ordered, do this:
var lastId = 0;
<div class="tab-pane fade " style="background-color:red;" id="genreTab-#item.GenreId">
#foreach (var item in Model)
{
if (#item.GenreId != lastId)
{
</div><div class="tab-pane fade " style="background-color:red;" id="genreTab-#item.GenreId">
}
#item.GenreId
lastId = #item.GenreId;
}
</div>
Syntax may be wrong, I don't know razor syntax.

Current link in top and submenu

I'm trying to set a "current" class on both top and sub menu items in an Umbraco installation.
Topmenu is like :
Home Products About Contact
Now when I click let's say Products, then i set the class name to "current". At the same time it loads a submenu like this :
Jeans
Sweeters
T-shirts
- Red
- Green
- Blue
And when I click let's say Sweeters then I wan't the products link and the sweeters link to have the the current class. How can I do this?
Code for topmenu
#{
<ul class="topnavigation">
#foreach (var c in Model.AncestorOrSelf(2).Children.Where("umbracoNaviHide!=true"))
{
<li class="#(Model.Id == c.Id ? "current" : "")">#c.Name</li>
}
</ul>
}
Code for submenu
#{
<ul>
#foreach (var page in #Model.AncestorOrSelf(3).Children)
{
string style = string.Empty;
if (Model.Id == page.Id) { style = "class=current"; }
<li #style><a href="#page.Url" #Html.Raw(style)>#page.Name</a></li>
if (page.Childen != null && page.Children.Count() > 0 && Model.AncestorsOrSelf().Where("Id == #0", page.Id).Count() > 0)
{
<ul>
#foreach (dynamic secondPage in page.Children.Where("!umbracoNaviHide"))
{
string style1 = string.Empty;
if (Model.Id == secondPage.Id) { style1 = "class=current"; }
<li #style1>
- #secondPage.Name
</li>
}
</ul>
}
}
</ul>
}
A page has a Path property which contains a comma-delimited list of IDs representing the ancestors of the page.
You could check whether the Products page's ID exists within the current page's Path property using something like #Model.Path.Contains(c.Id.ToString()).

How can I make an unordered list appear within a foreach loop in MVC3 Razor?

I have the following code:
#foreach (Content topic in Model.Topics)
{
if (topic.RowKey.Substring(2, 2) == "00")
{
<h3>#topic.RowKey.Substring(0, 2).TrimStart('0') - #Html.Raw(topic.Title)</h3>
}
else
{
<p>#String.Format("{0}.{1}",topic.RowKey.Substring(0, 2).TrimStart('0'),topic.RowKey.Substring(2, 2).TrimStart('0').PadLeft(1, '0')) - #Html.Raw(topic.Title)</p>
}
}
My input data (value of topic.RowKey) looks like this:
0100 <- This is topic 1
0101 <- This is topic 1.1
0102 <- This is topic 1.2
0103 <- This is topic 1.3
0200 <- This is topic 2
0201 <- This is topic 2.1
etc....
This works but what I would really like to do is to have an h3 heading every time the first two digits of the RowKey change and then between then and then next h3 heading I would like to have an unordered list instead of just <p>xxx</p>. I have tried lots of different combinations of things but nothing works. Is this even possible to do with Razor? Where I had huge problems was how can I get the <ul> and </ul>s to appear correctly? I know I can put a <ul> after the <h3> but how can I place the </ul>?
not checked in razor, there may be an # missing here or there, but...
#var groupedTopics = Model.Topics.GroupBy(t => t.RowKey.Substring(0, 2));
#foreach (var group in groupedTopics) {
var firstTopic = group.First();
<h3>#firstTopic.RowKey.Substring(0, 2).TrimStart('0') - #Html.row(firstTopic.Title)</h3>
<ul>
#foreach (var topic in group.Skip(1)) {
<li>#String.Format("{0}.{1}",topic.RowKey.Substring(0, 2).TrimStart('0'),topic.RowKey.Substring(2, 2).TrimStart('0').PadLeft(1, '0')) - #Html.Raw(topic.Title)</li>
}
</ul>
}
It would be more easy create a model parent-child, populated before send it to the view
the model
public class topic {
//everything else
public List<topic> subtopic{ get; set; }
}
the view
#foreach (Content topic in Model.Topics)
{
<h3>#topic.RowKey.Substring(0, 2).TrimStart('0') - #Html.Raw(topic.Title)</h3>
if (topic.subtopic.count > 0)
{
<ul>
#foreach (Content subtopic in topic.subtopic)
{
<li>#String.Format("{0}.{1}", topic.RowKey.Substring(0, 2).TrimStart('0'), topic.RowKey.Substring(2, 2).TrimStart('0').PadLeft(1, '0')) - #Html.Raw(topic.Title)</li>
}
</ul>
}
}
Its not about razor, this is something you can solve with any rendering enginge, web or windows forms or anything else.
#model List<Topic>
#{
int currentKey = 0;
bool withinUL = false;
foreach (Topic topic in Model)
{
if (topic.RowKey != currentKey)
{
currentKey = topic.RowKey;
if (withinUL)
{
withinUL = false;
#:</ul>
}
#:<h3>#topic.RowKey - #Html.Raw(topic.Title)</h3>
}
else
{
if (!withinUL)
{
withinUL = true;
#:<ul></ul>
}
#:<li>#topic.RowKey - #Html.Raw(topic.Title)</li>
}
}
}
#{bool isFirst = true;}
#foreach (Content topic in Model.Topics)
{
if (topic.RowKey.Substring(2, 2) == "00")
{
if(!isFirst) {
<text></ul></text>
isFirst = false
}
<h3>#topic.RowKey.Substring(0, 2).TrimStart('0') - #Html.Raw(topic.Title)</h3>
}
else
{
<li>
<p>#String.Format("{0}.{1}",topic.RowKey.Substring(0, 2).TrimStart('0'),topic.RowKey.Substring(2, 2).TrimStart('0').PadLeft(1, '0')) - #Html.Raw(topic.Title)</p></li>
}
}
</ul>

Umbraco 5 ask if user has permission to node

Im working with Umbraco 5.1 beta. On the internet (this information is from previous versions, could not find recent documentation on it) I find that I could ask a node if the user has Access. In that way I want to build up my menu. The thing is, I cant get it to work, the HasAccess and IsProtected properties are not working. What am I doing wrong? Or does it work different in the newer versions of Umbraco? (I also tried it as method, still no result)
This is the code I'm now using:
#inherits RenderViewPage
#using Umbraco.Cms.Web;
#{
var Homepage = #DynamicModel;
while (Homepage.ContentType.Alias != "homePage")
{
Homepage = Homepage.Parent;
}
}
<ul>
<li>Home</li>
#foreach (var item in Homepage.Children) {
if(!item.IsProtected || (item.IsProtected && item.HasAccess)) {
if(#item.CurrentTemplate != null) {
var childName = item.Name ?? "(No name yet)";
<li>#childName </li>
}
}
}
</ul>
If you are just looking to suppress nodes that the user cannot access. Then you can use the WhereCanAccess() method.
Example: (This will hide all child nodes that the user doesn't have access to)
#inherits RenderViewPage
#using Umbraco.Cms.Web;
#{
var Homepage = #DynamicModel;
while (Homepage.ContentType.Alias != "homePage")
{
Homepage = Homepage.Parent;
}
}
<ul>
<li>Home</li>
#foreach (var item in Homepage.Children.WhereCanAccess())
{
if(#item.CurrentTemplate != null)
{
var childName = item.Name ?? "(No name yet)";
<li>#childName </li>
}
}
</ul>
Trying to find if a node IsProtected seems to be somewhat more complex (although only a couple of lines of code. Well the only way I found find to do it anyway!)
Example: (This just puts an * next to the name of protected menu items)
#inherits RenderViewPage
#using Umbraco.Cms.Web;
#{
var Homepage = #DynamicModel;
while (Homepage.ContentType.Alias != "homePage")
{
Homepage = Homepage.Parent;
}
var appContext = DependencyResolver.Current.GetService<IUmbracoApplicationContext>();
}
<ul>
<li>Home</li>
#foreach (var item in Homepage.Children)
{
var isProtected = appContext.Security.PublicAccess.IsProtected(item.Id);
if (#item.CurrentTemplate != null)
{
var childName = item.Name ?? "(No name yet)";
childName = (isProtected) ? "* " + childName : childName;
<li>#childName </li>
}
}
</ul>

Is there a way to make a #section optional with the asp.net mvc Razor ViewEngine?

I have a Page.cshtml similar to the following (that does not work):
#{
Layout = "../Shared/Layouts/_Layout.cshtml";
var mycollection = (ViewBag.TheCollection as IQueryable<MyCollectionType>);
}
<h2>#ViewBag.Title</h2>
content here
#if (mycollection != null && mycollection.Count() > 0)
{
#section ContentRight
{
<h2>
Stuff
</h2>
<ul class="stuff">
#foreach (MyCollectionType item in mycollection )
{
<li class="stuff-item">#item.Name</li>
}
</ul>
}
}
As I said, this does not work. I want to not define the section if there's nothing in the collection. Is there any way to have something like this work? If not, what are my other options? I'm very new to this Razor ViewEngine.
Edit
In my layout i have:
#if(IsSectionDefined("ContentRight"))
{
<div class="right">
RenderSection("ContentRight")
</div>
}
what i don't want is the div to output when the section is empty.
I ended up doing something a little hacky to get it working how I needed it.
on my page i have:
#{
Layout = "../Shared/Layouts/_Layout.cshtml";
var mycollection = (ViewBag.TheCollection as IQueryable<MyCollectionType>);
ViewBag.ShowContentRight = mycollection != null && mycollection.Count() > 0;
}
then in my layout i have:
#if(IsSectionDefined("ContentRight") && (ViewBag.ShowContentRight == null ||ViewBag.ShowContentRight == true))
{
<div class="right">
RenderSection("ContentRight")
</div>
}
else if(IsSectionDefined("ContentRight"))
{
RenderSection("ContentRight")
}
If the section is defined it has to be rendered, but if there's no content i dont want the <div>s
If there's a better way i'd like to know.
The renderer is expecting the method to be called sometime in the layout file. You can spoof the renderer and use "global" conditionals (think login).
#{
ViewBag.content = RenderBody();
}
#if (Request.IsAuthenticated) {
#ViewBag.content;
}
else {
#Html.Partial("_LoginPartial")
}
Extension method with private static readonly field info for perf:
private static readonly FieldInfo RenderedSectionsFieldInfo = typeof(WebPageBase).GetField("_renderedSections", BindingFlags.Instance | BindingFlags.NonPublic);
public static void EnsureSectionsAreRegisteredAsRendered(this WebPageBase webPageBase, params string[] sectionNames)
{
var renderedSections = RenderedSectionsFieldInfo.GetValue(webPageBase) as HashSet<string>;
if (renderedSections == null)
{
throw new WebCoreException("Could not get hashset from private field _renderedSections from WebPageBase");
}
foreach (var sectionName in sectionNames)
{
if (!renderedSections.Contains(sectionName))
{
renderedSections.Add(sectionName);
}
}
}
In your cshtml:
#{ this.EnsureSectionsAreRegisteredAsRendered("SectionName1", " SectionName2", "…"); }
Yes, yes, yes.... I know.... bad reflection! Use at your own risk :)
I use the following method in my view base class (from this excellent blog post http://haacked.com/archive/2011/03/05/defining-default-content-for-a-razor-layout-section.aspx/):
public HelperResult RenderSection(string name, Func<dynamic, HelperResult> defaultContents)
{
if (IsSectionDefined(name))
{
return RenderSection(name);
}
return defaultContents(null);
}
If you don't have a view base class, I recommend one because it lets you add all sorts of little extra functionality to your views. Just create a class with the following signature: public abstract class MyViewPage<T> : WebViewPage<T> and then set it in your web.config:
<system.web.webPages.razor>
<pages pageBaseType="MyViewPage">
...
</pages>
</system.web.webPages.razor>
You can wrap your whole section in an if statement with IsSectionDefined
Layout.cshtml:
#if (IsSectionDefined("ContentRight"))
{
<div>
#RenderSection(name: "ContentRight", required: false)
</div>
}
Your cshtml page:
#section ContentRight
{
#if (mycollection != null && mycollection.Count() > 0)
{
<h2>
Stuff
</h2>
<ul class="stuff">
#foreach (MyCollectionType item in mycollection )
{
<li class="stuff-item">#item.Name</li>
}
</ul>
}
}

Resources