Да, к сожалению, JsGrid это вам не KnockoutJs, так что обновлять придется самостоятельно. Впрочем, делается это очень легко.
В этом посте я расскажу про систему событий в JsGrid: как подписываться на события, какие интересные события JsGrid предоставляет, и приведу пример про обновление подсветки строки после редактирования ячеек этой строки.
Подписка на события
Для подписки на события в JsGrid существует метод AttachEvent объекта SP.JsGrid.JsGridControl:
/** Attach event handler to a particular event type */
AttachEvent(eventType: JsGrid.EventType, fnOnEvent: { (args: IEventArgs): void }): void;
/** Detach a previously set event handler */
DetachEvent(eventType: JsGrid.EventType, fnOnEvent): void;
AttachEvent(eventType: JsGrid.EventType, fnOnEvent: { (args: IEventArgs): void }): void;
/** Detach a previously set event handler */
DetachEvent(eventType: JsGrid.EventType, fnOnEvent): void;
Существует целых 30 различных типов событий (eventType) – при клике на ячейку, правом клике, двойном клике, начале редактирования ячейки, окончании редактирования, после вставки данных из буфера обмена, при выборе записи, при изменении текущего выделения и т.д.
Вот полный список:
export enum EventType {
OnCellFocusChanged,
OnRowFocusChanged,
OnCellEditBegin,
OnCellEditCompleted,
OnRightClick,
OnPropertyChanged,
OnRecordInserted,
OnRecordDeleted,
OnRecordChecked,
OnCellErrorStateChanged,
OnEntryRecordAdded,
OnEntryRecordCommitted,
OnEntryRecordPropertyChanged,
OnRowErrorStateChanged,
OnDoubleClick,
OnBeforeGridDispose,
OnSingleCellClick,
OnInitialChangesForChangeKeyComplete,
OnVacateChange,
OnGridErrorStateChanged,
OnSingleCellKeyDown,
OnRecordsReordered,
OnBeforePropertyChanged,
OnRowEscape,
OnBeginRenameColumn,
OnEndRenameColumn,
OnPasteBegin,
OnPasteEnd,
OnBeginRedoDataUpdateChange,
OnBeginUndoDataUpdateChange
}
OnCellFocusChanged,
OnRowFocusChanged,
OnCellEditBegin,
OnCellEditCompleted,
OnRightClick,
OnPropertyChanged,
OnRecordInserted,
OnRecordDeleted,
OnRecordChecked,
OnCellErrorStateChanged,
OnEntryRecordAdded,
OnEntryRecordCommitted,
OnEntryRecordPropertyChanged,
OnRowErrorStateChanged,
OnDoubleClick,
OnBeforeGridDispose,
OnSingleCellClick,
OnInitialChangesForChangeKeyComplete,
OnVacateChange,
OnGridErrorStateChanged,
OnSingleCellKeyDown,
OnRecordsReordered,
OnBeforePropertyChanged,
OnRowEscape,
OnBeginRenameColumn,
OnEndRenameColumn,
OnPasteBegin,
OnPasteEnd,
OnBeginRedoDataUpdateChange,
OnBeginUndoDataUpdateChange
}
Как видите, события могут быть крайне полезны при создании интерактивных интерфейсов на основе JsGrid. Открываются действительно интересные возможности: мало того, что у нас Excel в браузере, мы еще вдобавок можем сделать его интерактивным! :)
Теперь про обработчики событий. На вход в обработчик поступает некоторый объект IEventArgs. В зависимости от типа события, объект этот будет разный и содержать разные сведения. Есть хорошие новости: в процессе подготовки дефинишенов, мне удалось вычислить все возможные типы IEventArgs и описать их!
Вот они:
export interface IEventArgs { }
export module EventArgs {
export class OnEntryRecordAdded implements IEventArgs {
constructor(recordKey: number);
recordKey: number;
}
export class CellFocusChanged implements IEventArgs {
constructor(newRecordKey: number, newFieldKey: string, oldRecordKey: number, oldFieldKey: string);
newRecordKey: number;
newFieldKey: string;
oldRecordKey: number;
oldFieldKey: string;
}
export class RowFocusChanged implements IEventArgs {
constructor(newRecordKey: number, oldRecordKey: number);
newRecordKey: number;
oldRecordKey: number;
}
export class CellEditBegin implements IEventArgs {
constructor(recordKey: number, fieldKey: string);
recordKey: number;
fieldKey: string;
}
export class CellEditCompleted implements IEventArgs {
constructor(recordKey: number, fieldKey: string, changeKey: JsGrid.IChangeKey, bCancelled: boolean);
recordKey: number;
fieldKey: string;
changeKey: JsGrid.IChangeKey;
bCancelled: boolean;
}
export class Click implements IEventArgs {
constructor(eventInfo, context: JsGrid.ClickContext, recordKey: number, fieldKey: string);
eventInfo: any;
context: JsGrid.ClickContext;
recordKey: number;
fieldKey: string;
}
export class PropertyChanged implements IEventArgs {
constructor(recordKey: number, fieldKey: string, oldProp: SP.JsGrid.Internal.PropertyUpdate, newProp: SP.JsGrid.Internal.PropertyUpdate, propType: SP.JsGrid.IPropertyType, changeKey: SP.JsGrid.IChangeKey, validationState: SP.JsGrid.ValidationState);
recordKey: number;
fieldKey: string;
oldProp: SP.JsGrid.Internal.PropertyUpdate;
newProp: SP.JsGrid.Internal.PropertyUpdate;
propType: SP.JsGrid.IPropertyType;
changeKey: SP.JsGrid.IChangeKey;
validationState: SP.JsGrid.ValidationState;
}
export class RecordInserted implements IEventArgs {
constructor(recordKey, recordIdx, afterRecordKey, changeKey);
recordKey: number;
recordIdx: number;
afterRecordKey: number;
changeKey: JsGrid.IChangeKey;
}
export class RecordDeleted implements IEventArgs {
constructor(recordKey, recordIdx, changeKey);
recordKey: number;
recordIdx: number;
changeKey: JsGrid.IChangeKey;
}
export class RecordChecked implements IEventArgs {
constructor(recordKeySet: SP.Utilities.Set, bChecked: boolean);
recordKeySet: SP.Utilities.Set;
bChecked: boolean;
}
export class OnCellErrorStateChanged implements IEventArgs {
constructor(recordKey, fieldKey, bAddingError, bCellCurrentlyHasError, bCellHadError, errorId);
recordKey: number;
fieldKey: string;
bAddingError: boolean;
bCellCurrentlyHasError: boolean;
bCellHadError: boolean;
errorId: number;
}
export class OnRowErrorStateChanged implements IEventArgs {
constructor(recordKey, bAddingError, bErrorCurrentlyInRow, bRowHadError, errorId, message);
recordKey: number;
bAddingError: boolean;
bErrorCurrentlyInRow: boolean;
bRowHadError: boolean;
errorId: number;
message: string;
}
export class OnEntryRecordCommitted implements IEventArgs {
constructor(origRecKey: string, recordKey: number, changeKey: JsGrid.IChangeKey);
originalRecordKey: number;
recordKey: number;
changeKey: JsGrid.IChangeKey
}
export class SingleCellClick implements IEventArgs {
constructor(eventInfo, recordKey: number, fieldKey: string);
eventInfo: any;
recordKey: number;
fieldKey: string;
}
export class PendingChangeKeyInitiallyComplete implements IEventArgs {
constructor(changeKey: JsGrid.IChangeKey);
changeKey: JsGrid.IChangeKey
}
export class VacateChange implements IEventArgs {
constructor(changeKey: JsGrid.IChangeKey);
changeKey: JsGrid.IChangeKey
}
export class GridErrorStateChanged implements IEventArgs {
constructor(bAnyErrors: boolean);
bAnyErrors: boolean;
}
export class SingleCellKeyDown implements IEventArgs {
constructor(eventInfo, recordKey: number, fieldKey: string);
eventInfo: any;
recordKey: number;
fieldKey: string;
}
export class OnRecordsReordered implements IEventArgs {
constructor(recordKeys: string[], changeKey: JsGrid.IChangeKey);
reorderedKeys: string[];
changeKey: JsGrid.IChangeKey;
}
export class OnRowEscape implements IEventArgs {
constructor(recordKey: number);
recordKey: number;
}
export class OnEndRenameColumn implements IEventArgs {
constructor(columnKey: string, originalColumnTitle: string, newColumnTitle: string);
columnKey: string;
originalColumnTitle: string;
newColumnTitle: string;
}
export class OnBeginRedoDataUpdateChange implements IEventArgs {
constructor(changeKey: JsGrid.IChangeKey);
changeKey: JsGrid.IChangeKey
}
export class OnBeginUndoDataUpdateChange implements IEventArgs {
constructor(changeKey: JsGrid.IChangeKey);
changeKey: JsGrid.IChangeKey
}
}
export module EventArgs {
export class OnEntryRecordAdded implements IEventArgs {
constructor(recordKey: number);
recordKey: number;
}
export class CellFocusChanged implements IEventArgs {
constructor(newRecordKey: number, newFieldKey: string, oldRecordKey: number, oldFieldKey: string);
newRecordKey: number;
newFieldKey: string;
oldRecordKey: number;
oldFieldKey: string;
}
export class RowFocusChanged implements IEventArgs {
constructor(newRecordKey: number, oldRecordKey: number);
newRecordKey: number;
oldRecordKey: number;
}
export class CellEditBegin implements IEventArgs {
constructor(recordKey: number, fieldKey: string);
recordKey: number;
fieldKey: string;
}
export class CellEditCompleted implements IEventArgs {
constructor(recordKey: number, fieldKey: string, changeKey: JsGrid.IChangeKey, bCancelled: boolean);
recordKey: number;
fieldKey: string;
changeKey: JsGrid.IChangeKey;
bCancelled: boolean;
}
export class Click implements IEventArgs {
constructor(eventInfo, context: JsGrid.ClickContext, recordKey: number, fieldKey: string);
eventInfo: any;
context: JsGrid.ClickContext;
recordKey: number;
fieldKey: string;
}
export class PropertyChanged implements IEventArgs {
constructor(recordKey: number, fieldKey: string, oldProp: SP.JsGrid.Internal.PropertyUpdate, newProp: SP.JsGrid.Internal.PropertyUpdate, propType: SP.JsGrid.IPropertyType, changeKey: SP.JsGrid.IChangeKey, validationState: SP.JsGrid.ValidationState);
recordKey: number;
fieldKey: string;
oldProp: SP.JsGrid.Internal.PropertyUpdate;
newProp: SP.JsGrid.Internal.PropertyUpdate;
propType: SP.JsGrid.IPropertyType;
changeKey: SP.JsGrid.IChangeKey;
validationState: SP.JsGrid.ValidationState;
}
export class RecordInserted implements IEventArgs {
constructor(recordKey, recordIdx, afterRecordKey, changeKey);
recordKey: number;
recordIdx: number;
afterRecordKey: number;
changeKey: JsGrid.IChangeKey;
}
export class RecordDeleted implements IEventArgs {
constructor(recordKey, recordIdx, changeKey);
recordKey: number;
recordIdx: number;
changeKey: JsGrid.IChangeKey;
}
export class RecordChecked implements IEventArgs {
constructor(recordKeySet: SP.Utilities.Set, bChecked: boolean);
recordKeySet: SP.Utilities.Set;
bChecked: boolean;
}
export class OnCellErrorStateChanged implements IEventArgs {
constructor(recordKey, fieldKey, bAddingError, bCellCurrentlyHasError, bCellHadError, errorId);
recordKey: number;
fieldKey: string;
bAddingError: boolean;
bCellCurrentlyHasError: boolean;
bCellHadError: boolean;
errorId: number;
}
export class OnRowErrorStateChanged implements IEventArgs {
constructor(recordKey, bAddingError, bErrorCurrentlyInRow, bRowHadError, errorId, message);
recordKey: number;
bAddingError: boolean;
bErrorCurrentlyInRow: boolean;
bRowHadError: boolean;
errorId: number;
message: string;
}
export class OnEntryRecordCommitted implements IEventArgs {
constructor(origRecKey: string, recordKey: number, changeKey: JsGrid.IChangeKey);
originalRecordKey: number;
recordKey: number;
changeKey: JsGrid.IChangeKey
}
export class SingleCellClick implements IEventArgs {
constructor(eventInfo, recordKey: number, fieldKey: string);
eventInfo: any;
recordKey: number;
fieldKey: string;
}
export class PendingChangeKeyInitiallyComplete implements IEventArgs {
constructor(changeKey: JsGrid.IChangeKey);
changeKey: JsGrid.IChangeKey
}
export class VacateChange implements IEventArgs {
constructor(changeKey: JsGrid.IChangeKey);
changeKey: JsGrid.IChangeKey
}
export class GridErrorStateChanged implements IEventArgs {
constructor(bAnyErrors: boolean);
bAnyErrors: boolean;
}
export class SingleCellKeyDown implements IEventArgs {
constructor(eventInfo, recordKey: number, fieldKey: string);
eventInfo: any;
recordKey: number;
fieldKey: string;
}
export class OnRecordsReordered implements IEventArgs {
constructor(recordKeys: string[], changeKey: JsGrid.IChangeKey);
reorderedKeys: string[];
changeKey: JsGrid.IChangeKey;
}
export class OnRowEscape implements IEventArgs {
constructor(recordKey: number);
recordKey: number;
}
export class OnEndRenameColumn implements IEventArgs {
constructor(columnKey: string, originalColumnTitle: string, newColumnTitle: string);
columnKey: string;
originalColumnTitle: string;
newColumnTitle: string;
}
export class OnBeginRedoDataUpdateChange implements IEventArgs {
constructor(changeKey: JsGrid.IChangeKey);
changeKey: JsGrid.IChangeKey
}
export class OnBeginUndoDataUpdateChange implements IEventArgs {
constructor(changeKey: JsGrid.IChangeKey);
changeKey: JsGrid.IChangeKey
}
}
Дефинишены входят в состав проекта TypeScript Definitions for SharePoint 2013, и вы можете посмотреть текущее состояние дефинишенов JsGrid по вот этой прямой ссылке. Так что если вам нужно понять что такое например IChangeKey и другие сложные типы, изучайте на здоровье.
Disclaimer: На момент написания этого поста дефинишены еще далеки до завершения и могут быть не до конца юзабельны, но я постоянно над ними работаю, надеюсь закончить в течение нескольких недель.
SP.JsGrid.EventType.OnPropertyChanged
Событие OnPropertyChanged срабатывает каждый раз, когда значение какой-либо из ячеек изменилось. Очевидно, можно использовать это событие для обновления строки.
Для того, чтобы JsGrid собственно обновил строку, можно использовать метод RefreshRow объекта JsGridControl:
/** Re-render the specified row in the view. */
RefreshRow(recordKey: number): void;
RefreshRow(recordKey: number): void;
Теперь разберемся с EventArgs. Определение JsGrid.EventArgs.PropertyChanged выглядит следующим образом:
export class PropertyChanged implements IEventArgs {
constructor(recordKey: number, fieldKey: string, oldProp: SP.JsGrid.Internal.PropertyUpdate, newProp: SP.JsGrid.Internal.PropertyUpdate, propType: SP.JsGrid.IPropertyType, changeKey: SP.JsGrid.IChangeKey, validationState: SP.JsGrid.ValidationState);
recordKey: number;
fieldKey: string;
oldProp: SP.JsGrid.Internal.PropertyUpdate;
newProp: SP.JsGrid.Internal.PropertyUpdate;
propType: SP.JsGrid.IPropertyType;
changeKey: SP.JsGrid.IChangeKey;
validationState: SP.JsGrid.ValidationState;
}
constructor(recordKey: number, fieldKey: string, oldProp: SP.JsGrid.Internal.PropertyUpdate, newProp: SP.JsGrid.Internal.PropertyUpdate, propType: SP.JsGrid.IPropertyType, changeKey: SP.JsGrid.IChangeKey, validationState: SP.JsGrid.ValidationState);
recordKey: number;
fieldKey: string;
oldProp: SP.JsGrid.Internal.PropertyUpdate;
newProp: SP.JsGrid.Internal.PropertyUpdate;
propType: SP.JsGrid.IPropertyType;
changeKey: SP.JsGrid.IChangeKey;
validationState: SP.JsGrid.ValidationState;
}
Здесь самое главное, что у нас есть recordKey и fieldKey, которые позволяют однозначно определить, какая строка и какая ячейка были изменены.
Таким образом, код для регистрации обработчика в простейшем случае будет выглядеть примерно так:
control.AttachEvent(SP.JsGrid.EventType.OnPropertyChanged, function (args) {
control.RefreshRow(args.recordKey)
});
control.RefreshRow(args.recordKey)
});
Обратите внимание, что среди значений объекта EventArgs есть довольно интересные вещи, такие как ValidationState, который может быть Invalid, и в зависимости от которого вы также можете свою какую-то кастомную логику реализовывать.
Очень важно также, что доступно не только новое значение свойства, но и предыдущее. Определение SP.JsGrid.Internal.PropertyUpdate выглядит вот как:
export class PropertyUpdate {
constructor(data: any, localized: string);
data: any;
localized: string;
}
constructor(data: any, localized: string);
data: any;
localized: string;
}
А важно знать предыдущее значение, например потому что часто требуется выполнять какие-то действия только если значение изменилось например с А на В, но не с Б на В – распространенный пример, это состояние задач в багтрекере: если статус меняется с “Открыто” на “Проверено” минуя фазу “Выполнено” (которую обычно тестируют), тут точно надо бить тревогу! :)
Кстати, AttachEvent (в отличие от SetDelegate) не требует, чтобы его выполняли именно в момент инициализации грида, как минимум в случае события OnPropertyChanged и других событий, на которых я тестировал.
Заключение
События – это неотъемлемая часть любого API. В JsGrid событий много, подписываться на них легко, и даже описания EventArgs у нас теперь есть благодаря скромному мне :)
Так что тут всё отлично, мин я не нашел, используем! :)
Здравствуйте, Андрей!
ОтветитьУдалитьА существует ли событие, которое происходит после полной загрузки JSGrid или инициализации его компонентов?
Дело в том, что необходимо выполнить скрипт сразу после загрузки страницы, содержащей JSGrid. Страница с JSGrid представляет собой стандартную страницу PWA.
Если использовать _spBodyOnLoadFunctionNames, то при выполнении моего скрипта возникают ошибки, связанные с тем, что необходимые мне компоненты JSGrid еще не инициализированы.
Возможно ли как-то обойти данную проблему?