replacement of asp:Treeview for ASP.NET MVC application - asp.net-mvc

I thus far worked with asp:Treeview for all my dynamic menus for my web applications..
Any suitable replacement of it in an asp.net mvc web application...
Any HTML helper that can perform like Treeview for me?

I would use jQuery based plugin. Like this one.

In my Mvc Controls Toolkit I have a server control based on the jQuery TreView. However, I allow node editing, insertion ov new nodes, and moving sub-tree into another location by dragging it with the mouse. All changes are reflected Automatically on data structures on the server side when the view is posted. Moreover all nodes are templated and the same tree can have different type of nodes. Give a look here:
http://mvccontrolstoolkit.codeplex.com/wikipage?title=TreeView

As Arnis said, using Jquery Pluggin, it is so easy!
I do it by encapsulating the code and html in a Partial View as a UserControl. You can do it by a recursive logic:
#helper ShowTree(TreeItem item, IEnumerable<TreeItem> tree)
{
var childs = folders.Where(g => g.ParentId == item.Id);
if (childs.Count() == 0)
{
<text>
<li class="last"><span class="folder">#item.Title</span></li>
</text>
}
else
{
<text>
<li class="expandable">
<div class="hitarea expandable-hitarea">
</div>
<span class="folder">#item.Title</span>
<ul style="display: none;">
#{foreach (var child in childs)
{
#ShowTree(child, folders)
}
}
</ul>
</li>
</text>
}
}

Related

if.bind on a repeater - doesn't work in Edge & IE

I have the following html part of code:
<li repeat.for="route of router.navigation" style="border: 0px;" if.bind="showNav(route)">
<a href.bind="route.href" if.bind="!route.settings.nav">
${route.title}
</a>
<a href="javascript:;" if.bind="route.settings.nav">
${route.title}
</a>
<ul if.bind="route.settings.nav" class="dropdown-menu">
<li repeat.for="menu of route.settings.nav" class="ul-menu">
<a href.bind="menu.href">${menu.title}</a>
</li>
</ul>
</li>
In Opera, Chrome this code works fine, but in IE & Edge doesn't work - I don't see this HTML-part.
Problem is in the following statement (in the first line):
if.bind="showNav(route)"
If I deleted it, I can see my navigation menu in Edge & IE also.
Code for showNav:
showNav(row) {
if (!row.config.role) {
return true;
}
this.currentUserName = localStorage.getItem("token_user");
var currentUser = localStorage.getItem("token_role");
var role = row.config.role.includes(currentUser);
return role;
}
If I add in showNav
console.log(row);
It logs undefined in Edge & IE, but in Opera & Chrome I see the full necessary value.
I work with Aurelia framework, so route.navigation goes from ts-file and has the necessary value.
What could be the problem?
The github issue from #jesse-de-bruijne is different, that if.bind and repeat.for are not on the same DOM element. Furthermore, that issue was FIXED long time ago. But anyway, the show.bind purposed by Jesse works.
The real issue is that you are using if.bind and repeat.for on exact same DOM element, which is not supported by Aurelia due to uneven behavior from browsers. Aurelia documentation has not yet addressed this.
Besides the show.bind fix, you can also use template element (which will result to no extra DOM wrapper actually) to seperate repeat.for and if.bind.
<template> <!-- the top level template in your html file -->
...
<template repeat.for="route of router.navigation">
<li style="border: 0px;" if.bind="showNav(route)">
...
</li>
</template>
...
</template>
FYI: Repeat, with and if are called template controllers. They bind before other bindings. You cannot use multiple template controller attributes on the same dom element (because of different behavior among browsers).
The above comment is from Aurelia core member jdanyow on one of my issues.
https://github.com/aurelia/templating-resources/issues/252
Indeed, different browsers sort the HTML attributes differently. That's why your code works on some browsers but not all.
Try using a show.bind instead, if.bind has had some trouble with a repeater on the same line.
For example: https://github.com/aurelia/templating-resources/issues/84
If you do need to use an if.bind, for performance reasons for example, try putting a div child in the repeater containing said if.bind.

What should be the standard coding pattern for Sitecore

Using Sitecore 8.0, MVC, VS2015
I managed to generate the footer content as desired, for a practice site, but would like to know the standard way of doing things. The question is simple, but tried to explain in detail. Please bear with that :)
Requirement: The footer should display Office addresses and they should be editable by the Content author.
Sitecore Template : Location, Telephone 1, Telephone 2, IsActive(type-checkbox).
Datasource : A folder with items of the above template.
Code:
public class FooterViewModel
{
public List<Sitecore.Data.Items.Item> Addresses { get; set; }
}
public class FooterController : Controller
{
public ActionResult Footer()
{
var datasource = RenderingContext.Current.Rendering.Item;
FooterViewModel viewModel = new FooterViewModel();
viewModel.Addresses = new List<Item>();
if(datasource != null && datasource.HasChildren && datasource.Children.Count > 0)
{
foreach(Item address in datasource.Children)
{
if (address["IsActive"] == "1")
viewModel.Addresses.Add(address);
}
}
return View("~/Views/Shared/Footer.cshtml", viewModel);
}
}
Rendering the html using a Sitecore Controller Rendering
cshtml:
#using Sitecore.Mvc
#using Sitecore.Mvc.Presentation
#model Democore.Models.FooterViewModel
<div>
#foreach (var address in Model.Addresses)
{
<div>
<h3>#Html.Sitecore().Field("Location", address)</h3>
<ul>
<li>
#Html.Sitecore().Field("Telephone 1", address)
</li>
<li>
#Html.Sitecore().Field("Telephone 2", address)
</li>
</ul>
</div>
}
</div>
<div>
<p>&copy Copyright #DateTime.Now.Year. All rights reserved</p>
</div>
Here are my questions. (..well all 3 are more or less similar)
How to better this code/structure (or) in which case might it fail.
I did not like the way I hardcoded the field names in controller &
cshtml. What if the author changes the field names. How to tackle
that.
How does it work in a real scenario, say for eg if author
wants to show a third phone number. Will they contact the developer?
Because that requires a change in design & code too right?
This is a very broad question and will probably get closed -but here are some tips!
You controller is fine as far as error handling goes. You really want to add a global error handler for your site. This is a good example of that: http://www.sitecorenutsbolts.net/2015/10/23/Rendering-Exception-Handling-The-Right-Way/
You have 2 options - use constants and field ID's not the names, not a great option but works. A better option would be to use an ORM/Wrapper to do that. Two good ones are Fortis and Glass Mapper - both are very good - I contribute to Fortis so that would be my recomendation.
Normally if the design of the component changes it will require development support. You could use something like BrainJock's Score or ZenGarden to build your site and then the editor has a lot more control. But still likely would need a developer.
Hope this helps. For some info on good Sitecore architecture look here: Sitecore Helix and Sitecore Habitat

How to use a knockout component view with parent layout?

I am working on replacement ASP.NET MVC+Knockout with just Knockout, I want to remove ASP.NET and get just static js + html.
My ASP.NET views consist of Partial views (I call them widgets in my project), this Partial views easily replaced with Knockout components.. but I have a problem: ASP.NET Partial views have a Layout (some html decoration for every widget), how can I achieve similar for Knockout component view?
Simplified example. Old asp.net scheme:
View.cshtml:
<div>
#Html.Partial("SomeWidget")
</div>
SomeWidget.cshtml:
#{
Layout = "~/Views/Shared/_WidgetLayout.cshtml"; <!-- parent layout for widget -->
}
<span>This is some widget</span>
_WidgetLayout.cshtml:
<div>
<span>This is decorator for every widget</span>
#RenderBody() <!-- render widget view here (SomeWidget.cshtml in this example) -->
</div>
New knockout-only scheme:
View.html:
<div>
<some-widget></some-widget>
</div>
View.js:
ko.components.register('some-widget', { require: 'app/SomeWidget' });
SomeWidget.html:
<span>This is some widget</span>
SomeWidget.js:
var view = require('text!/views/SomeWidget.html');
return { template: view };
How to replace _WidgetLayout.cshtml in Knockout?
There are several ways you could possibly do this. The simplest way I can think of is to have a template component, and you nest the widget inside this. KO Components support nesting.
You can define a template component thus:
ko.components.register("widget-template", {
viewModel: function(params) {
var self=this;
self.WidgetName = params.widget;
},
template: "<div class='b'><span>This is decorator for every widget</i>
<div data-bind='component: { name: WidgetName }'></div></div>"
});
To use this, you put the template-widget into your HTML, and pass the name of the widget as a parameter:
<widget-template params="widget: 'widget1'"></widget-template>
Then you define a widget as another component:
ko.components.register("widget1", {
template: "<h3>Widget One</h3>"});
So now you have a re-usable template that can wrap any component. You can see more about this binding in the Knockout documentation.
See the full JS fiddle here: http://jsfiddle.net/Quango/a8h2bwtc/
Note that you can also make the name an observable rather than a static value, as seen here:
http://jsfiddle.net/Quango/tnphvvgd/

Binding lost (after click) in angular js small example

I have a very small application in Angular JS. It's placed inside a bigger rails application, but I don't see too much interaction. The angular application, allows the user to interact with a group of categories. As easy as:
var angular_app = angular.module('angular_app', []);
angular_app.config(['$httpProvider', function($httpProvider, $cookieStore) {
//Protection
}]);
angular_app.controller('CategoriesController', function ($scope, $http) {
$scope.isEditing = false;
$scope.categoryName = '';
$http.get('/api/categories').success(function(data) {
//We use this to data-bind with the HTML placed below
$scope.categories = data;
});
$scope.addNewCategory = function() {
...
}
$scope.editCategory = function(index) {
if (!index)
return;
var selectedCategory = $scope.categories[index];
// With ng-show, we make visible the part of the UI
// that should be used for editing
$scope.isEditing = true;
}
$scope.cancelEditCategory = function() {
$scope.isEditing = false;
}
$scope.deleteCategory = function(index) {
...
}
});
angular.element(document).ready(function() {
angular.bootstrap(document, ['angular_app']);
});
The idea is that the information is shown in a list, and we have an 'edit' button that allows the user to see other part of the UI that will let him perform changes.
<div ng-controller="CategoriesController">
<div ng-show='isEditing' class="popup_menu">
DIV FOR EDITING
</div>
<ul>
<li ng-repeat="category in categories">
<a href="#" ng-click='deleteCategory($index)'>[X]</a>
<a href="#" ng-click='editCategory($index)'>[E]</a>{{ category.name }}
</li>
</ul>
<input type="text" id="categoryTextBox" ng-model="categoryName"/>
<button id="submit" ng-click='addNewCategory()'>New category</button>
</div>
When I'm clicking the edit button, the corresponding part of the UI gets visible, but just after that, something happens, and the ul that should render the list, looses completely the binding, just showing something like:
[X] [E]{{ category.name }}
When it must be showing:
[X] [E]computer science
[X] [E]politics
[X] [E]news
(Which is what I have in the scope). It happens a few after the click (and works for a sec). No errors on the console, no interactions with other libraries (as far as I can see).
Thanks!
Turbolinks
I have no experience with Angular, but perhaps your problem could be to do with Turbolinks - this is a way of Rails loading the <body> tag of a page only - keeping the <head> intact.
Turbolinks is notorious for Javascript on Rails, as each time you reload your <body> without reloading the <head> part of your page, all your JS bindings are going to disappear. The solution to this, in normal JS, is to use JQuery / Javascript delegation, and delegate from the document object:
$(document).on("action", "delegated_object", function(){
...
});
Apologies if this does not work - it's a common issue for us, but as I have no experience with Angular, I don't know if it's going to help you or not.
It seems that I should have been more careful with the links:
<a href="#" ng-click='deleteCategory($index)'>[X]</a>
<a href="#" ng-click='editCategory($index)'>[E]</a>{{ category.name }}
Don't know exactly how this works, but seems that if the link has his href attribute, a GET request is made against 127.0.0.1, breaking in some way the angular code. If you put them like:
<a ng-click='deleteCategory($index)'>[X]</a>
<a ng-click='editCategory($index)'>[E]</a>{{ category.name }}
The problem will be solved. Thanks all for reading and helping!

Preserve the opened/closed state of my sub-menus when navigate to another page

I have an asp.net mvc4 solution.
I have a menu on the left side. By default this menu is closed.
If I click on an item, a sub-menu is showed.
The problem is when I navigate to another page, the opened/closed state of sub-menus are forgotten. The new page is showed and all sub-menus are still closed. I would like to preserve the opend/closed state of these sub-menus. How can I proceed?
Here is a portion of my left side menu:
<div class="page-sidebar">
<ul>
<li class="dropdown" data-role="dropdown">
<a><i class="icon-flip-2"></i> Transports</a>
<ul class="sub-menu light sidebar-dropdown-menu">
<li>#Html.ActionLink("En cours", "SearchTransportsAA", "Transp")</a></li>
<li>#Html.ActionLink("Passés", "SearchTransportsBB", "Transp")</a></li>
<li>#Html.ActionLink("Factures", "SearchTransportsCC", "Transp")</a></li>
</ul>
</li>
<li class="dropdown" data-role="dropdown">
<a><i class="icon-drawer-2"></i> Autorisations</a>
<ul class="sub-menu light sidebar-dropdown-menu open">
<li>#Html.ActionLink("Valides", "SearchAutorisAA", "Transp")</a></li>
<li>#Html.ActionLink("Périmés", "SearchAutorisBB", "Transp")</a></li>
<li>#Html.ActionLink("Recherche", "SearchAutorisCC", "Transp")</a></li>
</ul>
</li>
...
...
As you can see above, when 'open' is added to the class, the menu is marked to be open.
One way you could do it, is to have your Views that have this side menu inherit from a model that have property that contains the selected sub-menu item
public class ViewWithSideMenu
{
public ViewWithSideMenu(string menuItem)
{
MenuItem = menuItem;
}
public string MenuItem { get;set; }
}
ViewModel:
public class MyViewmModel : ViewWithSideMenu
{
public MyViewmModel() : base("someMenu") {}
}
View:
<ul class="sub-menu light sidebar-dropdown-menu#(Model.MenuItem == "someMenu"? " open" : "")">
this approach will only open a menu based on the page we are on, it doesn't really remember what the user clicked on, if it's very important for you to keep the user selections, you have 2 options,
keep sending them back to the server with every request (not very good, too much overhead)
keep updating the content using ajax requests and not a full reload of the page, in which case you keep the menu unchanged, and convert all your requests to ajax, and replace the content with the ajax response
You can try this, it worked for me :D
<script type="text/javascript">
$(document).ready(function () {
$("a[href='"+ window.location.pathname +"']").parents(".hidden-ul").css("display", "block");
});
</script>

Resources