Kendo UI bug with jQuery versions - grid

I have a Kendo UI grid and a Kendo UI Window on the same page. The window contains form elements for record insertion, a record being represented by a row in the grid. But for reasons not known by me, when I opened the window and closed it again and then reopened, Kendo UI scaled it to be 100x smaller. I didn't want to hack the window, so I've looked for alternative solution.
I've used jQuery 1.7.2. I've updated jQuery to version 1.8.0. and window opening, closing and reopening worked. I was very happy until I realized that now the grid filters are not working. When I click on a grid filter, nothing happens, no popup, nothing. What is the cause of this and what would be the solution?
EDIT:
This is my code (I've replaced the values of the Urls). Grid filters are working with jQuery 1.7.2. and window reopen works with new versions of jQuery. Also, if I remove the sort hack, the grid filter popup still doesn't show up.
var hshflt = {};
var addWindow;
var editWindow;
var init = false;
//Sort Hack
/*
Changes all dataSources to case insensitive sorting (client side sorting).
This snipped enable case insensitive sorting on Kendo UI grid, too.
The original case sensitive comparer is a private and can't be accessed without modifying the original source code.
tested with Kendo UI version 2012.2.710 (Q2 2012 / July 2012).
*/
var CaseInsensitiveComparer = {
getterCache: {},
getter: function (expression) {
return this.getterCache[expression] = this.getterCache[expression] || new Function("d", "return " + kendo.expr(expression));
},
selector: function (field) {
return jQuery.isFunction(field) ? field : this.getter(field);
},
asc: function (field) {
var selector = this.selector(field);
return function (a, b) {
if ((selector(a).toLowerCase) && (selector(b).toLowerCase)) {
a = selector(a).toLowerCase(); // the magical part
b = selector(b).toLowerCase();
}
return a > b ? 1 : (a < b ? -1 : 0);
};
},
desc: function (field) {
var selector = this.selector(field);
return function (a, b) {
if ((selector(a).toLowerCase) && (selector(b).toLowerCase)) {
a = selector(a).toLowerCase(); // the magical part
b = selector(b).toLowerCase();
}
return a < b ? 1 : (a > b ? -1 : 0);
};
},
create: function (descriptor) {
return this[descriptor.dir.toLowerCase()](descriptor.field);
},
combine: function (comparers) {
return function (a, b) {
var result = comparers[0](a, b),
idx,
length;
for (idx = 1, length = comparers.length; idx < length; idx++) {
result = result || comparers[idx](a, b);
}
return result;
};
}
};
kendo.data.Query.prototype.normalizeSort = function (field, dir) {
if (field) {
var descriptor = typeof field === "string" ? { field: field, dir: dir} : field,
descriptors = jQuery.isArray(descriptor) ? descriptor : (descriptor !== undefined ? [descriptor] : []);
return jQuery.grep(descriptors, function (d) { return !!d.dir; });
}
};
kendo.data.Query.prototype.sort = function (field, dir, comparer) {
var idx,
length,
descriptors = this.normalizeSort(field, dir),
comparers = [];
comparer = comparer || CaseInsensitiveComparer;
if (descriptors.length) {
for (idx = 0, length = descriptors.length; idx < length; idx++) {
comparers.push(comparer.create(descriptors[idx]));
}
return this.orderBy({ compare: comparer.combine(comparers) });
}
return this;
};
kendo.data.Query.prototype.orderBy = function (selector) {
var result = this.data.slice(0),
comparer = jQuery.isFunction(selector) || !selector ? CaseInsensitiveComparer.asc(selector) : selector.compare;
return new kendo.data.Query(result.sort(comparer));
};
kendo.data.Query.prototype.orderByDescending = function (selector) {
return new kendo.data.Query(this.data.slice(0).sort(CaseInsensitiveComparer.desc(selector)));
};
//Sort Hack
$("#refresh-btn").click(function () {
refreshGrid();
});
var grid;
function getPageIndex() {
if (!(grid)) {
return 0;
}
return grid.pager.page() - 1;
}
function getPageSize() {
if (!(grid)) {
return 10;
}
return grid.pager.pageSize();
}
function getFilters() {
if (!(grid)) {
return "";
}
return grid.dataSource.filter();
}
function getSorts() {
if (!(grid)) {
return "";
}
var arr = grid.dataSource.sort();
if ((arr) && (arr.length == 0)) {
return "";
}
var returnValue = "";
for (var index in arr) {
var type = "";
for (var col in grid.columns) {
if (grid.columns[col].field === arr[index].field) {
type = grid.columns[col].type;
}
}
returnValue += ((returnValue.length > 0) ? (";") : ("")) + arr[index].field + "," + (arr[index].dir === "asc") + "," + type;
}
return returnValue;
}
function getColumns() {
if (!(grid)) {
return "";
}
var columns = "";
for (var col in grid.columns) {
if (columns.length > 0) {
columns += ";";
}
columns += grid.columns[col].field + "," + grid.columns[col].type;
}
return columns;
}
var initGrid = true;
var grid2Data;
function getDataSource() {
$.ajax({
type: 'POST',
url: 'mydsurl' + getParams(),
data: "filter=" + JSON.stringify(getFilters()) + "&columns=" + getColumns(),
success: function (param) { grid2Data = param; },
//dataType: dataType,
async: false
});
return grid2Data.Data;
}
var shouldClickOnRefresh = false;
function refreshGrid() {
shouldClickOnRefresh = false;
$.ajax({
type: 'POST',
url: 'mydsurl' + getParams(),
data: "filter=" + JSON.stringify(getFilters()) + "&columns=" + getColumns(),
success: function (param) { grid2Data = param; },
//dataType: dataType,
async: false
});
grid.dataSource.total = function () {
return grid2Data.Total;
}
for (var col in grid.columns) {
if ((grid.columns[col].type) && (grid.columns[col].type === "Date")) {
for (var row in grid2Data.Data) {
grid2Data.Data[row][grid.columns[col].field] = new Date(parseInt((grid2Data.Data[row][grid.columns[col].field] + "").replace("/Date(", "").replace(")/", "")));
}
}
}
grid.dataSource.data(grid2Data.Data);
shouldClickOnRefresh = true;
}
function getParams() {
return getPageSize() + "|" + getPageIndex() + "|" + getSorts();
}
function bindGrid() {
var editUrl = 'myediturl';
if (!(editWindow)) {
editWindow = $("#edit-window");
}
$(".k-button.k-button-icontext.k-grid-edit").each(function (index) {
$(this).click(function () {
if (!editWindow.data("kendoWindow")) {
editWindow.kendoWindow({
title: "Edit User",
width: "60%",
height: "60%",
close: onClose,
open: onEditOpen,
content: editUrl + $("#grid").data().kendoGrid.dataSource.view()[index]["ID"]
});
}
else {
editWindow.data("kendoWindow").refresh(editUrl + $("#grid").data().kendoGrid.dataSource.view()[index]["ID"]);
editWindow.data("kendoWindow").open();
}
editWindow.data("kendoWindow").center();
return false;
})
});
$(".k-button.k-button-icontext.k-grid-delete").each(function (index) {
$(this).click(function () {
var r = confirm("Are you sure you want to delete this user?");
if (r == true) {
$.ajax({
type: 'POST',
url: 'mydelurl' + $("#grid").data().kendoGrid.dataSource.view()[index]["ID"],
success: function (param) { refreshGrid(); },
async: false
});
}
return false;
});
});
}
function onDataBound() {
if (!(shouldClickOnRefresh)) {
shouldClickOnRefresh = true;
bindGrid();
}
else {
refreshGrid();
}
}
$(function () {
$("#grid").kendoGrid({
dataBound: onDataBound,
dataSource: {
autoSync: true,
data: getDataSource(),
serverPaging: true,
schema: {
model: {
fields: {
Email: { type: "string" },
FullName: { type: "string" },
LogCreateDate: { type: "date" },
RoleName: { type: "string" },
UserName: { type: "string" }
}
},
total: function (response) {
return grid2Data.Total;
}
},
pageSize: 10
},
toolbar: ["create"],
scrollable: true,
sortable: true,
filterable: true,
pageable: {
input: true,
numeric: false,
pageSizes: true
},
columns: [
{
command: ["edit", "destroy"],
title: " "
},
{
field: "Email",
title: "Email",
type: "String"
},
{
field: "FullName",
title: "Full Name",
type: "String"
},
{
field: "LogCreateDate",
title: "Created",
type: "Date",
template: '#= kendo.toString(LogCreateDate,"MM/dd/yyyy") #'
},
{
field: "RoleName",
title: "Role",
type: "Custom"
},
{
field: "UserName",
type: "String"
}
],
editable: "popup"
});
grid = $("#grid").data("kendoGrid");
function onAddOpen() {
}
addWindow = $("#add-window");
$(".k-button.k-button-icontext.k-grid-add").click(function () {
if (!addWindow.data("kendoWindow")) {
addWindow.kendoWindow({
title: "Add User",
width: "60%",
height: "60%",
close: onClose,
open: onAddOpen,
content: 'myaddurl'
});
}
else {
addWindow.data("kendoWindow").open();
}
addWindow.data("kendoWindow").center();
addWindow.data("kendoWindow").refresh();
return false;
});
});
function onClose() {
$("#refresh-btn").click();
}
function onEditOpen() {
//editWindow.data("kendoWdinow").center();
}

I've hacked Kendo UI for the second time, this time I've solved its incompatibility with jQuery 1.8.3. using the following hack:
$(".k-grid-filter").each(function(index) {
$(this).click(function() {
$($(".k-filter-menu.k-popup.k-group.k-reset")[index]).offset({
left: $($(".k-grid-filter")[index]).offset().left - $($(".k-filter-menu.k-popup.k-group.k-reset")[index]).width(),
top: $($(".k-grid-filter")[index]).offset().top + $($(".k-grid-filter")[index]).height()})
})
});
I've put this hack into the document load event of the page an voila, it works. It surely looks ugly as hell with this hack, but after designing it will look good as new. I'm happy I found a work around, but I'm unhappy I had to hack Kendo UI twice. It is a very nice tool except the bugs.

jQuery 1.8.# is only compatible with Kendo UI - Q2 2012 SP1 (2012.2 913) or later..
If your kendo UI version is earlier, you should update it.

Related

FullCalendar initializing before events load

I have a FullCalendar that takes in an array(selectedEvents) from a function that uses the Location ID that is pulled from the url. The array is built onInit as is the calendar but the calendar has a timeout on it in order to allow the events to populate. When the calendar is initially navigated to from another part of the website, the events for the Default Location are not displayed but they are populated into the selectedEvents array. When I click on another location's calendar, the events are populated and then displayed correctly. The calendar works correctly thereforth. I believe the calendar is taking precedence over the population of events during the first initialization of the calendar even though it is wrapped in a timeout. I've seen this question once before on SO but it was not answered.
I've already tried extending the timeout of the calendar
getLocationEvents(location: PFLocation) {
// if (this.allEvents) {
this.selectedEvents = [];
if (location.events) {
console.log(location.events);
this.eventIdArray = location.events;
for (let event of this.eventIdArray) {
this.eventService.get(event).subscribe(data => {
console.log(data);
this.selectedEvents.push(data);
});
}
} else {
return;
}
console.log(this.selectedEvents);
return this.selectedEvents;
}
getBatchEvents(location: PFLocation) {
var that = this;
if (this.allEvents) {
if (location.batches) {
let batches = location.batches;
for (let batch of batches) {
this.batchService.get(batch).subscribe(data => {
for (let event of data.events) {
this.eventService
.get(event)
.subscribe(eventData => {
console.log(eventData);
this.selectedEvents.push(eventData);
});
}
});
}
console.log(this.selectedEvents);
console.log('out of Batch Events');
return this.selectedEvents;
}
}
if (!this.allEvents) {
this.selectedEvents = [];
if (location.batches) {
let batches = location.batches;
for (let batch of batches) {
this.batchService.get(batch).subscribe(data => {
for (let event of data.events) {
this.eventService
.get(event)
.subscribe(eventData => {
console.log(eventData);
this.selectedEvents.push(eventData);
});
}
});
}
console.log(this.batchEvents);
console.log('out of Batch Events');
return this.selectedEvents;
}
}
}
getAllEvents(location: PFLocation) {
this.allEvents = true;
var that = this;
that.getLocationEvents(location);
that.getBatchEvents(location);
}
ngOnInit() {
let that = this;
this.sub = this.route.params.subscribe(params => {
this.locationId = params['locationid'];
this.userService
.getLocation(this.locationId)
.subscribe(location => {
console.log('Out of on Init');
this.selectedLocation = JSON.parse(
JSON.stringify(location)
);
that.getAllEvents(this.selectedLocation);
console.log(this.selectedLocation);
});`enter code here`
console.log(this.selectedLocation);
if (this.locationId) {
const today = new Date();
const y = today.getFullYear();
const m = today.getMonth();
const d = today.getDate();
$('#fullCalendar').fullCalendar('removeEvents');
$('#fullCalendar').fullCalendar(
'addEventSource',
that.selectedEvents
);
setTimeout(function() {
$('#fullCalendar').fullCalendar({
viewRender: function(view: any, element: any){
// We make sure that we activate the
perfect scrollbar when the view isn't on Month
if (view.name != 'month') {
var elem = $(element).find('.fc-
scroller')[0];
let ps = new PerfectScrollbar(elem);
}
},
header: {
left: 'title',
center: 'month, agendaWeek, agendaDay',
right: 'prev, next, today'
},
defaultDate: today,
selectable: true,
selectHelper: true,
views: {
month: {
// name of view
titleFormat: 'MMMM YYYY'
// other view-specific options here
},
week: {
titleFormat: ' MMMM D YYYY'
},
day: {
titleFormat: 'D MMM, YYYY'
}
},
eventLimit: true, // allow "more" link when
too many events
select: function(start: any, end: any) {
that.openEventForm();
},
eventClick: function(event, jsEvent) {
that.completeEventForm(event);
}
});
}, 500);
}
});
}
I expect the calendar to be populated the first time I navigate to the page.
Aside from refactoring the backend and eliminating the spaghetti code it wasn't a difficult issue once someone pointed out how the calendar inits. First the calendar has to grab onto the DOM element then it grabs the event source. If it doesn't grab the #calendar then it won't grab the event source until the next time it's initialized and at that point it is using the old event source data(if set up as my code was). "viewRender" needs to happen first, then "removeEvents" then "addEventSource".
getLocationEvents(location: PFLocation) {
if (location.events) {
return this.eventService
.getFaciltyTasks(location._id)
.subscribe(data => {
console.log(data);
this.facilityEvents = data;
});
}
}
getAllBatchEvents(location: PFLocation) {
return this.eventService
.getAllCultivationTasks(location._id)
.subscribe(data => {
console.log(data);
this.cultivationEvents = data;
});
}
getBatchEvents(batchId: string) {
return this.eventService.getBatchTasks(batchId).subscribe(data => {
console.log(data);
this.batchEvents = data;
});
}
editEventForm() {
this.state = 'editEvent';
}
getAllEvents(location: PFLocation) {
var that = this;
return new Promise(resolve => {
return this.eventService
.getAllLocationTasks(location._id)
.subscribe(data => {
console.log(data);
that.selectedEvents = data;
that.calInit();
resolve(that.selectedEvents);
});
});
}
calInit() {
var that = this;
const $calendar = $('#fullCalendar');
console.log(that.locationId);
if (this.selectedEvents) {
const today = new Date();
const y = today.getFullYear();
const m = today.getMonth();
const d = today.getDate();
$calendar.fullCalendar({
viewRender: function(view: any, element: any) {
// We make sure that we activate the perfect scrollbar when the view isn't on Month
if (view.name != 'month') {
var elem = $(element).find('.fc-scroller')[0];
let ps = new PerfectScrollbar(elem);
}
},
customButtons: {
filter: {
text: 'filter',
click: function() {
that.open();
}
}
},
// events: that.selectedEvents,
header: {
left: 'title',
center: 'filter, month, agendaWeek, agendaDay',
right: 'prev, next, today'
},
defaultDate: today,
selectable: true,
selectHelper: true,
views: {
month: {
// name of view
titleFormat: 'MMMM YYYY'
// other view-specific options here
},
week: {
titleFormat: ' MMMM D YYYY'
},
day: {
titleFormat: 'D MMM, YYYY'
}
},
eventLimit: true, // allow "more" link when too many events
select: function(start: any, end: any) {
that.openEventForm();
},
eventClick: function(event, jsEvent) {
that.eventLocation = new PFLocation({});
that.eventBatch = new Batch();
that.eventRoom = new Room();
that.viewEvent(event);
}
});
$calendar.fullCalendar('removeEvents');
$calendar.fullCalendar('addEventSource', that.selectedEvents);
}
}
ngOnInit() {
// const $calendar = $('#fullCalendar');
let that = this;
// that.filter = false;
this.sub = this.route.params.subscribe(params => {
that.locationId = params['locationid'];
if (that.locationId) {
that.AuthService.getCurrentUser().then(user => {
that.currentUser = user;
var locations = that.currentUser.locations.admin.concat(
that.currentUser.locations.user
);
var location = find(locations, function(location) {
return location._id == that.locationId;
});
console.log(location);
console.log(that.currentUser);
});
}
this.userService
.getLocation(this.locationId)
.subscribe(location => {
console.log('Out of on Init');
this.selectedLocation = JSON.parse(
JSON.stringify(location)
);
that.getAllEvents(this.selectedLocation);
console.log(this.selectedLocation);
});
console.log(this.selectedLocation);
});
}

Script not works (ASP.NET MVC)

I have script for recording video
Here is code of it
var fileName;
stop.onclick = function () {
record.disabled = false;
stop.disabled = true;
window.onbeforeunload = null; //Solve trouble with deleting video
preview.src = '';
fileName = Math.round(Math.random() * 99999999) + 99999999;
console.log(fileName);
var full_url = document.URL; // Get current url
var url_array = full_url.split('/') // Split the string into an array with / as separator
var id = url_array[url_array.length - 1]; // Get the last part of the array (-1)
function save() {
$.ajax({
type: 'Post',
dataType: 'Json',
data: {
link: fileName,
id: id,
},
url: '#Url.Action("LinkWriter", "Interwier")',
success: function (da) {
if (da.Result === "Success") {
alert("lol");
} else {
alert('Error' + da.Message);
}
},
error: function (da) {
alert('Error');
}
});
}
I try to get url with this row var id = url_array[url_array.length - 1]; // Get the last part of the array (-1)
and with this code write to table filename
$.ajax({
type: 'Post',
dataType: 'Json',
data: {
link: fileName,
id: id,
},
url: '#Url.Action("LinkWriter", "Interwier")',
success: function (da) {
if (da.Result === "Success") {
alert("lol");
} else {
alert('Error' + da.Message);
}
},
error: function (da) {
alert('Error');
}
});
}
but it not works.
There is my Action method for it
[HttpPost]
public ActionResult LinkWriter(string link, int id) {
Link link_ = new Link
{
Link1 = link,
Interwier_Id = id,
};
db.Link.Add(link_);
db.SaveChanges();
return View();
}
But it not works. Where is my mistake?
UPDATE
As I understood not works this
function save() {
$.ajax({
type: 'Post',
dataType: 'Json',
data: {
link: fileName,
id: id,
},
url: '#Url.Action("LinkWriter", "Interwier")',
success: function (da) {
if (da.Result === "Success") {
alert("lol");
} else {
alert('Error' + da.Message);
}
},
error: function (da) {
alert('Error');
}
});
}

Stop x-tags from capturing focus/blur events

I am trying to create a custom element which wraps tinyMCE functionality.
I have the following:-
(function(xtag) {
xtag.register('x-tinymce', {
lifecycle:{
created: tinymceCreate,
removed: tinymceDestroy
},
accessors: {
disabled: {
attribute: {
boolean: true
},
get: getDisabledAttribute,
set: setDisabledAttribute
}
}
});
function tinymceCreate(){
var textarea = document.createElement('textarea');
var currentElement = this;
currentElement.textAreaId = xtag.uid();
textarea.id = currentElement.textAreaId;
currentElement.appendChild(textarea);
currentElement.currentMode = 'design';
var complexConfig = {
selector: '#' + currentElement.textAreaId,
setup: editorSetup
}
tinymce.init(complexConfig)
.then(function thenRetrieveEditor(editors) {
currentElement.currentEditor = editors[0];
currentElement.currentEditor.setMode(currentElement.currentMode ? currentElement.currentMode : 'design');
});
function editorSetup(editor) {
editor.on('blur', function blur(event) {
editor.save();
document.getElementById(editor.id).blur();
xtag.fireEvent(currentElement, 'blur', { detail: event, bubbles: false, cancellable: true });
});
editor.on('focus', function focus(event) {
xtag.fireEvent(currentElement, 'focus', { detail: event, bubbles: false, cancellable: true });
});
editor.on('BeforeSetContent', function beforeSetContent(ed) {
if (ed.content)
ed.content = ed.content.replace(/\t/ig, ' ');
});
}
}
function tinymceDestroy() {
if (this.currentEditor)
tinymce.remove(this.currentEditor);
}
function getDisabledAttribute() {
return this.currentMode === 'readonly';
}
function setDisabledAttribute(value) {
if (value) {
this.currentMode = 'readonly';
}
else {
this.currentMode = 'design';
}
if (this.currentEditor) {
this.currentEditor.setMode(this.currentMode);
}
}
})(xtag);
Now, when I register a blur event, it does get called, but so does the focus event. I think that this is because focus/blur events are captured by x-tag by default. I don't want it to do that. Instead, I want these events fired when the user focusses/blurs tinymce.
I am using xtags 1.5.11 and tinymce 4.4.3.
Update 1
OK, the problem is when I call:-
xtag.fireEvent(currentElement, 'focus', { detail: event, bubbles: false, cancellable: true });
This caused the focus to be lost on the editor and go to the containing eleemnt (x-tinymce). To counter this, I modified my editorSetup to look like this:-
function editorSetup(editor) {
// // Backspace is not detected in keypress, so need to include keyup event as well.
// editor.on('keypress change keyup focus', function(ed) {
// $j("#" + editor.id).trigger(ed.type);
// });
var isFocusFromEditor = false;
var isBlurFromEditor = false;
editor.on('blur', function blurEvent(event) {
console.log("blurred editor");
if (!isFocusFromEditor) {
editor.save();
xtag.fireEvent(currentElement, 'blur', { detail: event, bubbles: false, cancellable: false });
}
else {
console.log('refocussing');
isFocusFromEditor = false;
editor.focus();
isBlurFromEditor = true;
}
});
editor.on('focus', function focusEvent(event) {
console.log("Focus triggered");
isFocusFromEditor = true;
xtag.fireEvent(currentElement, 'focus', { detail: event, bubbles: false, cancellable: false });
});
editor.on('BeforeSetContent', function beforeSetContent(ed) {
if (ed.content) {
ed.content = ed.content.replace(/\t/ig, ' ');
}
});
}
This stops the blur event from triggering, unfortuantely, now, the blur event does not get called when you leave the editing area.
This feels like a tinyMCE problem, just not sure what.
Here is a solution that catches focus and blur events at the custom element level.
It uses the event.stopImmediatePropagation() method to stop the transmission of the blur event to other (external) event listeners when needed.
Also, it uses 2 hidden <input> (#FI and #FO) controls in order to catch the focus when the tab key is pressed.
document.registerElement('x-tinymce', {
prototype: Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
var textarea = this.querySelector('textarea')
var output = this.querySelector('output')
output.textContent = "state"
var self = this
self.textAreaId = this.dataset.id // xtag.uid();
textarea.id = self.textAreaId
var complexConfig = {
selector: '#' + self.textAreaId,
setup: editorSetup,
}
var FI = this.querySelector('#FI')
FI.addEventListener('focus', function(ev) {
if (ev.relatedTarget) {
var ev = new FocusEvent('focus')
self.dispatchEvent(ev)
} else {
var ev = new FocusEvent('blur')
self.dispatchEvent(ev)
focusNextElement(-1)
}
})
var FO = this.querySelector('#FO')
FO.addEventListener('focus', function(ev) {
if (!ev.relatedTarget) {
var ev = new FocusEvent('blur')
self.dispatchEvent(ev)
focusNextElement(1)
} else {
var ev = new FocusEvent('focus')
self.dispatchEvent(ev)
}
})
var focused = false
this.addEventListener('focus', function(ev) {
console.log('{focus} in ', this.localName)
if (!focused) {
focused = true
output.textContent = focused
self.editor.focus()
} else {
console.error('should not be here')
ev.stopImmediatePropagation()
}
})
this.addEventListener('blur', function(ev) {
console.log('{blur} in %s', this.localName)
if (focused) {
focused = false
output.textContent = focused
} else {
console.log('=> cancel blur')
ev.stopImmediatePropagation()
}
})
tinymce.init(complexConfig).then(function(editors) {
//self.currentEditor = editors[0]
})
//private
function focusNextElement(diff) {
//add all elements we want to include in our selection
var focussableElements = 'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'
if (document.activeElement) {
var focussable = Array.prototype.filter.call(document.querySelectorAll(focussableElements), function(element) {
//check for visibility while always include the current activeElement
return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement
})
var index = focussable.indexOf(document.activeElement)
focussable[index + diff].focus()
}
}
function editorSetup(editor) {
console.warn('editor setup')
self.editor = editor
editor.on('focus', function(event) {
if (!focused) {
var ev = new FocusEvent('focus')
self.dispatchEvent(ev)
}
})
editor.on('blur', function(event) {
//if ( focused )
{
var ev = new FocusEvent('blur')
self.dispatchEvent(ev)
}
})
}
}
},
detachedCallback: {
value: function() {
if (this.editor)
tinymce.remove(this.editor)
}
}
})
})
var xElem = document.querySelector('x-tinymce')
xElem.addEventListener('focus', function(ev) {
console.info('focus!')
})
xElem.addEventListener('blur', function(ev) {
console.info('blur!')
})
<script src="http://cdn.tinymce.com/4/tinymce.min.js"></script>
<input type="text">
<x-tinymce data-id="foo">
<output id=output></output>
<input type=text id=FI style='width:0;height:0;border:none'>
<textarea>content</textarea>
<input type=text id=FO style='width:0;height:0;border:none'>
</x-tinymce>
<input type="text">

How to get Ember.js's bindAttr to refresh?

I am trying to create "sort by" buttons which change sorting and css-classes when clicked on using Ember.js.
The sorting part and the initial class assignments work, however, the class assignments are not refreshed when I update the dependant properties.
What am I missing?
This is in my HTML:
<script type="text/x-handlebars" data-template-name="sort-option-item">
<dd {{bindAttr class="IsActive:active IsDesc:reversed"}}
{{action sortBy on="click"}}>{{Name}}</dd>
</script>
<script type="text/x-handlebars">
{{#each option in controller.sortOptions}}
{{view App.SortOptionView controllerBinding="option"}}
{{/each}}
</script>
An this is in my Javascript:
var App = null;
$(function () {
App = Ember.Application.create();
// Define Types:
App.SortOptionCtrl = Ember.Controller.extend({
Name: null,
Predicate: null,
Controller: null,
IsActive: false,
IsDesc: false,
sortBy: function () {
if (this.Controller != null)
this.Controller.sortBy(this.Predicate);
},
Check: function () {
this.IsActive = this.Controller != null
&& this.Controller.isSortedBy(this.Predicate);
this.IsDesc = this.Controller != null
&& this.Controller.isSortedDescBy(this.Predicate);
// adding an alert(this.IsActive); here
// proves that the function is indeed called and works as expected
}
});
App.ProductsController = Ember.ArrayController.extend({
initialized: false,
content: [],
viewContent: [],
sortProperties: ['Order'],
sortAscending: true,
sortOptions: [],
initialize: function () {
if (this.initialized == true)
return;
this.initialized = true;
var ctrl = this;
this.sortOptions.pushObject(App.SortOptionCtrl.create({
Name: 'Unsorted',
Predicate: null,
Controller: ctrl,
}));
this.sortOptions.pushObject(App.SortOptionCtrl.create({
Name: 'By Name',
Predicate: 'Name',
Controller: ctrl,
}));
this.sortOptions.pushObject(App.SortOptionCtrl.create({
Name: 'By Date',
Predicate: 'Date',
Controller: ctrl,
}));
this.sortOptions.forEach(function (opt) { opt.Check(); });
},
load: function () {
this.initialize();
// ....
},
sortBy: function (predicate) {
var prevPredicate = this.sortProperties[0];
if (predicate == prevPredicate && predicate != null) {
this.sortAscending = !(this.sortAscending);
}
else {
this.sortAscending = true;
}
this.sortProperties.length = 0;
if (predicate)
this.sortProperties.pushObject(predicate);
else
this.sortProperties.pushObject('Order');
this.sortOptions.forEach(function (opt) { opt.Check(); });
},
isSortedBy: function (predicate)
{
if (predicate == null)
predicate = 'Order';
var activePredicate = this.sortProperties[0];
if (predicate == activePredicate) {
return true;
}
else {
return false;
}
},
isSortedDescBy: function (predicate) {
if (predicate == null)
predicate = 'Order';
var activePredicate = this.sortProperties[0];
if (predicate == activePredicate) {
if (this.sortAscending)
return false;
else
return true;
}
else {
return false;
}
},
});
App.SortOptionView = Ember.View.extend({
templateName: 'sort-option-item'
});
// Create Instances:
App.productsController = App.ProductsController.create({
});
App.productsController.load();
App.initialize();
});
Versions: Ember: 1.0.0-rc.2, handlebars: 1.0.0-rc.3
If you want your views to react to whatever happens in the controller, you should create computed properties (via fn(){}.property('dependency')). However, in order for computed properties to work properly, you need to use Ember's get() and set() property accessors.
In your code you are doing things like
this.IsActive = this.Controller != null &&
this.Controller.isSortedBy(this.Predicate);
When you should be doing something like this:
this.set('active',
this.get('controller') != null &&
this.get('controller').isSortedBy(this.get('Predicate'))
);
You might have noticed that this code is setting a value into active, but the template is listening to isActive. That property has been changed into a computed property:
isActive: function() {
return this.get('active');
}.property('active')
It will listen for changes in the active property, and whenever that happens, it will cache the new value, and notify all subscriber objects to refresh/update.
Using Ember's get and set accessors is indicated in order to properly use the observables that make this chain of events possible.
I have modified your sample applying get and set where appropriate.
You can see it in this fiddle: http://jsfiddle.net/schawaska/fRMYu/
After Joe's help, more reading of Ember's manual and clean up of my code, I got to this sorter solution:
Sort option controller:
App.SortOptionCtrl = Em.Controller.extend({
Name: null,
Predicate: null,
_isActive: false,
isActive: function () {
return this.get('_isActive');
}.property('_isActive'),
_isDesc: false,
isDesc: function () {
return this.get('_isDesc');
}.property('_isDesc'),
controller: null,
updateState: function () {
if (!this.Predicate) {
this.set('_isActive', (this.get('controller')
.get('activePredicate') == null));
this.set('_isDesc', false);
}
else {
this.set('_isActive', (this.get('controller')
.get('activePredicate') == this.Predicate));
this.set('_isDesc', (this.get('_isActive')
&& !this.get('controller').get('sortAscending')));
}
}.observes('controller.activePredicate', 'controller.sortAscending'),
sortBy: function () {
if (this.get('controller') != null) {
this.get('controller').sortBy(this.Predicate);
}
},
});
Products controller:
App.ProductsController = Ember.ArrayController.extend({
content: [],
viewContent: [],
activePredicate: null,
sortProperties: ['Order'],
sortAscending: true,
sortOptions: [],
filter: function (obj) {
return true;
},
init: function () {
this._super();
var ctrl = this;
this.sortOptions.pushObject(App.SortOptionCtrl.create({
Name: 'Unsorted',
Predicate: null,
controller: ctrl,
}));
this.sortOptions.pushObject(App.SortOptionCtrl.create({
Name: 'By Name',
Predicate: 'Name',
controller: ctrl,
}));
this.sortOptions.pushObject(App.SortOptionCtrl.create({
Name: 'By Date',
Predicate: 'Date',
controller: ctrl,
}));
this.sortOptions.forEach(function (opt) {
opt.updateState();
});
},
sortBy: function (predicate) {
var prevPredicate = this.sortProperties[0];
if (predicate == prevPredicate && predicate != null) {
this.set('sortAscending', !(this.get('sortAscending')));
}
else {
this.set('sortAscending', true);
}
this.set('activePredicate', predicate);
this.set('sortProperties.length', 0);
if (predicate)
this.get('sortProperties').pushObject(predicate);
else
this.get('sortProperties').pushObject('Order');
},
});

Focusing or selecting the row, after grid load

I have a jsp page with button.
On clicking of the button, it creates a dojo grid dynamically.
After grid gets loaded, am iterating through the grid and if particular value is there, the whole row get selected.
I achieved it by using the below code, row gets selected, but am getting an error.
Error: "error: dojo.data.ItemFileReadStore: Invalid item argument"
Am using dojoX.grid.DataGrid(1.7)
Here is the code to create the dynamic grid:
var ioArgs = {
url: "./DynamicDBServlet",
content: { TABLE_NAME:tableName,WHERE_CONDN:condtn,COLUMNS:gridColumnName,ACTION:'select'},
handleAs: "json",
load: function(response) {
//alert(response["items"][0].USER_ID);
var tbl = document.getElementById(gridID);
//alert(tbl);
if(tbl) tbl.parentNode.removeChild(tbl);
var gridLayout = [];
var key;
colmn=gridColumnName.split(',');
var i=0;
while(i<colmn.length) {
if(colmn[i]=="STATUS"){
key = colmn[i];
gridLayout.push({
field: key,
name: displayName[i],
formatter:function(opvalue){
//alert(opvalue);
opvalue = parseInt(opvalue);
if(opvalue==1){
return "Executing";}
if(opvalue==2){
return "Suspended";}
if(opvalue==3){
return "Completed";}
if(opvalue==4){
return "Aborted";}
if(opvalue==5){
return "Error";}
if(opvalue==6){
return "Terminated";}},
width: '200px',
editable: false});
i++;
}if(colmn[i]=="PRIORITY"){
key = colmn[i];
gridLayout.push({
field: key,
name: displayName[i],
formatter:function(opvalue){
if(opvalue==1){
return "High";}
if(opvalue==8){
return "Normal";}
if(opvalue==15){
return "Low";}
},
width: '200px',
editable: false});
i++;
}if(colmn[i]=="PROCESS_METADATYPE"){
key = colmn[i];
gridLayout.push({
field: key,
name: displayName[i],
formatter:function(opvalue){
if(opvalue==4497){
return "Provisioning Sub Process";}
if(opvalue==4496){
return "Provisioning Loop";}
if(opvalue==4494){
return "Main Process";}
},
width: '200px',
editable: false});
i++;
}if(colmn[i]=="ORDER_TYPE"){
key = colmn[i];
gridLayout.push({
field: key,
name: displayName[i],
formatter:function(opvalue){
if(opvalue==4488){
return "Training Order";}
},
width: '200px',
editable: false});
i++;
}else{
key = colmn[i];
gridLayout.push({
field: key,
name: displayName[i],
width: '200px',
editable: false});
i++;
}
}
//alert(gridLayout);
var gridStore = new dojo.data.ItemFileWriteStore({
data: response
});
var dumm = dijit.byId('dynamicgrid'+tableName);
if(dumm) {
dumm.destroy();
}
var finderResponse = new dojox.grid.DataGrid({
id:"dynamicgrid"+tableName,
query: { },
store:gridStore,
structure: gridLayout,
selectionMode: "single"
}, document.createElement("div"));
dojo.byId(parentDiv).appendChild(finderResponse.domNode);
//var varb=dijit.byId("finderResponseGrid"+tableName);
//alert(varb);
//varb.layout.setColumnVisibility(0, false);
gbshowgridFlag = true;
finderResponse.startup();
//dijit.byId('dynamicgridCWPROCESS').focus.setFocusIndex(0);
try{
for( var i=0; i < dijit.byId('dynamicgridCWPROCESS').rowCount; i++){
var items = dijit.byId('dynamicgridCWPROCESS').getItem(i);
var value=dijit.byId('dynamicgridCWPROCESS').store.getValue(items,"PROCESS_ID");
if(value=="2507"){
dijit.byId('dynamicgridCWPROCESS').selection.setSelected(i,true);
}
}
if(defineDbl){
getProcessgrid();
}
}catch(e){
alert(e);
}
},
error: function(error) {
alert("An unexpected error occurred: " + error);
}
};
var deferred = dojo.xhrPost(ioArgs);
This code help me to select row in grid :
grid.selection.select(rowIndex);
In your case:
finderResponse.selection.select(rowIndex);
I hope this will solve your problem.
function selectRowByYourOwn(){
var row = null;
// you have to get is some have.. try onSelected='yourOwnFn', on your table tag or your div tag.
// function yourOwnFn(rowID){ row = rowID;}
grid.selection.clear();
grid.selection.setSelected(row, true);
grid.render();
}

Resources