#18182: Implementing Autocompleted on Tags

Work Item: 18182

--HG--
branch : 1.x
This commit is contained in:
piedone
2011-11-02 15:15:29 -07:00
parent bc7be146f0
commit a1d749423e
10 changed files with 127 additions and 4 deletions

View File

@@ -63,5 +63,12 @@ namespace Orchard.Tags.Controllers {
return View(viewModel);
}
public JsonResult FetchSimilarTags(string snippet) {
return Json(
_tagService.GetTagsByNameSnippet(snippet).Select(tag => tag.TagName).ToList(),
JsonRequestBehavior.AllowGet
);
}
}
}

View File

@@ -8,7 +8,7 @@ Features:
Orchard.Tags:
Name: Tags
Description: The tags module is providing basic tagging for arbitrary content types.
Dependencies: Settings
Dependencies: Settings, Orchard.jQuery
Category: Navigation
Orchard.Tags.Projections:
Name: Tags Projections

View File

@@ -82,6 +82,7 @@
</ItemGroup>
<ItemGroup>
<Content Include="Module.txt" />
<Content Include="Scripts\orchard-tags-autocomplete.js" />
<Content Include="Styles\images\menu.tags.png" />
<Content Include="Styles\menu.tags-admin.css" />
<Content Include="Styles\orchard-tags-admin.css" />
@@ -117,6 +118,9 @@
<ItemGroup>
<Content Include="web.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Scripts\Web.config" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@@ -3,7 +3,11 @@ using Orchard.UI.Resources;
namespace Orchard.Tags {
public class ResourceManifest : IResourceManifestProvider {
public void BuildManifests(ResourceManifestBuilder builder) {
builder.Add().DefineStyle("TagsAdmin").SetUrl("orchard-tags-admin.css");
var manifest = builder.Add();
manifest.DefineScript("TagsAutocomplete").SetUrl("orchard-tags-autocomplete.js").SetDependencies("jQueryUI_Autocomplete");
manifest.DefineStyle("TagsAdmin").SetUrl("orchard-tags-admin.css");
}
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.web>
<httpHandlers>
<!-- iis6 - for any request in this location, return via managed static file handler -->
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers accessPolicy="Script,Read">
<!--
iis7 - for any request to a file exists on disk, return it via native http module.
accessPolicy 'Script' is to allow for a managed 404 page.
-->
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
</handlers>
</system.webServer>
</configuration>

View File

@@ -0,0 +1,57 @@
(function ($) {
$.extend({
tagsAutocomplete: {
minSnippetLength: 3,
autocomplete: function (textboxId, fetchUrl, minSnippetLength) {
if (minSnippetLength != null) this.minSnippetLength = minSnippetLength;
function split(val) {
return val.split(/,\s*/);
}
function extractLast(snippet) {
return split(snippet).pop();
}
var that = this;
var textBox = $('#' + textboxId);
textBox.bind('keydown', function (event) {
// don't navigate away from the field on tab when selecting an item
if (event.keyCode === $.ui.keyCode.TAB && $(this).data('autocomplete').menu.active) {
event.preventDefault();
}
}).autocomplete({
source: function (request, response) {
$.getJSON(fetchUrl, {
snippet: extractLast(request.term)
}, response);
},
appendTo: textBox.parent(),
search: function () {
// custom minLength
var snippet = extractLast(this.value);
if (snippet.length < that.minSnippetLength) {
return false;
}
},
focus: function () {
// prevent value inserted on focus
return false;
},
select: function (event, ui) {
var snippets = split(this.value);
// remove the current input
snippets.pop();
// add the selected item
snippets.push(ui.item.value);
// add placeholder to get the comma-and-space at the end
snippets.push('');
this.value = snippets.join(', ');
return false;
}
});
}
}
});
})(jQuery);

View File

@@ -5,6 +5,14 @@ using Orchard.Tags.Models;
namespace Orchard.Tags.Services {
public interface ITagService : IDependency {
IEnumerable<TagRecord> GetTags();
/// <summary>
/// Returns tags whose name start with snippet
/// </summary>
/// <param name="snippet">The starting snippet</param>
/// <param name="maxCount">Maximum number of tags returned</param>
/// <returns>Tags found</returns>
IEnumerable<TagRecord> GetTagsByNameSnippet(string snippet, int maxCount = 10);
TagRecord GetTag(int tagId);
TagRecord GetTagByName(string tagName);
IEnumerable<IContent> GetTaggedContentItems(int tagId);

View File

@@ -41,6 +41,11 @@ namespace Orchard.Tags.Services {
return _tagRepository.Table.ToList();
}
public IEnumerable<TagRecord> GetTagsByNameSnippet(string snippet, int maxCount = 10) {
if (String.IsNullOrEmpty(snippet)) return null; // Otherwise would return the whole dataset
return _tagRepository.Fetch(tag => tag.TagName.StartsWith(snippet)).Take(maxCount);
}
public TagRecord GetTag(int tagId) {
return _tagRepository.Get(x => x.Id == tagId);
}

View File

@@ -8,4 +8,4 @@
}
.orchard-tags .manage label[for=TagName] {
display:none;
}
}

View File

@@ -1,6 +1,23 @@
@model Orchard.Tags.ViewModels.EditTagsViewModel
@{
Style.Require("jQueryUI_Orchard");
Script.Require("TagsAutocomplete");
}
<fieldset>
@Html.LabelFor(m => m.Tags, T("Tags"))
@Html.TextBoxFor(m => m.Tags, new { @class = "large text" })
</fieldset>
</fieldset>
@using (Script.Foot()) {
<script type="text/javascript">
//<![CDATA[
$(function () {
$.tagsAutocomplete.autocomplete(
'@Html.FieldIdFor(m => m.Tags)',
'@Url.Action("FetchSimilarTags", new { Controller = "Home", Area = "Orchard.Tags" })', 1);
});
//]]>
</script>
}