nearly got tag typeahead working

This commit is contained in:
Shannon
2014-05-05 19:07:57 +10:00
parent 27e6b8c80c
commit d2a20b10aa
9 changed files with 128 additions and 42 deletions

View File

@@ -1,7 +1,7 @@
namespace Umbraco.Core.Media
{
//NOTE: Could definitely have done with a better name
//TODO: Could definitely have done with a better name
public class Result
{
public Status Status { get; set; }

View File

@@ -188,6 +188,11 @@ angular.module('umbraco.services')
*/
load: function (pathArray, scope) {
var promise;
if (!angular.isArray(pathArray)) {
throw "pathArray must be an array";
}
var nonEmpty = _.reject(pathArray, function(item) {
return item === undefined || item === "";
});

View File

@@ -1,20 +1,20 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.TagsController",
function ($rootScope, $scope, $log, assetsService) {
function ($rootScope, $scope, $log, assetsService, umbRequestHelper) {
//load current value
$scope.currentTags = [];
if ($scope.model.value) {
$scope.currentTags = $scope.model.value.split(",");
}
$scope.addTag = function(e) {
$scope.addTag = function (e) {
var code = e.keyCode || e.which;
if (code == 13) { //Enter keycode
//this is required, otherwise the html form will attempt to submit.
e.preventDefault();
if ($scope.currentTags.indexOf($scope.tagToAdd) < 0) {
$scope.currentTags.push($scope.tagToAdd);
}
@@ -41,5 +41,56 @@ angular.module("umbraco")
$scope.currentTags = $scope.model.value.split(",");
};
assetsService.loadJs("lib/typeahead/typeahead.bundle.min.js").then(function () {
//configure the tags data source
var tagsHound = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace("value"),
queryTokenizer: Bloodhound.tokenizers.whitespace,
//pre-fetch the tags for this category
prefetch: {
url: umbRequestHelper.getApiUrl("tagsDataBaseUrl", "GetTags", [{ tagGroup: $scope.model.config.group }]),
//TTL = 5 minutes
ttl: 300000,
filter: function (list) {
return _.map(list, function (i) {
return { value: i.text };
});
}
},
//dynamically get the tags for this category
remote: {
url: umbRequestHelper.getApiUrl("tagsDataBaseUrl", "GetTags", [{ tagGroup: $scope.model.config.group }]),
filter: function (list) {
return _.map(list, function (i) {
return { value: i.text };
});
}
}
});
tagsHound.initialize();
//configure the type ahead
$('#tags-' + $scope.model.alias).typeahead(
//use the default options
null, {
//see: https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md#options
// name = the data set name, we'll make this the tag group name
name: $scope.model.config.group,
// apparently thsi should be the same as the value above in the call to Bloodhound.tokenizers.obj.whitespace
// this isn't very clear in the docs but you can see that it's consistent with this statement here:
// http://twitter.github.io/typeahead.js/examples/
displayKey: "value",
source: tagsHound.ttAdapter()
});
});
//on destroy:
// $('.typeahead').typeahead('destroy');
}
);

View File

@@ -1,8 +1,10 @@
<div ng-controller="Umbraco.PropertyEditors.TagsController" class="umb-editor umb-tags">
<span ng-repeat="tag in currentTags" ng-click="removeTag(tag)" class="label label-primary tag">{{tag}} <i class="icon icon-delete"></i></span>
<input type="text"
ng-keydown="addTag($event)"
placeholder="Type to add tags.."
ng-model="tagToAdd" />
<span ng-repeat="tag in currentTags" ng-click="removeTag(tag)" class="label label-primary tag">{{tag}} <i class="icon icon-delete"></i></span>
<input type="text"
id="tags-{{model.alias}}"
ng-keydown="addTag($event)"
placeholder="Type to add tags.."
ng-model="tagToAdd" />
</div>

View File

@@ -209,6 +209,10 @@ namespace Umbraco.Web.Editors
{
"contentTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"tagsDataBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TagsDataController>(
controller => controller.GetTags(""))
}
}
},

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

View File

@@ -0,0 +1,24 @@
using System.Collections.Generic;
using Umbraco.Web.Editors;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.PropertyEditors
{
/// <summary>
/// A controller used for type-ahead values for tags
/// </summary>
/// <remarks>
/// DO NOT inherit from UmbracoAuthorizedJsonController since we don't want to use the angularized
/// json formatter as it causes probs.
/// </remarks>
[PluginController("UmbracoApi")]
public class TagsDataController : UmbracoAuthorizedApiController
{
public IEnumerable<TagModel> GetTags(string tagGroup)
{
return Umbraco.TagQuery.GetAllTags(tagGroup);
}
}
}

View File

@@ -356,6 +356,7 @@
<Compile Include="Models\PostRedirectModel.cs" />
<Compile Include="Models\PublishedProperty.cs" />
<Compile Include="Mvc\RedirectToUmbracoUrlResult.cs" />
<Compile Include="PropertyEditors\TagsDataController.cs" />
<Compile Include="PublishedCache\MemberPublishedContent.cs" />
<Compile Include="PublishedCache\RawValueProperty.cs" />
<Compile Include="Models\RenderModelOfTContent.cs" />

View File

@@ -7,6 +7,7 @@ using System.Net.Http;
using System.Net.Http.Formatting;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Umbraco.Core.Logging;
namespace Umbraco.Web.WebApi
@@ -19,6 +20,7 @@ namespace Umbraco.Web.WebApi
/// </remarks>
public class AngularJsonMediaTypeFormatter : JsonMediaTypeFormatter
{
/// <summary>
/// This will prepend the special chars to the stream output that angular will strip
/// </summary>
@@ -28,43 +30,41 @@ namespace Umbraco.Web.WebApi
/// <param name="content"></param>
/// <param name="transportContext"></param>
/// <returns></returns>
public async override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
//Before we were calling the base method to do this however it was causing problems:
// http://issues.umbraco.org/issue/U4-4546
// though I can't seem to figure out why the null ref exception was being thrown, it is very strange.
// This code is basically what the base class does and at least we can track/test exactly what is going on.
if (type == null) throw new ArgumentNullException("type");
if (writeStream == null) throw new ArgumentNullException("writeStream");
using (var memStream = new MemoryStream())
var task = Task.Factory.StartNew(() =>
{
try
{
//Let the base class do all the processing using our custom stream
await base.WriteToStreamAsync(type, value, memStream, content, transportContext);
}
catch (Exception ex)
{
LogHelper.Error<AngularJsonMediaTypeFormatter>("An error occurred writing to the output stream", ex);
throw;
}
var effectiveEncoding = SelectCharacterEncoding(content == null ? null : content.Headers);
memStream.Flush();
memStream.Position = 0;
//read the result string from the stream
// (see: http://docs.angularjs.org/api/ng.$http)
string output;
using (var reader = new StreamReader(memStream))
using (var streamWriter = new StreamWriter(writeStream, effectiveEncoding))
using (var jsonTextWriter = new JsonTextWriter(streamWriter)
{
output = reader.ReadToEnd();
}
//pre-pend the angular chars to the result
output = ")]}',\n" + output;
//write out the result to the original stream
using (var writer = new StreamWriter(writeStream))
CloseOutput = false
})
{
writer.Write(output);
//write the special encoding for angular json to the start
// (see: http://docs.angularjs.org/api/ng.$http)
streamWriter.Write(")]}',\n");
if (Indent)
{
jsonTextWriter.Formatting = Formatting.Indented;
}
var jsonSerializer = JsonSerializer.Create(SerializerSettings);
jsonSerializer.Serialize(jsonTextWriter, value);
jsonTextWriter.Flush();
}
}
});
return task;
}
}