Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use tagit with knockout

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">&times;</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!

like image 395
rjdmello Avatar asked Oct 12 '12 08:10

rjdmello


3 Answers

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>
like image 146
Michael Kire Hansen Avatar answered Nov 05 '22 03:11

Michael Kire Hansen


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]);
        }
    }
}
like image 27
Robert Wagner Avatar answered Nov 05 '22 02:11

Robert Wagner


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.

like image 1
Cedric Avatar answered Nov 05 '22 02:11

Cedric