Giter Club home page Giter Club logo

knockout-sortable's Introduction

knockout-sortable is a binding for Knockout.js designed to connect observableArrays with jQuery UI's sortable functionality. This allows a user to drag and drop items within a list or between lists and have the corresponding observableArrays updated appropriately.

Basic Usage

  • using anonymous templates:
<ul data-bind="sortable: items">
  <li data-bind="text: name"></li>
</ul>
  • using named templates:
<ul data-bind="sortable: { template: 'itemTmpl', data: items }"></ul>
<script id="itemTmpl" type="text/html"><li data-bind="text: name"></li></script>

Note: The sortable binding assumes that the child "templates" have a single container element. You cannot use containerless bindings (comment-based) bindings at the top-level of your template, as the jQuery draggable/sortable functionality needs an element to operate on.

Note2: (Update: 0.9.0 adds code to automatically strip leading/trailing whitespace) When using named templates, you will have the best results across browsers, if you ensure that there is only a single top-level node inside your template with no surrounding text nodes. Inside of the top-level nodes, you can freely use whitespace/text nodes. So, you will want:

<!-- good - no text nodes surrounding template root node -->
<script id="goodTmpl" type="text/html"><li data-bind="text: name">
    <span data-bind="text: name"></span>
</li></script>

<!-- bad -->
<script id="badTmpl" type="text/html">
<li>
  <span data-bind="text: name"></span>
</li>
</script>

Additional Options

  • connectClass - specify the class that should be used to indicate a droppable target. The default class is "ko_container". This value can be passed in the binding or configured globally by setting ko.bindingHandlers.sortable.connectClass.

  • allowDrop - specify whether this container should be a target for drops. This can be a static value, observable, or a function that is passed the observableArray as its first argument. If a function is specified, then it will be executed in a computed observable, so it will run again whenever any dependencies are updated. This option can be passed in the binding or configured globally by setting ko.bindingHandlers.sortable.allowDrop.

  • beforeMove - specify a function to execute prior to an item being moved from its original position to its new position in the data. This function receives an object for its first argument that contains the following information:

    • arg.item - the actual item being moved
    • arg.sourceIndex - the position of the item in the original observableArray
    • arg.sourceParent - the original observableArray
    • arg.sourceParentNode - the container node of the original list
    • arg.targetIndex - the position of the item in the destination observableArray
    • arg.targetParent - the destination observableArray
    • arg.cancelDrop - this defaults to false and can be set to true to indicate that the drop should be cancelled.

    This option can be passed in the binding or configured globally by setting ko.bindingHandlers.sortable.beforeMove. This callback also receives the event and ui objects as the second and third arguments.

  • afterMove - specify a function to execute after an item has been moved to its new destination. This function receives an object for its first argument that contains the following information:

    • arg.item - the actual item being moved
    • arg.sourceIndex - the position of the item in the original observableArray
    • arg.sourceParent - the original observableArray
    • arg.sourceParentNode - the container node of the original list. Useful if moving items between lists, but within a single array. The value of this in the callback will be the target container node.
    • arg.targetIndex - the position of the item in the destination observableArray
    • arg.targetParent - the destination observableArray

    This option can be passed in the binding or configured globally by setting ko.bindingHandlers.sortable.afterMove. This callback also receives the event and ui objects as the second and third arguments.

  • dragged - specify a function to execute after a draggable item has been dropped into a sortable. This callback receives the drag item as the first argument, the event as the second argument, and the ui object as the third argument. If the function returns a value, then it will be used as item that is dropped into the sortable. This can be used as an alternative to the original item including a clone function.

  • isEnabled - specify whether the sortable widget should be enabled. If this is an observable, then it will enable/disable the widget when the observable's value changes. This option can be passed in the binding or configured globally by setting ko.bindingHandlers.sortable.isEnabled.

  • strategyMove - specify whether dropping an item within the same list should move the same item to the new index rather than removing and re-adding the item in the new location (which is the default and causes the item to be re-rendered). This option can be passed in the binding or configured globally by setting ko.bindingHandlers.sortable.strategyMove. The default value is false.

  • options - specify any additional options to pass on to the .sortable jQuery UI call. These options can be specified in the binding or specified globally by setting ko.bindingHandlers.sortable.options.

  • afterAdd, beforeRemove, afterRender, includeDestroyed, templateEngine, as - this binding will pass these options on to the template binding.

Draggable binding

This library also includes a draggable binding that you can place on single items that can be moved into a sortable collection. When the item is dropped into a sortable, the plugin will attempt to call a clone function on the item to make a suitable copy of it, otherwise it will use the item directly. Additionally, the dragged callback can be used to provide a copy of the object, as described above.

  • using anonymous templates:
<div data-bind="draggable: item">
  <span data-bind="text: name"></span>
</div>
  • using named templates:
<div data-bind="draggable: { template: 'itemTmpl', data: item }"></div>
<script id="itemTmpl" type="text/html"><span data-bind="text: name"></span></script>

Additional Options

  • connectClass - specify a class used to indicate which sortables that this draggable should be allowed to drop into. The default class is "ko_container". This value can be passed in the binding or configured globally by setting ko.bindingHandlers.draggable.connectClass.

  • isEnabled - specify whether the draggable widget should be enabled. If this is an observable, then it will enable/disable the widget when the observable's value changes. This option can be passed in the binding or configured globally by setting ko.bindingHandlers.draggable.isEnabled.

  • options - specify any additional options to pass on to the .draggable jQuery UI call. These options can be specified in the binding or specified globally by setting ko.bindingHandlers.draggable.options.

Droppable binding

This library also includes a droppable binding that you can place on items which are targets for any draggable item. The binding can update an observable or a simple function on your viewmodel.

<div data-bind="droppable: dropTo">
  <span>Drop Items Here</span>
</div>

Additional options

  • isEnabled - specify whether the droppable widget should be enabled. If this is an observable, then it will enable/disable the widget when the observable's value changes. This option can be passed in the binding or configured globally by setting ko.bindingHandlers.droppable.isEnabled.

  • options - specify any additional option to pass to the .droppable jQuery UI call. When using options, your method or observable should be provided on the data property.

<div data-bind="droppable: {data:dropTo, isEnabled:enableDrop, options:{greedy:true}}">
  <span>Drop Items Here</span>
</div

Dependencies

  • Knockout 2.0+
  • jQuery - no specific version identified yet as minimum
  • jQuery UI - If needing AMD/CommonJS support, then it requires >=1.12 (or ensuring that paths are mapped via config properly - it looks for jquery-ui/ui/widgets/sortable and jquery-ui/ui/widgets/draggable)

Touch Support - for touch support take a look at: http://touchpunch.furf.com/

Build: This project uses grunt for building/minifying.

Examples The examples directory contains samples that include a simple sortable list, connected lists, and a seating chart that takes advantage of many of the additional options.

Fiddles

License: MIT http://www.opensource.org/licenses/mit-license.php

knockout-sortable's People

Contributors

apsu avatar bago avatar carlosagsmendes avatar chadedrupt avatar davidchristiansen avatar j201 avatar jd-robbs avatar lisandropuzzolo avatar manuel-s avatar mente avatar rniemeyer avatar scottux avatar sloria avatar stephenclouse avatar thvd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

knockout-sortable's Issues

All items in a sortable column become draggable; only the parent divs should become draggable

To reproduce data bind using the sortable: binding. In the container that has the sortable: binding create some divs.

Behaves as expected:

<div data-bind="sortable:items">
   <div>item 1</div>
   <div>item 2</div>
</div>

Behaves badly:

<div data-bind="sortable:items">
   <div id="i1">item 1
      <div id="nested_div>nested</div>
   </div>
   <div id="i2">item 2</div>
</div>

In this case the nested_div is also draggable and can be dragged out of its parent.

IMO the expected behaviour is that only the direct children (i1 and i2) should be draggable. I'm fairly sure that this is how the regular jquery sortable works. But I stand to be corrected.

My workaround is to use a jquery :not selector in the options items to exclude the nested items from being draggable.

Support for "as"

Knockout-sortable could definitely benefit from an implementation of the "as" option, which has been added to the knockout foreach binding in 2.2.0.

Question regarding cancelDrop: Perform a request

I'd like to do a request to a server before I update my model. I can use beforeCancel for that. If I change args.cancelDrop to true the item won't be moved.

Is there a way to perform an ajax request and wait for the result before leaving the beforeMove function?

I thought about

beforeMove = function(args) {
    var xhr = $.ajax({url: 'http://example.com/update/', async: false})
    .fail(function(xhr, text_status) {
        if (xhr.status === 0) { // connection error
            args.cancelDrop = true;
        }
    });
}

but I know that $.ajax({async:false}) is not waiting for the request to finish.

Does anyone have a solution for this?

Keep up the great work, knockout-sortable is awesome!

global options.options clobbered by binding options.options not individually overriden

If jQuery UI Sortable options are set in ko.bindingHandlers.sortable.options and a (possibly disjoint) set of options are specified in the binding (at the "call site"), the binding options completely override all the global ones, rather than overriding them individually.

For example, these global options:

ko.bindingHandlers.sortable.options = {
    placeholder: 'ui-state-highlight',
    tolerance: 'pointer',
    forcePlaceholderSize: true,
    distance: 20
};

and this tweak at the callsite:

<ul data-bind='sortable: {data: someObsArray, options: {containment: ".modal-body"}}, template: "someTemplate"'></ul>

currently results in the jQuery UI Sortable created by the (awesome!) binding only getting option containment, not the 4 other custom global options. Pull request fixing this to follow.

No option to clone\copy an item.

hi,
its not an issue but an add on that is required in my case .
i want to copy the an item instead of moving . i following you "seating chart"
http://jsfiddle.net/rniemeyer/UdXr4/
post in my requirement i want to copy the students (available students) in to different places (groups)
a sudent can be associated with multiple groups but each group must have distinct student.

also in the ""available students list""all ungrouped students (those who do not belong to any group are coloured as red)

please help me out to do this and provide an updated sample.
Thanks!!

Sorting is funky with string literals

I'm using sortable binding on KO's observableArray with literal string values;

self.items = ko.observableArray(["foo", "bar", "duplicate", "duplicate", "duplicate"]);

When I sort any of the 'duplicate' items, the rest disappears. I understand this happens because sortable binding removes elements with obsevableArray.remove() method. It would be great if it removed items by index instead.

Setup component.json for Bower and add to repo

Since I saw this issue #41 I figured I should make an issue for adding this to Twitter's Bower package manager, especially since I already forked the project, made the required component.json file and uploaded it to Bower already :D

You can see the file here: https://github.com/Apsu/knockout-sortable/blob/master/component.json and I can request the Bower project remove my package entry (which points at my fork) and either upload yours for you, or you can upload it. Bower is quite easy to use and a fantastic tool, imo.

Create NuGet Package

I would like to see a nuget package for this script. It would require adding an xml file to the source code, and I can setup a build to auto publish from the source once that is in.

Supporting virtual elements ?

Hi,

I want to use your sortable binding with virtual elements :

`


  • Header item


  • Item

`

But when I try this, Knockout tell me that the binding doesn't support virtual elements.

So,

  1. Is it possible to bring the support of virtual elements on the sortable binding (jQueryUI limitation, ko.virtualElements missing function) ?
  2. If yes, do you think that this is representing a great development effort ?

Thanks,

Simon Berube

doesn't work properly with virtual element on ko 2.2

For sortable I use div as container and divs as items. I also tried table and trs. In template I use ko virtual element and if binding to show or hide some content depending on viewModel data. It breaks on knockout.2.2.js on line 546 if initial value of property which is bound to 'if' binding is false. Everything works file if initial value is true. I made simple example in jsFiddle.net. Try to change Task's Active to true for both items. It worked with fine with ko 2.1

http://jsfiddle.net/Shv5t/1/

incompatible with fooArray.destroy(item)

When using the destroy() method instead of remove(), sorting of the remaining items becomes erratic. Depending on the number of items eliminated, dragging items from earlier in the list to slots later in the list will actually cause the items to appear a few slots before the chosen target. It seems as though the "destroyed" items are still counted when computing the destination index. This behavior can be worked around by manually dragging all the items to the top of the list, one by one, at which point, the destroyed items are invisibly hiding at the end of the list where their indices are greater than the last visible item.

example of problem: http://jsfiddle.net/singram/zgEMZ/29/
line: self.person().aliases.remove(data); // works
change to: self.person().aliases.destroy(data); // breaks

Cross-window issues

When using a single model bound to both the current document and an iframe in it, ie with the following snippet in the iframe:

var ko = window.parent.ko;
var model = window.parent.model;

ko.applyBindings(model, document.body);

Sortable doesn't work correctly, if you try to grab a sortable item in the iframe it won't move until you move the mouse outside of the iframe. Similar to the issue described on stackoverflow. The issue being that knockout will use the jquery(ui) object of the parent window to create the sortable even though the element upon which it's called is in another document.

Currently to solve this issue i've slightly modified knockout-sortable.js slightly to make it work but that solution is not ideal:

     //connect items with observableArrays
     ko.bindingHandlers.sortable = {
         init: function(element, valueAccessor, allBindingsAccessor, data, context) {
-            var $element = $(element),
+            var ownerWindow = (element.ownerDocument.defaultView || element.ownerDocument.parentWindow);
+            var jQuery = ownerWindow.jQuery || $;
+
+            var $element = jQuery(element),
                 value = unwrap(valueAccessor()) || {},
                 templateOptions = prepareTemplateOptions(valueAccessor, "foreach"),
                 sortable = {},

JqueryUI 1.10.3 issue

It appears there is an issue with this plugin and the latest version of JqueryUI. If the user attempts to move an item to the end of a different list it tries to put the item as the second to last. See this fiddle for an example. The issue is easiest to see if you drag an item down towards the bottom of its current list before dragging to the other list. The issue only seems to occur on the first drag into the area, if you drag outside of the other list then back it seems to work as expected.

capture The red arrow is where the item will be inserted when the mouse is released, the blue arrow is where it should actually go.

UI and data can get out of sync

I found this bug while helping someone use sortable (a simpler version) with my deferred updates plugin. Here's how to reproduce (in Chrome, at least):

  1. Open the seating chart example: http://jsfiddle.net/rniemeyer/UdXr4/
  2. Drag Bobby below Ted (but don't drop it) and then put it back.
  3. Drag Jim before Ted (but don't drop it) and put it back.
  4. Drag Bobby and drop it after Ted.
  5. Now the list and data are out of sync. The list says Ted, Jim, Bobby (wrong) and the data says Ted, Bobby, Jim (correct).

Here's a link to the issue where I discussed this and provided a fix: mbest/knockout-deferred-updates#2

HTML comments in sortable binding makes dragging funny

My html structure is of the form

<ul class="something" data-bind="sortable: { allOptionsHere}">
    <!-- Some 10 lines of comment -->
    <li class="field" data-bind="template: 'someTemplate'"></li>
</ul>

Sortable initializes properly but dragging an element down in list causes elements between initial and final position skip a parent in binding.
e.g.
if elements in template "someTemplate" are $data
and element hierarchy is: $parent[2] > $parent[1] > $parent[0] > $data
after dragging element from position 0 to 4, all elements between 0 & 4 (1,2,3) will have following hierarchy: $parent[2] > $parent[1] > $data

Removing the comment solves the issue.

There is no way for the connectWith option to be false

In some case you do not want a connectWith selector to be set (eg. preventing of dragging and dropping of items between levels in a nested list). However, there does not currently seem to be a way to specify connectWith as its default value of false.

If no connectClass is specified on the plugin then it defaults to .ko_container

I have hacked the code around so that:

connectWith: connectClass ? "." + connectClass : false

and the default connectClass value is null:

connectClass: null,

This works for my situation but a more generic method is probably needed.

Uncaught TypeError: Object [object Object] has no method 'sortable'

Anyone getting this error?

Uncaught TypeError: Object [object Object] has no method 'sortable'
ko.bindingHandlers.sortable.init.controlsDescendantBindings

Just wondering if anyone knows what that means, I get the error but then it still seems to work anyway?

My html looks like:

  • The only things I added to my javascript viewmodel is:

    var Task = function (name) {
    this.name = ko.observable(name);
    };

    viewModel.selectedTask = ko.observable();
    viewModel.clearTask = function () {
        viewModel.selectedTask(null);
    };
    viewModel.selectedProject = ko.observable();
    viewModel.selectedProject.subscribe(function (newValue) {
        newValue.ObjectiveBulletList = ko.observableArray([]);
    
    
        newValue.ObjectiveBulletList.push(new Task("Test"));
    });
    

    Apparently I need to do something else but I'm not sure what I'm missing...

  • Sortable doesn't work with filtered observableArrays

    Firstly, let me thank you for Knockout, it saved me from going insane with jQuery programming. It is so nice to be able to use declarative programming, templates and bindings instead.

    Now, an issue. There is a problem when you use Sortable with dynamically filtered and sorted arrays. You splice and reorder the original array and remove all filtered out items, so they disappear after sorting.

    I am using Knockback which nicely integrates Knockout with Backbone. Here is the problem in essence:

    this.panels = ko.observableArray([
    { dock: "left", order: 0 },
    { dock: "right", order: 1 },
    { dock: "left", order: 2 },
    { dock: "right", order: 3 }
    ]);

    this.panelsLeft = kb.collectionObservable(panels, { filters: function (panel) { return panel.dock != "left"; }, sort_attribute: "order" });

    this.panelsRight = kb.collectionObservable(panels, { filters: function (panel) { return panel.dock != "right"; }, sort_attribute: "order" });

    <div data-bind="sortable: panelsLeft"> ... template

    <div data-bind="sortable: panelsRight"> ... template

    The user sorts the left panels, and all right panels disappear, and vice versa.

    Not sure how to fix the problem. It would be nice if your plugin called a callback function instead of physically reordering array items. I would simply set item.order:

    callback = function( item, newIndex, etc ) { item.order = func(index); }

    Maybe I have to modify Knockback filtering logic instead?

    CancelDrop does not seem to be working when assigned via a binding vs. globally.

    I have a binding against a collection which looks like this:

                        <div class="rletter-transactions" data-bind="sortable: { data: details, template: 'transactionDetailTemplate', beforeMove: function(arg) { $parent.canMoveTransaction(this, arg); }, afterMove: function(arg) { $parent.afterMoveTransactionToLetter(this,arg); } }">
    ...
    </div>
    
    <script type="text/html" id="transactionDetailTemplate">
    <div style="padding: 2px; margin: 3px; cursor: pointer; min-width: 200px; display: block; clear: both; background-color: White; border-radius: 4px;">
        <span data-bind="text: fundDescription"></span> -
        <span data-bind="text: withdrawlAmount().toFixed(2)"></span> -
        <span data-bind="text: withdrawlType"></span>
    </div>
    </script>

    and my Script# code looks like:

            public void CanMoveTransaction(RedemptionLettersModel self, KnockoutSortableBeforeMoveOptions<TransactionDetail> arg)
            {
                if (((int)Type.GetField(arg.TargetParent, "FundId")) != 0)
                {
                    if (arg.Item.FundCanCombineTransactionsAcrossFunds.GetValue())
                    {
                        if (((bool)Type.GetField(arg.TargetParent, "FundCanCombineTransactionsAcrossFunds")) && !arg.CancelDrop)
                        {
                            if (((RedemptionFlag)Type.GetField(arg.TargetParent, "RedemptionType")) == RedemptionFlag.Partial)
                            {
                                // all transactions within the target collection are of the same quarter id.
                                int numItems = arg.TargetParent.GetItems().Length;
                                for (int i = 0; i < numItems; i++)
                                {
                                    int targetFundId = ((int)Type.GetField(arg.TargetParent, "FundId"));
                                    int itemFundId = arg.Item.FundId.GetValue();
                                    if (targetFundId != itemFundId && !arg.CancelDrop)
                                    {
                                        Script.Alert("Cannot add this transaction into selected letter.  Transactions within selected letter are for a different period than the selected transaction.");
                                        arg.CancelDrop = true;
                                    }
                                }
                            }
                        }
                        else
                        {
                            Script.Alert("Cannot add this transaction into selected letter.  Transactions within selected letter do not allow combined transactions across funds.");
                            arg.CancelDrop = true;
                        }
                    }
                    else
                    {
                        if (((int)Type.GetField(arg.TargetParent, "FundId")) != arg.Item.FundId.GetValue() && !arg.CancelDrop)
                        {
                            Script.Alert("Cannot mix funds without having the 'Combine Funds' flag set on the Fund.");
                            arg.CancelDrop = true;
                        }
    
                        if (((RedemptionFlag)Type.GetField(arg.TargetParent, "RedemptionType")) == RedemptionFlag.Full && !arg.CancelDrop)
                        {
                            Script.Alert("Can only have 1 full redemption on this letter.");
                            arg.CancelDrop = true;
                        }
                    }
                }
            }

    When I debug the resulting javascript in Firefox, I'm getting the alerts that the arg.CancelDrop=true are being set, but the collection is still affected. Is this an issue with not binding globally, or haven't I set this up right?


    When I debug this in FireFox, and go into the section that reacts to the beforeMove call in the Sortable plugin, it seems like ui.sender should be populated, but it isn't (since source and destination lists are not equal, the code says to use ui.sender). Not sure if this is a Script# issue due to scoping, or if this is a genuine issue in the Sortable plugin for Knockout.js

    Does this work on smart phones?

    Does sortable work on mobile / smart phones / iPad etc. I've tried on iPad / Android powered phones without success. I am not able to select individual items. Whenever I tap on one item the whole list gets selected and drag/drop doesn't happen.

    live `isEnabled`

    It would be great to have a possibility of enabling/disabling a sortable list during the process of sorting another one. I pass isEnabled option with observable in the binding to a sortable <ul> list and want to manipulate it in sort events catched from another sortable <ul> list. Now isEnabled is freezed when sorting begins and is unfreezed only when sorting stops.

    UI element loses binding to viewmodel object after using CancelDrop

    Hi Ryan!
    Thanks for the plugin, it saved me a lot of time.
    Just wanted to outline an issue that i found. I've got 2 linked lists and i want to disable manual sorting inside the second list. So i used the cancelDrop option. I also have +/- buttons on the list items that are also moving the items between lists by relocating them between the observable arrays.
    The issue is that when I try to use manual sorting and the action is being cancelled by cancelDrop, and then try to move that item to the linked list by removing the assoctiated object from from one array and adding to another, the UI item element is not being destroyed. I think that could be something with the jquery.sortable("cancel") that you use, but i am not sure.

    You can replicate the issue on this example http://jsfiddle.net/igoryan/V4m5K/1/
    To do so, you will need to drag the Caption 6 in place of Caption 5. And then press + button on Caption 6. The Caption 6 will move the left list but will also stay in the right list.

    I came up with this solution. I just changed this line:
    $(arg.sourceParent === arg.targetParent ? this : ui.sender).sortable('cancel');
    to

    sourceParent.remove(item);
    sourceParent.splice(arg.sourceIndex, 0, item);
    ko.utils.domData.set(el, ITEMKEY, null);
    ui.item.remove();

    So i basically force the UI to redraw the item by removing/adding the object from array and deleting the previous UI element - that's what your plug in does when the item is allowed to drop.

    The same example with fix applied : http://jsfiddle.net/igoryan/JpJYH/8/

    Regards,
    Igor

    arg.sourceParentNode is set to the receiver not the sender

    Hi and thanks for this great contribution :)

    When I am using your library and I log:

     console.log("before Moved '" + arg.item.name() + "' from "
                + arg.sourceParentNode.id 
                + " (index: " + arg.sourceIndex + ") to "
                + event.target.id + " (index " + arg.targetIndex + ")");
    

    for arg.sourceParentNode.id I get the id of the receiving node, not the sending node.

    I think the issue is related to code around line165

    sourceParentNode: sourceParent && el.parentNode,
    

    If I insert a log stmt after that code like this: console.log("sender", ui.sender)

    Then I see the correct sender dom element.

    I believe sourceParentNode should be set to ui.sender, or so it seems to me.

    In my case, I'm going to use the ui.sender that is passed to my beforeMove handler. But unless I'm misinterpreting the purpose of the arg.sourceParentNode variable then it looks to me that arg.sourceParentNode should in fact be set to ui.sender.

    My interpretation of the variables is that I should be able to get a reference to the source's observableArray and dom node, and the target's observableArray and dom node via the arg var. At least for me that would be ideal.

    Please advise if I'm misinterpreting something.

    Many Thanks,
    Matt

    underlyingArray is undefined on a dynamically-created sortable

    I have a setup very similar to the seating chart example. However, when I create a dynamic sortable "table" (or in my case "user") and drop a student (in my case a "task") from a statically-created sortable it creates the error Uncaught TypeError: Cannot read property 'splice' of undefined.

    This sortable is created for each user in my model (which can be dynamic). I notice that when I drop a task from a pending task to a user (static sortable to dynamic sortable) the underlyingArray for the targetParent is undefined. However when I perform the reverse operation it correctly splices the underlyingArray on pending tasks.

    There are no other errors when the sortable is created, so whatever is missing is going undefined.

    How can I help provide more context and help?

    Draggable and afterReceive

    To facilitate the custom handling of a receive from a draggable, I altered the receive method like this:

    receive: function (event, ui) {
        dragItem = ko.utils.domData.get(ui.item[0], DRAGKEY);
        if (dragItem && dragItem.clone) {
            dragItem = dragItem.clone();
        }
        if (sortable.afterReceive) {
            sortable.afterReceive(this, dragItem, event, ui);
        }
    }
    

    In the sortable KO binding you can then specify the afterReceive callback function in the same way as afterMove and beforeMove.

    Are you able to add this into the next build?

    Thanks

    Chris

    isEnabled/allowDrop don't update when set by a computed expression

    I have a <div> that shows one of several sortable lists, selectable from a drop-down. Some of these lists are read-only. My binding looks like this:

    <div class="board" data-bind="css: { readonly: readOnly }>
        <select data-bind="options: lists"></select>
        <div class="board-body" data-bind="sortable: { template: 'listItemTmpl', data: items, isEnabled: !readOnly(), allowDrop: !readOnly() }">
        </div>
    </div>
    

    The property readOnly is an observable and updates when lists is changed. If the user changes from a read-only list to a writeable one, the binding doesn't update the isEnabled or allowDrop behaviors.

    My current workaround is to use a computed observable writeable that just returns !readOnly(), and this works as expected. So it's not show-stopping, but every other binding lets you use expressions at any point, so this should probably be made consistent.

    Option Start doesn't fire at Start time

    I created a sortable with the following:

    gt; div data-bind="sortable: {data: dataA]array, allowDrop: true, afterMove: dropCallback, options: {start: PresentationTransform}}, text: name.toUpperCase(), attr: { 'class': 'droppable clsFolder_' + name.toUpperCase(), 'id': name } " lt;

    Couldn't find an example to insert options, so I can't be sure that is the way to enter an option,

    If that is the way, then the PresentationTransform is not being raised by the start option at the calling of:

    $element.sortable(ko.utils.extend(sortable.options

    I tried with the beforeMove, but it is raised at Update time (although it looks like it does at the same time than afterMove.

    Is there an example for the use of options / start?

    In any case, I need to alter the presentation of the droppable object before it is moved.

    Thanks in advance

    Miguel Delgado

    d

    draggable binding interferes with $parent

    Looks like $parent context variable is incorrect inside elements that use the draggable binding. Example fiddle:

    http://jsfiddle.net/pjbWC/5/

    This fiddle prints out $data and $parent values for array elements, they turn out to be equal, which I didn't expect.

    In source of the binding, update() method, there's this line:

    var templateOptions = prepareTemplateOptions(valueAccessor, "data");
    

    The issue goes away if I change "data" to something else:

    var templateOptions = prepareTemplateOptions(valueAccessor, "draggable");
    

    I don't understand this well at all, so not sure if this is correct fix, and what side-effects it may have.

    afterRender can't be easily globally customized

    The "default"/base global definition of ko.bindingHandlers.sortable.afterRender contains important functionality for this custom binding widget that shouldn't be blindly clobbered, but rather should be done like this

        var baseAfterRender = ko.bindingHandlers.sortable.afterRender;
        ko.bindingHandlers.sortable.afterRender = function(elements, data) {
            // execute "base" functionality
            baseAfterRender.call(data, elements, data);
            // start custom functionality...
            elements = $(elements).filter("li");
            elements.removeClass('ui-state-hover');
            elements.mouseover(function() {
                $(this).addClass('ui-state-hover');
            });
            elements.mouseout(function() {
                $(this).removeClass('ui-state-hover');
            });
        };

    The template binding customization method doesn't have this issue. It would be cool if this pitfall wasn't there - maybe a "private" _afterRender and the global and/or options afterRender callbacks are optional (and called after _afterRender).

    The documentation mentions afterRender only once

    afterAdd, beforeRemove, afterRender, includeDestroyed, templateEngine - this binding will pass these options on to the template binding.

    However, afterRender is not referenced in function prepareTemplateOptions (afterAdd is). Is something wrong there? (i.e., afterRemove missing from that array?

    Problem with 'dragged' function and receive event.

    Hi, I'm a developer using this lib.

    I had to use 'dragged' function to manipulate the object before dropping to the other sortable list. However:
    First, in receive event function, 'dragItem = dataGet(ui.item[0], DRAGKEY)' doesn't get anything, so later code is ignored. I had to use 'dragItem = dataGet(ui.item[0], ITEMKEY)' to get the object.

    Second, in update event function, 'item = dataGet(el, ITEMKEY) || dragItem;' ignores dragItem when dataGet(el, ITEMKEY) is available. I had to use 'item = dragItem || dataGet(el, ITEMKEY);' to use manipulated object.

    Could someone let me know why I am having this issue? Or is there something I missed? Thanks!

    jQuery Tmpl conflict

    I created a sortable with the following :

    and while using jQuery templates in other view i've got an error :

    Uncaught SyntaxError: Unexpected token & jQuery.tmpl.js:10

    is there any conflict with using Sortable with jQuery Templates ?

    Falsey values not handled properly

    Issue #50 fixed support for non-objects (an observableArray of strings, for example). However, there is logic that checks for the falsiness of certain values, which causes failures if the array item is 0, null, undefined, NaN, or "".

    Sortable gets initialised too early

    $(element).sortable is called before the template contents get a chance to bind. This is problematic when a sortable option is specified. For instance, if I only want to sort items that have a 'valid' class I specify options like this: options: { items: 'li.valid' }. If I apply this valid class to my list elements using a data-bind, this class is not applied when sortable is called.

    This can be trivially fixed by wrapping the call to sortable in a Clock.setTimeout(func, 0); but maybe there's a more elegant fix.

    'out' event won't fire when 2 similar sortables are on the same page

    See here

    http://jsfiddle.net/eyG3A/

    Everything but the 'out' event seems to fire normally, but it seems to have really buggy behavior when there are multiple sortables on similarly structured data at the same time.

    To reproduce, try dragging an item from one of the lists away from both lists (not into the other list). 'out' won't fire. Then remove one of the unordered lists and rerun. It will fire normally.

    Having a condition inside the sortable item template that can exclude the item creates error

    See http://jsfiddle.net/unklefolk/VQndj/ based on the simple example.

    The condition:

    ko if: $data.Name !== 'Fix car'

    .. causes a certain item to be excluded. However, this causes an error:

    Uncaught TypeError: Cannot read property '__ko__1332343640095' of null

    This is because in afterRender, element.parentNode is null.

        afterRender: function (elements, data) {
            ko.utils.arrayForEach(elements, function (element) {
                if (element.nodeType === 1) {
                    ko.utils.domData.set(element, itemKey, data);
                    ko.utils.domData.set(element, parentKey, ko.utils.domData.get(element.parentNode, listKey));
                }
            });
        },
    

    The following seems to fix the error but not sure if this is the correct fix:

        afterRender: function (elements, data) {
            ko.utils.arrayForEach(elements, function (element) {
                if (element.nodeType === 1 && element.parentNode) {
                    ko.utils.domData.set(element, itemKey, data);
                    ko.utils.domData.set(element, parentKey, ko.utils.domData.get(element.parentNode, listKey));
                }
            });
        },
    

    Prevent knockout-sortable from recreating DOM elements

    When using knockout-sortable, the element I am dragging has its contents destroyed and recreated in the DOM. This is different behavior than when making the elements sortable using jQuery UI sortable directly, which just moves the element and its contents.

    In my scenario, each of my sortable items is a widget that has its own ko viewmodel applied to it via a custom binding. It's expensive (and unnecessary in my case) to have the widget recreated each time the user moves it.

    Is there a way to have knockout-sortable leave the item's mark-up untouched after it is moved?

    ie8 sortable with template makes items disappear

    In ie8, with the connectedLists lists example. If I drag them around I can make them disappear. I see that in other examples they import jquery-tmpl.js and this fixes it for simple templates.

    ie error -
    Object expected knockout-2.2.0.js, line 38 character 482

    IE 7 drag to sort but drop w/ no sort (drop back in same position) - leaves z-index and display:none on all items inside <li>.

    For IE 7 I had an issue I spent quite a bit of time on. I have something working now, so I am not sure this is an issue but I could certainly see others coming across something similar. I was using ul w/ li's and the li's had some buttons and a span and input. When I was sorting everything worked perfect except when I started dragging an item, but dropped it with no actual sort change. When this occurred everything in the li had display: none (not the li though). My fix works but I cannot explain why.

    data-bind="sortable: {data: model, afterMove: saveSort, options: {opacity:.5}}"

    ^ I added the options{} bit above. opacity seems to solve my issue. Like I said, I cannot explain this. I saw a similar issue here marked as resolved. It showed when I did a search for " ie " < with spaces. Some of this javascript is over my head. I apologize that I cannot help further. If you need anything please let me know. I will keep an eye on this thread for a couple days.

    looking through all of the jquery ui sortable docs I found nothing that hooked into drop event when no change was detected in the sort order...

    Thanks,
    jxmiller

    How do you access a mapped sourceParent?

    Thought I had found all of the caveats, but maybe this is a limitation of mapping ;)

    In your seating chart example you have arg.targetParent.id. However, what if that ID of the target parent was mapped using the mapping plugin?

    I tried multiple methods to access it but the common error was cannot access __ko_mapping__ of undefined. http://jsfiddle.net/DeLongey/F782z/

    Do I write my own handlers for this?

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google โค๏ธ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.