2014-12-14 2 views
0

Событие event.index в событии VALUES_ADDED, похоже, берется прямо из аргументов, переданных вызову CollaborativeList.move, что может быть неверным. Например,Являются ли события причиной CollaborativeList.move неправильной?

list.move(0,0) // and 
list.move(0,1) 

являются Без операционное, а первый элемент в списке остается нулевым индексом. Однако событие event.index в результате события VALUES_ADDED равно 0 и 1 соответственно.

Кроме того,

list.move(0,2) 

приводит первый элемент в списке, в конечном итоге по индексу 1. Однако event.index в результате события 2.

Наконец, за исключением случаев, nööp , лучше не запускать какое-либо событие, потому что нет никаких изменений.

ответ

1

Так что это работает немного запутанно, и документы не объясняют, что/почему значения индекса являются тем, чем они являются.

Значения индекса действительно полезны только для обновления локальных списков (локальные списки представляют собой массив объектов, которые соответствуют/ссылаются на объекты Realtime в ваших CollaborativeLists), поэтому индексы настраиваются таким образом, что вы можете использовать их напрямую для изменения вашего локального списка шаг за шагом. В реальном времени api не пытается отправить минимальное количество событий, и если вы перемещаете один и тот же элемент несколько раз, индексы, отображаемые в событиях, не обязательно отражают окончательное местоположение элемента в списке.

Также полезно быстрый бит от «Обработка событий» Руководство Realtime API для разработчиков:

Все события на изменения, которые происходят в операции соединения являются испускается после того, все изменения будут применены к модели ,

С этим в виду, быстрый пример, чтобы проиллюстрировать то, что на самом деле происходит и почему это происходит так:

beginCompoundOperation() 
move(0, 3) 
move(2, 0) 
endCompoundOperation() 

И с результатами переезда:

beginCompoundOperation() 
[A B C D] 
move(0, 3) 
[B C A D] 
move(2, 0) 
[A B C D] 
endCompoundOperation() 

В свою очередь, , следующие события:

VALUE_ADDED : Index: 3, MovedFromIndex: 0 
VALUE_REMOVED : Index: 0, MovedToIndex: 3 
VALUE_ADDED : Index: 0, MovedFromIndex: 2 
VALUE_REMOVED : Index, 3, MovedToIndex: 0 

(Note that the MovedFromIndx/MovedToIndex refer to the state of the list BEFORE the action in the event) 

Это позволит вашему локальному список, чтобы сделать следующие изменения:

[A B C D] 
VALUE_ADDED(3, 0) 
[A B C A D] 
VALUE_REMOVED(0, 3) 
[B C A D] 
VALUE_ADDED(0, 2) 
[A B C A D] 
VALUE_REMOVED(3, 0) 
[A B C D] 

Таким образом, в вашем примере, во время добавленного события, индекс назначения принимает во внимание, что вы двигаетесь пункт дальше в список, не удаляя его которое произойдет в соответствующем событии VALUES_REMOVED.

Надеюсь, что это полезна!

И, конечно, какой-то код, чтобы проверить вещи, если вы уже не имеете что-то вроде этого:

move_test.HTML

<!DOCTYPE html> 
<html> 
<head> 
    <title>Move Test</title> 

    <script type="text/javascript" src="//apis.google.com/js/api.js"></script> 
    <script src="/js/misc/move_test.js" type="application/javascript" ></script> 
</head> 
<body onload="onPageLoad()"> 
    <button onclick="runScenario()">Run Scenario</button> 
</body> 

move_test.js

var clientId = "<YOUR CLIENT ID HERE>"; 
var REALTIME_MIMETYPE = 'application/vnd.google-apps.drive-sdk'; 

// Everything interesting is at the bottom in the onDocLoaded function. 

function createRealtimeFile(title, description, callback) 
{ 
    console.log('Creating Drive Document'); 
    gapi.client.drive.files.insert({ 
     'resource': 
     { 
      'mimeType': REALTIME_MIMETYPE, 
      'title': title, 
      'description': description 
     } 
    }).execute(function (docInfo) 
    { 
     callback(docInfo, /*newDoc*/true); 
    }); 
} 

function openRealtimeFile(title, callback) 
{ 
    gapi.client.load('drive', 'v2', function() 
    { 
     gapi.client.drive.files.list(
     { 
      'q': 'title='+"'"+title+"' and 'me' in owners and trashed=false" 
     }).execute(function (results) 
     { 
      if (!results.items || results.items.length === 0) 
      { 
       createRealtimeFile(title, /*DocDescription*/"", callback); 
      } 
      else 
      { 
       callback(results.items[0], /*newDoc*/false); 
      } 
     }); 
    }); 
} 

function onPageLoad() 
{ 
    var GScope = 
    { 
     Drive: 'https://www.googleapis.com/auth/drive.file' 
    }; 

    gapi.load('auth:client,drive-realtime,drive-share', function() 
    { 
     var handleAuthResult = function(authResult) 
     { 
      console.log('Requesting Drive Document'); 

      openRealtimeFile("TESTDOC__", function (docInfo, newDoc) 
      { 
       if (docInfo && docInfo.id) 
       { 
        gapi.drive.realtime.load(docInfo.id, onDocLoaded, onDocInitialized, onDocLoadError); 
       } 
       else 
       { 
        console.log('Unable to find realtime doc'); 
        debugger; 
       } 
      }); 
     }; 

     gapi.auth.authorize(
     { 
      client_id: clientId, 
      scope: [ GScope.Drive ], 
      immediate: false 
     }, handleAuthResult); 
    }); 
} 

function onDocLoadError(e) 
{ 
    console.log('Doc Load Error: ', e); 
    findAndLoadDoc(); 
} 

function onDocInitialized(model) 
{ 
    console.log('Drive Document Initialized'); 

    var root = model.createMap(); 

    model.getRoot().set('docRoot', root); 
} 

function itemListen(item, listenerFn) 
{ 
    if (listenerFn) 
    { 
     item.addEventListener(gapi.drive.realtime.EventType.OBJECT_CHANGED, listenerFn); 
    } 
} 

function createItem(model, name, listenerFn) 
{ 
    var map = model.createMap(); 
    var items = model.createList(); 

    map.set('items', items); 
    map.set('name', name); 

    if (listenerFn) 
    { 
     map.addEventListener(gapi.drive.realtime.EventType.OBJECT_CHANGED, listenerFn); 
    } 

    return map; 
} 

function addItem(parent, child) 
{ 
    parent.get('items').push(child); 
} 

function removeItem(parent, index) 
{ 
    parent.get('items').remove(index); 
} 

function moveItem(oldParent, oldIndex, newParent, newIndex) 
{ 
    if (oldParent !== newParent) 
    { 
     oldParent.get('items').moveToList(oldIndex, newParent.get('items'), newIndex); 
    } 
    else 
    { 
     oldParent.get('items').move(oldIndex, newIndex); 
    } 
} 

var scenarioRunning = false 

function rootListen(event) 
{ 
    if (!scenarioRunning) 
    { 
     return; 
    } 

    for (var i = 0; i < event.events.length; ++i) 
    { 
     var ev = event.events[i]; 

     switch (event.events[i].type) 
     { 
      case gapi.drive.realtime.EventType.VALUES_ADDED: 
       console.log('ADDED: ', ev); 
       console.log(' Index: ', ev.index, ev.movedFromIndex); 
       console.log(' List: ', ev.target.get(ev.index).get('name')); 
       console.log(' Value: ', ev.values[0].get('name')); 
       printList(root); 
       break; 
      case gapi.drive.realtime.EventType.VALUES_REMOVED: 
       console.log('REMOVED: ', ev); 
       console.log(' Index: ', ev.index, ev.movedToIndex); 
       console.log(' List: ', ev.target.get(ev.index).get('name')); 
       console.log(' Value: ', ev.values[0].get('name')); 
       printList(root); 
       break; 
      default: 
       debugger; 
       break; 
     } 
    } 
} 

function printList(item) 
{ 
    var list = item.get('items'); 

    var str = '[ '; 

    for (var i = 0; i < list.length; ++i) 
    { 
     var entry = list.get(i); 

     str += entry.get('name') + ' '; 
    } 

    str += ']'; 

    console.log(str); 
} 

var docModel; 
var docRoot; 
var root; 
function onDocLoaded(doc) 
{ 
    docModel = doc.getModel(); 
    docRoot = docModel.getRoot(); 

    window.__docRoot = docRoot; 

    if (docRoot.has('root')) 
    { 
     docRoot.delete('root'); 
    } 

    docRoot.set('root', createItem(docModel, 'root', rootListen)); 

    console.log('Document Loaded - Scenario Ready'); 

    root = docRoot.get('root'); 



    docModel.beginCompoundOperation(); 

    //  root 
    //  | 
    // [A, B, C, D] 

    var childA = createItem(docModel, 'childA'); 
    addItem(root, childA); 

    var childB = createItem(docModel, 'childB'); 
    addItem(root, childB); 

    var childC = createItem(docModel, 'childC'); 
    addItem(root, childC); 

    var childD = createItem(docModel, 'childD'); 
    addItem(root, childD); 

    docModel.endCompoundOperation(); 


    window.runScenario = function() 
    { 
     scenarioRunning = true; 

     printList(root); 

     console.log('---- Scenario Start ----'); 


     docModel.beginCompoundOperation(); 

     moveItem(root, 0, root, 3); 
     moveItem(root, 2, root, 0); 

     docModel.endCompoundOperation(); 


     console.log('---- Scenario Complete ----'); 

     printList(root); 
    } 
} 
+0

Спасибо, я не заметил, что событие было до удалить, которая решает эту проблему. Есть ли причина, по которой вы не подавляете никаких событий, которые в любом случае являются noops? Кроме того, кажется более естественным каким-либо образом иметь событие удаления перед событием добавления. Является ли событие добавления первым, чтобы значения индекса не изменялись, или существует другая причина? –

+1

Я просто догадываюсь, но кажется вероятным, что причина не удалять nop-события - это последовательность и простота. Не нужно быть логикой, чтобы решить, являются ли события нулевыми или если они имеют эффект. В конце концов, возможно, что конечный результат будет равно нулю (перемещение (0, 2), перемещение (2, 0) и т. Д.), Поэтому ответственность подталкивается к потребителю. Что касается добавления vs remove в качестве первого события - добавление на первом месте позволяет вам легче различать событие «move» и «remove». Если событие удаления было первым, вы не знали бы, удалился ли он из списка или переместился в новое место ». –

Смежные вопросы