How to use tagit with knockout
-
06-07-2021 - |
Question
i am using http://aehlke.github.com/tag-it/ in my code how to bind with viewmodel
html
<ul data-bind='jqueryui: "tagit",foreach: Tags' >
<li class="tagit-choice ui-widget-content ui-state-default ui-corner-all" data-bind='with: $data'>
<span class="tagit-label" data-bind='text: $data'></span>
<a class="tagit-close">
<span class="text-icon">×</span>
<span class="ui-icon ui-icon-close"></span>
</a>
<input type="hidden" name="item[tags][]" data-bind='value: $data' style="display: none;">
</li>
</ul>
Js code
function AppViewModel() {
var self = this;
function Tag(data) {
this.Name = data;
}
self.Tags = ko.observableArray([
new Tag('red'),
new Tag('blue'),
new Tag('green')
]);
}
// Activates knockout.js
ko.applyBindings(new AppViewModel());
Thanks in advance for your assistance!
Solution 4
thanks Cedric no need to write custom binder
i solve this way link
$("#mytags").tagit({
availableTags: JSON.parse(ko.toJSON(_.pluck(AppViewModel.Tags, 'Name'))),
onTagAdded: function (event, tagval) {
//on every add add with id if tag exist in system
var newtag = $(tagval).children('span.tagit-label').html();
var temp = _.find(AppViewModel.Tags, function (item) { return item.Name() == newtag; });
if (temp) {
AppViewModel().SelectedTags.push( Tag({ 'Id': temp.Id(), "Name": newtag} ));
}
else {
AppViewModel().SelectedTags.push( Tag({ "Name": newtag} ));
}
},
onTagRemoved: function (event, tagval) {
// do something special
var tempTag = $(tagval).children('span.tagit-label').html();
AppViewModel().SelectedTags.remove(_.find(SelectedTags(), function (item) { return item.Name == tempTag; }));
}});
OTHER TIPS
Here's a another binding handler for knockout based on Robert Wagner's answer, since i didnt feel that it was dynamic enough:
ko.bindingHandlers.tagit = {
//https://github.com/aehlke/tag-it
init: function (element, valueAccessor, allBindingsAccessor) {
var bind = function () {
valueAccessor().tags($(element).tagit("assignedTags"));
};
var options = $.extend({
allowSpaces: false,
caseSensitive: false,
showAutocompleteOnFocus: true,
afterTagAdded: function(t,s) { bind(); },
afterTagRemoved: function(t,s) { bind(); },
placeholderText: "",
preprocessTag: function () { },
beforeTagAdded: function (evt, tag) {
if (tag.tagLabel.length == 0 || tag.tagLabel.toLowerCase() === options.placeholderText.toLowerCase()) {
return false;
}
return true;
}
}, valueAccessor().options || {});
var tags = valueAccessor()["autocomplete"];
if (tags)
$.extend(options, {
autocomplete: $.extend({ source: tags.source, delay: 0, minLength: 0 },tags)
});
$(element).tagit(options);
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
var tags = value.tags();
$(element).tagit("removeAll");
for (var x = 0; x < tags.length; x++) {
$(element).tagit("createTag", tags[x]);
}
}
};
My preprocess and autocompleter functions:
self.TagAutocompleter = function (d, ds) {
DataMethod.postData("tag/autocomplete", d, function (data) {
ds(ko.utils.arrayMap(data, function (t) { return t.Tag; }));
});
};
self.TagProcessor = function (tag) {
return tag.toLowerCase().replace("#", '');
};
And use in html:
<ul data-bind="tagit:{tags:Tags, autocomplete:{source:TagAutocompleter, delay:250, minLength: 2}, options:{preprocessTag: TagProcessor}}">
</ul>
Here is a custom binding https://gist.github.com/droyad/6136446
ko.bindingHandlers.tags = {
init: function (element, valueAccessor, allBindingsAccessor) {
var bind = function() {
var observable = valueAccessor();
observable($(element).tagit("assignedTags").join(','));
};
var options = {
allowSpaces: true,
caseSensitive: false,
showAutocompleteOnFocus: true,
afterTagAdded: bind,
afterTagRemoved: bind
};
var tags = allBindingsAccessor()["tagsSource"];
if (tags)
$.extend(options, {
autocomplete: { source: tags, delay: 0, minLength: 0 }
});
$(element).tagit(options);
$(element).data('uiTagit').tagInput.css("width", "50px");
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
var tags = value.split(',');
$(element).tagit("removeAll");
for (var x = 0; x < tags.length; x++) {
$(element).tagit("createTag", tags[x]);
}
}
}
I write a simple fiddle where it's work. It's construct component with de tag list. Fiddle
But it's not a two ways binding. If you wan't to do that i advise you to create a custom binder where it call the knockout's foreach model binder . See information to Custom model binders
On the init function, you need to subscibe to tags changes in knockout observableArray for update the control. And you need to subscribe to onTagAdded event and onTagRemoved event.
There is a sample code where i extend the foreach component:
ko.bindingHandlers.extendForeach = {
makeForeachValueAccessor: function (valueAccessor) {
return function () {
if ((!bindingValue) || typeof bindingValue.length == "number"){
bindingValue = {
data : bindingValue
}
}
return {
'data': bindingValue['data'],
'afterAdd': bindingValue['afterAdd'],
'beforeRemove': bindingValue['beforeRemove'],
'afterRender': bindingValue['afterRender'],
};
};
},
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var newValAccess = ko.bindingHandlers.extendForeach.makeForeachValueAccessor(valueAccessor);
return ko.bindingHandlers.foreach.init(element, newValAccess, allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var newValAccess = ko.bindingHandlers.extendForeach.makeForeachValueAccessor(valueAccessor);
return ko.bindingHandlers.foreach.update(element, newValAccess, allBindingsAccessor, viewModel, bindingContext);
}
}
I hope this help you.