Удалить элемент из списка с помощью Knockout.js

Я пытаюсь удалить элемент из списка. Я использую Knockout.js с плагином сопоставления. Мой код выглядит так:

Сериализировать в Json

@{ var jsonData = new HtmlString(new JavaScriptSerializer().Serialize(Model));}

Шаблон

<script type="text/html" id="imgsList">
    {{each model.Imgs}}
        <div style="float:left; margin: 10px 10px 10px 0;">
            <div><a href="${Filename}"><img src="${Filename}" style="width:100px;"></img></a></div>
            <div data-bind="click: deleteImage">Delete</div>
        </div>
    {{/each}}
</script>

К.О. JavaScript

<script type="text/javascript">
     $(function() {
        //KO Setup
        var viewModel = { 
            "model": ko.mapping.fromJS(@jsonData),
            "deleteImage" : function(item) {alert(item.Filename + ' deleted.');}
        }

        ko.applyBindings(viewModel);
    });
</script>

HTML

<div data-bind="template: 'imgsList'"></div>

Вопрос

Все работает так, как ожидалось. Однако список изображений отображается с кнопками удаления, когда вы нажимаете элемент кнопки. Имя файла не определено. Мысли?

Изменить: взято из руководства KNockout.js: «При вызове вашего обработчика Knockout предоставит текущее значение модели в качестве первого параметра. Это особенно полезно, если вы визуализируете некоторый пользовательский интерфейс для каждого элемента в коллекции, и вам нужно узнать, какой элемент пользовательского интерфейса был нажат».

Похоже, я не получаю ожидаемый объект Img. Я не знаю, что я получаю обратно!


person Jason    schedule 02.01.2012    source источник


Ответы (2)


Когда вы используете синтаксис {{each}} в шаблонах jQuery, контекст данных — это то, с чем связан общий шаблон. В вашем случае это вся модель представления.

Несколько вариантов:

1- вы можете использовать свой текущий код и передать элемент, который вы «каждый» используете, в функцию, например ( http://jsfiddle.net/rniemeyer/qB9tp/1/):

<div data-bind="click: function() { $root.deleteImage($value); }">Delete</div>

Однако использование анонимной функции в привязке данных довольно уродливо. Есть лучшие варианты.

2- вы можете использовать параметр foreach привязки шаблона, который работает с шаблонами jQuery и более эффективен, чем {{каждый}} как ( http://jsfiddle.net/rniemeyer/qB9tp/2/):

<script type="text/html" id="imgsList">
    <div style="float:left; margin: 10px 10px 10px 0;">
        <div>
            <a href="${Filename}">${Filename}</a>
        </div>
        <div data-bind="click: $root.deleteImage">Delete</div>
    </div>
</script>

<div data-bind="template: { name: 'imgsList', foreach: model.Imgs }"></div>

Теперь контекстом шаблона является отдельный объект изображения, и вызов $root.deleteImage передаст его в качестве первого аргумента.

3- Поскольку плагин jQuery Templates устарел, а Knockout теперь поддерживает собственные шаблоны, вы можете удалить свою зависимость от плагина jQuery Templates. Вы все еще можете использовать именованный шаблон (просто нужно заменить любой синтаксис шаблонов jQuery атрибутами привязки данных), например: http://jsfiddle.net/rniemeyer/qB9tp/3/ или даже удалить шаблон и просто использовать привязку потока управления foreach, например: http://jsfiddle.net/rniemeyer/qB9tp/4/

<div data-bind="foreach: model.Imgs">
    <div style="float:left; margin: 10px 10px 10px 0;">
        <div>
            <a data-bind="text: Filename, attr: { href: Filename }"></a>
        </div>
        <div data-bind="click: $root.deleteImage">Delete</div>
    </div>
</div>

4- Хотя я предпочитаю вариант № 3, вы даже можете использовать делегирование событий и прикрепить «живой» обработчик, например: http://jsfiddle.net/rniemeyer/qB9tp/5/

$("#main").on("click", ".del", function() {
   var data = ko.dataFor(this);
   viewModel.deleteImage(data); 
});

Это может быть особенно полезно, если вы будете присоединять большое количество одних и тех же обработчиков через привязку click (например, в сетке).

person RP Niemeyer    schedule 03.01.2012
comment
Удивительный ответ. Я остановился на варианте 3.2 после прочтения об устаревании jquery.tmpl. Однако я не знал, что у KO теперь есть встроенная поддержка. Рад, что теперь у меня есть все эти возможности в заднем кармане. Спасибо! - person Jason; 05.01.2012

Я заметил, что здесь есть пример того, как это сделать:

http://blog.stevensanderson.com/2011/12/21/knockout-2-0-0-released/

Ознакомьтесь с 4. Более чистая обработка событий, где Стив показывает пример удаления элемента из списка.

<h3>Products</h3>

<ul data-bind="foreach: products">
    <li>
        <strong data-bind="text: name"></strong>
        <button data-bind="click: $parent.removeProduct">Delete</button>
    </li>
</ul>

Javascript:

    function appViewModel() {
        var self = this;
        self.products = ko.observableArray([
            { name: "XBox" },
            { name: "PlayStation" },
            { name: "Banana" },
            { name: "Wii" }
        ]);

        self.removeProduct = function(product) {
            self.products.remove(product);   
        }
    };

ko.applyBindings(new appViewModel());

Но учтите, что приведенный выше пример относится к последней версии KnockoutJS 2.0.

person Jason Evans    schedule 02.01.2012
comment
Да, это работает как чемпион, когда я делаю это так. Проблема в том, что когда я использую шаблоны, они перестают работать. - person Jason; 03.01.2012
comment
Похоже, вы используете шаблоны jQuery, которые не работают так же, как собственные шаблоны с KO. Что вы хотели использовать? - person John Papa; 03.01.2012
comment
@Jason, ты должен использовать нативные шаблоны KO. jquery.tmpl — это своего рода забытая технология, и никаких обновлений в будущем не планируется. - person Judah Gabriel Himango; 03.01.2012
comment
Мне было интересно, что делать, потому что я читал о том, что jquery.tmpl устарел. Он все еще находится на гитхабе KO, поэтому я подумал, что сообщество KO сейчас поддерживает его или что-то в этом роде. Рад узнать, что у KO есть встроенная поддержка, и я могу просто удалить tmpl. - person Jason; 05.01.2012