Populate dijit menubar with data from sql database - c#

I haven't worked so much with jquery, ajax and json before, but I want to learn how to use it using dojo and dijit. There are many tutorials, but few c# with sql database examples.
I've created a json output with dijit menubar data from my database which looks like this:
[{"MenuItemId":1,"MenuName":"Root","Tooltip":"Root","IsParent":true,"ParentId":0,"IsVisible":false,"SortIndex":null},
{"MenuItemId":3,"MenuName":"Blogg","Tooltip":"Min Blogg","IsParent":false,"ParentId":1,"IsVisible":true,"SortIndex":1000},
{"MenuItemId":4,"MenuName":"Administrasjon","Tooltip":"Viser Administrasjon","IsParent":true,"ParentId":1,"IsVisible":true,"SortIndex":10000},
{"MenuItemId":5,"MenuName":"DropMenu","Tooltip":"Drop menu","IsParent":true,"ParentId":1,"IsVisible":true,"SortIndex":9000},
{"MenuItemId":6,"MenuName":"Menuitem1","Tooltip":"Menuitem1","IsParent":false,"ParentId":5,"IsVisible":true,"SortIndex":9001},
{"MenuItemId":9,"MenuName":"Menuitem2","Tooltip":"Menuitem2","IsParent":false,"ParentId":5,"IsVisible":true,"SortIndex":9002}]
I want to bind these data to dijit menubar, but I haven't figured out how to doo it yet.
Here is the code I've made so far. I've been trying back and forth, but I haven't managed to get data out from the json data I try to get.
Here is a sample in where I try to get data out of my json data output. The sample is trying to write data in console view, but I want to populate a dijit manybar:
<script>
require([
"dijit/MenuBar",
"dijit/PopupMenuBarItem",
"dijit/Menu",
"dijit/MenuItem",
"dijit/DropDownMenu",
"dojo/dom",
"dojo/request",
"dojo/json",
"dojo/_base/array",
"dojo/domReady!"
], function(MenuBar, PopupMenuBarItem, Menu, MenuItem, DropDownMenu, dom, request, JSON, arrayUtil) {
console.log('hello world');
// Results will be displayed in resultDiv
var resultDiv = dom.byId("resultDiv");
// Request the JSON data from the server
request.get("/api/Menu", {
// Parse data from JSON to a JavaScript object
handleAs: "json"
}).then(function(data) {
// Display the data sent from the server
var html = "";
var pMenuBar = new MenuBar({});
var pSubMenu = new DropDownMenu({});
console.log('data : ' + data.toString());
arrayUtil.forEach(data.items, function(item, i) {
console.log(item[0].value);
//console.log('item :' + item.name + '\r\n');
//console.log('value : ' + item.value + '\r\n');
});
//html += "<dt>" + item.name +
// "</dt><dd>" + item.value +
// " (" + (typeof item.value) + ")</dd>";
//});
//resultDiv.innerHTML = html;
//pMenuBar.placeAt("wrapper");
//pMenuBar.startup();
},
function(error) {
// Display the error returned
resultDiv.innerHTML = error;
});
});
</script>
What I need is a an example in how to iterate json data to populate dijit menubar.
<body class="claro">
<div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'sidebar', gutters:true, liveSplitters:true" id="borderContainer">
<div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="splitter:true, region:'leading'" style="width: 300px;">
<div id="markup" style="width: 300px; height: 300px"></div>
</div>
<div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="splitter:true, region:'center'">
<div id="wrapper"></div>
<div id="resultDiv"></div>
</div>
</div>
</body>
I could really use some help on this one. Thanks for all the advice I can get.
Thank you! :)
Kind regards,
Jon Haakon

I think I would make a recursive function that burrows down into the JSON structure you receive from the server, and builds menu items along the way.
Annoyingly, there is a small difference between items in the menu bar itself, and items in submenus belonging to the menu bar. The menu bar wants dijit/PopupMenuBarItem and dijit/MenuBarItem objects, while the popup menus themselves want dijit/PopupMenuItem and dijit/MenuItem, correspondingly.
Here's an example:
function recursiveMakeMenuItem(data, id, inMenuBar) {
// Get the item itself and its children (if any)
var item = arrayUtil.filter(data, function(i){return i.MenuItemId==id;})[0],
subs = arrayUtil.filter(data, function(i){return i.ParentId==id;}),
widget = null, menu = null;
if(subs.length) {
// This item has children, so we need to make both an item that
// triggers the a submenu (differentiating between items in the
// menu bar and items in menus) and the submenu itself.
widget = inMenuBar ? new PopupMenuBarItem() : new PopupMenuItem();
menu = new Menu();
widget.set("popup", menu);
// We then recursively dig deeper to generate the sub menus.
arrayUtil.forEach(subs, function(item) {
menu.addChild(recursiveMakeMenuItem(data, item.MenuItemId));
});
}
else {
// No children, but again we need to differentiate between items
// in the menu bar and items in menus.
widget = inMenuBar ? new MenuBarItem() : new MenuItem();
}
widget.set("label", item ? item.MenuName : "ERROR id "+id);
return widget;
}
In your code, you would call this when you receive data from the server. For example, if you wanted the single item with ParentId 0 to be the top level menu bar item, you could do:
request.post("/echo/json/", {
handleAs: "json"
}).then(function(data) {
var pMenuBar = new MenuBar({region: "top"});
pMenuBar.addChild( recursiveMakeMenuItem(data.items, 1, true) );
dijitRegistry.byId("borderContainer").addChild(pMenuBar);
pMenuBar.startup();
});
Or, if everything with ParentId 1 should be top level items:
request.post("/echo/json/", {
handleAs: "json"
}).then(function(data) {
var pMenuBar = new MenuBar({region: "top"});
arrayUtil.forEach(data.items, function(i) {
if(i.ParentId !== 1) return;
pMenuBar.addChild(recursiveMakeMenuItem(data.items, i.MenuItemId, true));
});
dijitRegistry.byId("borderContainer").addChild(pMenuBar);
pMenuBar.startup();
});
Here's an example fiddle: http://fiddle.jshell.net/2Gpkd/1/

Related

In ASP.net, what kind of IF statement could I use to hide a div if the image inside it matches the current page URL?

This is within Sitefinity if that matters, and I am really new at ASP.NET and C#.
I have an image-based navigation element at the bottom of a page that links to different articles using the same template. There are 5 articles, and I would like the link to the active page/article to be hidden so there is a grid of 4 image links.
Here's a screenshot:
https://i.imgur.com/PG2Sfpo.png
Here is the code behind it:
#{
string navTitle = string.Empty;
string url = string.Empty;
if (Model.CurrentSiteMapNode != null && Model.CurrentSiteMapNode.ParentNode != null)
{
if (Model.CurrentSiteMapNode.Title == "Home")
{
navTitle = Model.CurrentSiteMapNode.ParentNode.Title;
}
else
{
navTitle = Model.CurrentSiteMapNode.Title;
}
url = Model.CurrentSiteMapNode.ParentNode.Url;
}
}
<div class="foundation-stories-container">
#foreach (var node in Model.Nodes)
{
#RenderRootLevelNode(node);
}
</div>
#*Here is specified the rendering for the root level*#
#helper RenderRootLevelNode(NodeViewModel node)
{
string[] thisPage = (node.Url).Split('/');
string thisImage = thisPage[4] + ".jpg";
<a href="#node.Url" target="#node.LinkTarget">
<div class="foundation-story-block">
<div class="hovereffect">
<img src="[OUR WEBSITE URL]/stories/#thisImage" class="img-fluid">
<div class="overlay">
<h2>#node.Title</h2>
</div>
</div>
</div>
</a>
}
So we're already getting the page URL and image file name
string[] thisPage = (node.Url).Split('/');
string thisImage = thisPage[4] + ".jpg";
Is this as easy as doing the following?
if (thisImage = thisPage)
{
foundation-story-block.AddClassToHtmlControl("hide")
}
Seems easy enough, but I don't know where to start.
I'm better at Javascript, so I do have a JS solution in place for this already, but I'd really like to find a cleaner way to do it.
<script type="text/javascript">
$(document).ready(function() {
var active = window.location.pathname.split("/").pop()
var name = active;
name = name.replace(/-/g, ' ');
jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
return function( elem ) {
return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >=
0;
};
});
$("h2:Contains('" + name + "')").closest(".foundation-story-block").addClass("hide");
});
</script>
This exists on the main template page.
Gets the last part of the URL
Sets that as a variable called "name"
Changes the dash to a space if there is one (most of the pages are associated with names so it's like /first-last)
Then it goes and looks at the which is where the title of the page lives, and if it equals the "name" variable, the ".hide" class is added to the block.
Thanks for any help anyone can provide.
You could bind a click event to your elements with the foundation-story-block class. The reason I use .on instead of .click is because when using UpdatePanels the click event won't fire after an UpdatePanel has it's update event triggered - you might encounter a similar problem with your dynamic binding so I used .on to avoid this.
$(".foundation-story-block").on("click", function() {
// Remove the "hide" class from any elements that have it applied
$.each($(".foundation-story-block.hide"), function(index, value) {
// Remove the class using the "this" context from the anonymous function
$(this).removeClass("hide");
});
// Add the "hide" class to the element that was clicked
$(this).addClass("hide");
});
I haven't run this though an IDE so it might not be 100% correct but it will put you on the correct path.
It is possible, yes. Here is how:
...
#{
var hiddenClass = thisImage == thisPage ? "hide" : string.Empty;
}
<div class="foundation-story-block #hiddenClass">
<div class="hovereffect">
<img src="[OUR WEBSITE URL]/stories/#thisImage" class="img-fluid">
<div class="overlay">
<h2>#node.Title</h2>
</div>
</div>
</div>

Umbraco 7 load content media picker in custom section

I've created a custom section in Umbraco 7 that references external urls, but have a requirement to extend it to use exactly the same functionality as the media picker from the 'Content' rich text editor. I don't need any other rich text functionality other than to load the media picker overlay from an icon, and select either an internal or external url.
I've tried to distil the umbraco source code, as well as trying various adaptations of online tutorials, but as yet I can't get the media picker to load.
I know that fundamentally I need:
Another angular controller to return the data from the content
'getall' method
An html section that contains the media picker overlay
A reference in the edit.html in my custom section to launch the overlay.
However, as yet I haven't been able to wire it all together, so any help much appreciated.
So, this is how I came up with the solution.....
The first win was that I discovered 2 excellent tutorial blog posts, upon the shoulders of which this solution stands, so much respect to the following code cats:
Tim Geyssons - Nibble postings:
http://www.nibble.be/?p=440
Markus Johansson - Enkelmedia
http://www.enkelmedia.se/blogg/2013/11/22/creating-custom-sections-in-umbraco-7-part-1.aspx
Create a model object to represent a keyphrase, which will be associated to a new, simple, ORM table.
The ToString() method allows a friendly name to be output on the front-end.
[TableName("Keyphrase")]
public class Keyphrase
{
[PrimaryKeyColumn(AutoIncrement = true)]
public int Id { get; set; }
public string Name { get; set; }
public string Phrase { get; set; }
public string Link { get; set; }
public override string ToString()
{
return Name;
}
}
Create an Umbraco 'application' that will register the new custom section by implementing the IApplication interface. I've called mine 'Utilities' and associated it to the utilities icon.
[Application("Utilities", "Utilities", "icon-utilities", 8)]
public class UtilitiesApplication : IApplication { }
The decorator allows us to supply a name, alias, icon and sort-order of the new custom section.
Create an Umbraco tree web controller that will allow us to create the desired menu behaviour for our keyphrases, and display the keyphrase collection from our database keyphrase table.
[PluginController("Utilities")]
[Umbraco.Web.Trees.Tree("Utilities", "KeyphraseTree", "Keyphrase", iconClosed: "icon-doc", sortOrder: 1)]
public class KeyphraseTreeController : TreeController
{
private KeyphraseApiController _keyphraseApiController;
public KeyphraseTreeController()
{
_keyphraseApiController = new KeyphraseApiController();
}
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
var nodes = new TreeNodeCollection();
var keyphrases = _keyphraseApiController.GetAll();
if (id == Constants.System.Root.ToInvariantString())
{
foreach (var keyphrase in keyphrases)
{
var node = CreateTreeNode(
keyphrase.Id.ToString(),
"-1",
queryStrings,
keyphrase.ToString(),
"icon-book-alt",
false);
nodes.Add(node);
}
}
return nodes;
}
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
{
var menu = new MenuItemCollection();
if (id == Constants.System.Root.ToInvariantString())
{
// root actions
menu.Items.Add<CreateChildEntity, ActionNew>(ui.Text("actions", ActionNew.Instance.Alias));
menu.Items.Add<RefreshNode, ActionRefresh>(ui.Text("actions", ActionRefresh.Instance.Alias), true);
return menu;
}
else
{
menu.Items.Add<ActionDelete>(ui.Text("actions", ActionDelete.Instance.Alias));
}
return menu;
}
}
The class decorators and TreeController extension allow us to declare the web controller for our keyphrase tree, associate it to our Utilities custom section, as well as choose an icon and sort order.
We also declare an api controller (we'll get to that!), which will allow us access to our Keyphrase data object.
The GetTreeNodes method allows us to iterate the keyphrase data collection and return the resultant nodes to the view.
The GetMenuNode method allows us to create the menu options we require for our custom section.
We state that if the node is the root (Utilities), then allow us to add child nodes and refresh the node collection.
However, if we are lower in the node tree (Keyphrase) then we only want users to be able to delete the node (ie the user shouldn't be allowed to create another level of nodes deeper than Keyphrase)
Create an api controller for our Keyphrase CRUD requests
public class KeyphraseApiController : UmbracoAuthorizedJsonController
{
public IEnumerable<Keyphrase> GetAll()
{
var query = new Sql().Select("*").From("keyphrase");
return DatabaseContext.Database.Fetch<Keyphrase>(query);
}
public Keyphrase GetById(int id)
{
var query = new Sql().Select("*").From("keyphrase").Where<Keyphrase>(x => x.Id == id);
return DatabaseContext.Database.Fetch<Keyphrase>(query).FirstOrDefault();
}
public Keyphrase PostSave(Keyphrase keyphrase)
{
if (keyphrase.Id > 0)
DatabaseContext.Database.Update(keyphrase);
else
DatabaseContext.Database.Save(keyphrase);
return keyphrase;
}
public int DeleteById(int id)
{
return DatabaseContext.Database.Delete<Keyphrase>(id);
}
}
Create the custom section views with angular controllers, which is the current architectual style in Umbraco 7.
It should be noted that Umbraco expects that your custom section components are put into the following structure App_Plugins//BackOffice/
We need a view to display and edit our keyphrase name, target phrase and url
<form name="keyphraseForm"
ng-controller="Keyphrase.KeyphraseEditController"
ng-show="loaded"
ng-submit="save(keyphrase)"
val-form-manager>
<umb-panel>
<umb-header>
<div class="span7">
<umb-content-name placeholder=""
ng-model="keyphrase.Name" />
</div>
<div class="span5">
<div class="btn-toolbar pull-right umb-btn-toolbar">
<umb-options-menu ng-show="currentNode"
current-node="currentNode"
current-section="{{currentSection}}">
</umb-options-menu>
</div>
</div>
</umb-header>
<div class="umb-panel-body umb-scrollable row-fluid">
<div class="tab-content form-horizontal" style="padding-bottom: 90px">
<div class="umb-pane">
<umb-control-group label="Target keyphrase" description="Keyphrase to be linked'">
<input type="text" class="umb-editor umb-textstring" ng-model="keyphrase.Phrase" required />
</umb-control-group>
<umb-control-group label="Keyphrase link" description="Internal or external url">
<p>{{keyphrase.Link}}</p>
<umb-link-picker ng-model="keyphrase.Link" required/>
</umb-control-group>
<div class="umb-tab-buttons" detect-fold>
<div class="btn-group">
<button type="submit" data-hotkey="ctrl+s" class="btn btn-success">
<localize key="buttons_save">Save</localize>
</button>
</div>
</div>
</div>
</div>
</div>
</umb-panel>
</form>
This utilises umbraco and angular markup to display data input fields dynamically and associate our view to an angular controller that interacts with our data layer
angular.module("umbraco").controller("Keyphrase.KeyphraseEditController",
function ($scope, $routeParams, keyphraseResource, notificationsService, navigationService) {
$scope.loaded = false;
if ($routeParams.id == -1) {
$scope.keyphrase = {};
$scope.loaded = true;
}
else {
//get a keyphrase id -> service
keyphraseResource.getById($routeParams.id).then(function (response) {
$scope.keyphrase = response.data;
$scope.loaded = true;
});
}
$scope.save = function (keyphrase) {
keyphraseResource.save(keyphrase).then(function (response) {
$scope.keyphrase = response.data;
$scope.keyphraseForm.$dirty = false;
navigationService.syncTree({ tree: 'KeyphraseTree', path: [-1, -1], forceReload: true });
notificationsService.success("Success", keyphrase.Name + " has been saved");
});
};
});
Then we need html and corresponding angular controller for the keyphrase delete behaviour
<div class="umb-pane" ng-controller="Keyphrase.KeyphraseDeleteController">
<p>
Are you sure you want to delete {{currentNode.name}} ?
</p>
<div>
<div class="umb-pane btn-toolbar umb-btn-toolbar">
<div class="control-group umb-control-group">
<a href="" class="btn btn-link" ng-click="cancelDelete()"
<localize key="general_cancel">Cancel</localize>
</a>
<a href="" class="btn btn-primary" ng-click="delete(currentNode.id)">
<localize key="general_ok">OK</localize>
</a>
</div>
</div>
</div>
</div>
Utilise Umbraco's linkpicker to allow a user to select an internal or external url.
We need html markup to launch the LinkPicker
<div>
<ul class="unstyled list-icons">
<li>
<i class="icon icon-add blue"></i>
<a href ng-click="openLinkPicker()" prevent-default>Select</a>
</li>
</ul>
</div>
And an associated directive js file that launches the link picker and posts the selected url back to the html view
angular.module("umbraco.directives")
.directive('umbLinkPicker', function (dialogService, entityResource) {
return {
restrict: 'E',
replace: true,
templateUrl: '/App_Plugins/Utilities/umb-link-picker.html',
require: "ngModel",
link: function (scope, element, attr, ctrl) {
ctrl.$render = function () {
var val = parseInt(ctrl.$viewValue);
if (!isNaN(val) && angular.isNumber(val) && val > 0) {
entityResource.getById(val, "Content").then(function (item) {
scope.node = item;
});
}
};
scope.openLinkPicker = function () {
dialogService.linkPicker({ callback: populateLink });
}
scope.removeLink = function () {
scope.node = undefined;
updateModel(0);
}
function populateLink(item) {
scope.node = item;
updateModel(item.url);
}
function updateModel(id) {
ctrl.$setViewValue(id);
}
}
};
});
There is one final js file that allows us to send data across the wire, with everyone's favourite http verbs GET, POST(handles put too here too) and DELETE
angular.module("umbraco.resources")
.factory("keyphraseResource", function ($http) {
return {
getById: function (id) {
return $http.get("BackOffice/Api/KeyphraseApi/GetById?id=" + id);
},
save: function (keyphrase) {
return $http.post("BackOffice/Api/KeyphraseApi/PostSave", angular.toJson(keyphrase));
},
deleteById: function (id) {
return $http.delete("BackOffice/Api/KeyphraseApi/DeleteById?id=" + id);
}
};
});
In addition, we will need a package manifest to register our javascript behaviour
{
javascript: [
'~/App_Plugins/Utilities/BackOffice/KeyphraseTree/edit.controller.js',
'~/App_Plugins/Utilities/BackOffice/KeyphraseTree/delete.controller.js',
'~/App_Plugins/Utilities/keyphrase.resource.js',
'~/App_Plugins/Utilities/umbLinkPicker.directive.js'
]
}
Implement tweaks to allow the CMS portion of the solution to work correctly.
At this point we've almost got our custom section singing, but we just need to jump a couple more Umbraco hoops, namely
a) add a keyphrase event class that creates our keyphrase db table if it doesn't exist (see point 8)
b) fire up Umbraco and associate the new custom section to the target user (from the User menu)
c) alter the placeholder text for the custom section by searching for it in umbraco-->config-->en.xml and swapping out the placeholder text for 'Utilities'
Intercept target content fields of target datatypes when content is saved or published
The requirement I was given was to intercept the body content of a news article, so you'll need to create a document type in Umbraco that has, for example, a title field of type 'Textstring', and bodyContent field of type 'Richtext editor'.
You'll also want a, or many, keyphrase(s) to target, which should now be in a new Umbraco custom section, 'Utilities'
Here I've targeted the keyphrase 'technology news' to link to the bbc technology news site so that any time I write the phrase 'technology news' the href link will be inserted automatically.
This is obviously quite a simple example, but would be quite powerful if a user needed to link to certain repetitive legal documents, for example tax, property, due dilligence, for example, which could be hosted either externally or within the CMS itself. The href link will open an external resource in a new tab, and internal resource in the same window (we'll get to that in Point 9)
So, the principle of what we're trying to achieve is to intercept the Umbraco save event for a document and manipulate our rich text to insert our link. This is done as follows:
a) Establish a method (ContentServiceOnSaving) that will fire when a user clicks 'save', or 'publish and save'.
b) Target our desired content field to find our keyphrases.
c) Parse the target content html against our keyphrase collection to create our internal/external links.
NB: If you just want to get the custom section up and running, you only need the ApplicationStarted method to create the KeyPhrase table.
public class KeyphraseEvents : ApplicationEventHandler
{
private KeyphraseApiController _keyphraseApiController;
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication,
ApplicationContext applicationContext)
{
_keyphraseApiController = new KeyphraseApiController();
ContentService.Saving += ContentServiceOnSaving;
var db = applicationContext.DatabaseContext.Database;
if (!db.TableExist("keyphrase"))
{
db.CreateTable<Keyphrase>(false);
}
}
private void ContentServiceOnSaving(IContentService sender, SaveEventArgs<IContent> saveEventArgs)
{
var keyphrases = _keyphraseApiController.GetAll();
var keyphraseContentParser = new KeyphraseContentParser();
foreach (IContent content in saveEventArgs.SavedEntities)
{
if (content.ContentType.Alias.Equals("NewsArticle"))
{
var blogContent = content.GetValue<string>("bodyContent");
var parsedBodyText = keyphraseContentParser.ReplaceKeyphrasesWithLinks(blogContent, keyphrases);
content.SetValue("bodyContent", parsedBodyText);
}
}
}
}
The ContentServiceOnSaving method allows us to intercept any save event in Umbraco. Afterwhich we check our incoming content to see if it's of the type we're expecting - in this example 'NewsArticle' - and if it is, then target the 'bodyContent' section, parse this with our 'KeyphraseContentParser', and swap the current 'bodyContent' with the parsed 'bodyContent'.
Create a Keyphrase parser to swap keyphrases for internal/external links
public class KeyphraseContentParser
{
public string ReplaceKeyphrasesWithLinks(string htmlContent, IEnumerable<Keyphrase> keyphrases)
{
var parsedHtmlStringBuilder = new StringBuilder(htmlContent);
foreach (var keyphrase in keyphrases)
{
if (htmlContent.CaseContains(keyphrase.Phrase, StringComparison.OrdinalIgnoreCase))
{
var index = 0;
do
{
index = parsedHtmlStringBuilder.ToString()
.IndexOf(keyphrase.Phrase, index, StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
var keyphraseSuffix = parsedHtmlStringBuilder.ToString(index, keyphrase.Phrase.Length + 4);
var keyPhraseFromContent = parsedHtmlStringBuilder.ToString(index, keyphrase.Phrase.Length);
var keyphraseTarget = "_blank";
if (keyphrase.Link.StartsWith("/"))
{
keyphraseTarget = "_self";
}
var keyphraseLinkReplacement = String.Format("<a href='{0}' target='{1}'>{2}</a>",
keyphrase.Link, keyphraseTarget, keyPhraseFromContent);
if (!keyphraseSuffix.Equals(String.Format("{0}</a>", keyPhraseFromContent)))
{
parsedHtmlStringBuilder.Remove(index, keyPhraseFromContent.Length);
parsedHtmlStringBuilder.Insert(index, keyphraseLinkReplacement);
index += keyphraseLinkReplacement.Length;
}
else
{
var previousStartBracket = parsedHtmlStringBuilder.ToString().LastIndexOf("<a", index);
var nextEndBracket = parsedHtmlStringBuilder.ToString().IndexOf("a>", index);
parsedHtmlStringBuilder.Remove(previousStartBracket, (nextEndBracket - (previousStartBracket - 2)));
parsedHtmlStringBuilder.Insert(previousStartBracket, keyphraseLinkReplacement);
index = previousStartBracket + keyphraseLinkReplacement.Length;
}
}
} while (index != -1);
}
}
return parsedHtmlStringBuilder.ToString();
}
}
It's probably easiest to step through the above code, but fundamentally the parser has to:
a) find and wrap all keyphrases, ignoring case, with a link to an internal CMS, or external web resource.
b) handle an already parsed html string to both leave links in place and not create nested links.
c) allow CMS keyphrase changes to be updated in the parsed html string.
The blog of this, as well as the github code can be found from the links in the previous post.
Ok, so after finding some excellent helper posts and digging around I came up with the solution, which I've written about here:
http://frazzledcircuits.blogspot.co.uk/2015/03/umbraco-7-automatic-keyphrase.html
And the source code is here:
https://github.com/AdTarling/UmbracoSandbox

How can I use knockout.js to bind data in my C# code behind to a select element on the front-end?

Here is the part of the UI that I'm trying to update:
<label>Country</label>
<select name="country" data-bind="options: $parent.CountryList, optionsCaption: '- Select -'"></select>
As you can see, I tried $parent.CountryList because I was hoping that would refer to the CountryList in the code behind. Here is a snippet of the Page_Load function where I'm storing data from a database into a list of countries:
using (CCGEntities db = new CCGEntities())
{
List<Country> CountryList = db.Countries.ToList();
}
The goal is to take the list of countries and have them populate the select element as a dropdown menu. I tried mimicking the binding for asp:DropDownList but the code behind didn't pick up on an ID attribute for the select element. Would I be better of doing this with asp:DropDownList?
That is not how you must do it in ASP.NET. You must create ViewModels and bind them using ko.applybindings(viewModel, YOUR_HTML_ELEMENT_ROOT). In order to do that, the model should either:
a) Be serialized at run time by the server and dumped into a javascript variable
or b) Fetched dynamically with javascript and then apply the binding
A complete example of how to achieve this with Entity Framework, ASP.NET and Knockout is available here.
Dropdownlist population with knockout.js can seem a bit tricky at first. I ve implemented a knockout binder to make this easier:
ko.extenders.autoOptions = function (target, type) {
target["Options"] = [];
console.log("getting options for type: " + type);
target.Options = ApplicationGateway.getOptions([ApplicationName.Api.Options]+type);
return target;
};
This binder can be used in the following manner in your javascript models:
self.Gender = ko.observable().extend({ autoOptions: 'GENDER', required: { message: 'Gender is required' } });
Which results in the following template usage:
<select data-bind="options: Gender.Options,
value: Gender,optionsText: 'Text',optionsValue: 'Value'">
</select>
My Asp.Net Web API Options Controller looks like this:
public class OptionsController : ApiController
{
private ResourceManager _resourceManager;
private CultureInfo _cultureInfo;
[HttpGet]
[Route("api/Options/{type}")]
public List<ListItem> List(string type)
{
_resourceManager = new ResourceManager("Application.Resources.RESOURCE", typeof(APPLICATION).Assembly);
_cultureInfo = new CultureInfo(Application.CurrentSession.User.LanguageSelected);
switch (type.ToUpper())
{
case "GENDER": return Gender();
...
}
return new List<ListItem>();
}
private List<ListItem> Gender()
{
var items = new List<ListItem>
{
new ListItem(_resourceManager.GetString("Label_Gender_Male", _cultureInfo),Domain.Enums.Gender.Male.ToString()),
new ListItem(_resourceManager.GetString("Label_Gender_Female", _cultureInfo), Domain.Enums.Gender.Female.ToString()),
};
return items;
}
}

Ext.net howto access treepanel nodes

I am a beginer in ext.net. I have created a simple tree with dynamic loading using proxy from out database.
#{
Layout = null;
}
#{
var thisPlugin = Model as IPlugin;
if (thisPlugin == null)
{
throw new Exception("IPlugin expected");
}
var loadChildrenFunc = new Func<string>(() =>
{
string retVal = Url.Action("GetChildren/" + Url.Encode(thisPlugin.Id));
return retVal;
});
var tree = Html.X().TreePanel().ID("FACILITY_TREE");
}
#(tree.Title("Facility tree")
.Icon(Icon.Table)
.Frame(true)
.Height(450)
.Width(300)
.Border(false)
.Store(Html.X().TreeStore().Proxy(Html.X().AjaxProxy().Url(loadChildrenFunc())))
.Root(Html.X().Node().NodeID("0").Text("Facility objects tree")))
Treepanel succesfuly created and loads nodes.
The problem I have is to acces tree to save it state.
For example then expand/collapse nodes i need to call controller and save treeview state. So the question is how to solve it?
Anoter, after click/double click i need to call JS function to modify some data on page.
Please help. Thanks!
i think you need to add a event listener.
like this
listeners: {
collapse: function() {
alert('collapsed');
},
expand: function() {
alert('expand')
},
itemclick: function(s,r) {
alert(r.data.text);
}
}
hope this helps

How could I find an element with similar ID in JS?

In my page I have labels that look like this:
ContentPlaceHolder1_gvGroups_lblName_0
with corrosponding:
ContentPlaceHolder1_gvGroups_lblHidden_0
I can assert that any *lblName has a corrosponding *lblHidden.
Now, I have some js:
//use to make any label with 'edit' class editable
function makeLabelsEditable() {
$(".edit").focusout(function () {
setLabel(this);
});
$(".edit").click(function () {
editLabel(this);
});
}
//used to edit labels
function editLabel(source) {
source.innerHTML = '<input type="text" maxlength="40" value="' + source.innerHTML + '"/>';
$(source).unbind('click');
source.children[0].focus()
}
//used to edit labels
function setLabel(source) {
if (source.children[0].value != '') {
$(source).click(function () {
editLabel(this);
});
source.innerHTML = source.children[0].value;
}
}
I can assert that anything marked with class edit will be valid.
I need to modify this code like so:
//used to edit labels
function setLabel(source) {
if (source.children[0].value != '') {
$(source).click(function () {
editLabel(this);
});
source.innerHTML = source.children[0].value;
var hidden = someHowGetHiddenFromSource(source);
hidden.innerHTML = source.children[0].value;
}
}
I'm sure this is possible, I just do not know how.
Essentially, it would be something like:
getElementByID(replace(source.id,'lblName','lblHidden'));
I just do not know what JS / JQ functions can do that.
Thanks
Essentially, it would be something like:
getElementByID(replace(source.id,'lblName','lblHidden'));
That's almost exactly it. It's:
var hidden = document.getElementById(source.id.replace('lblName', 'lblHidden'));
hidden will be the DOM element.
Or using jQuery:
var hidden = $("#" + source.id.replace('lblName', 'lblHidden'));
hidden will be a jQuery instance wrapping the element.
The id property of DOM elements is a string. JavaScript strings have a replace function which accepts, in the simple case, the string to replace and the replacement string. (There's more to it than that, but that's all you need in this case.)

Categories