prevent expanding parent node when its datasource is updated due to addition of a child item inside kendo treeview - kendo-treeview

I have a kendotreeview with 3 root parent nodes.Example is shown below.
When I drop child3 into New SubGroup, the node "New Subgroup" gets expanded by default even if it was collapsed before. I would like to prevent this possibility. If New SubGroup was expanded before, then I would like to keep it as it is. The problem is that the expand gets called before the databound event and hence I am stuck here.
Please help.
parent1:
--New SubGroup
--Child2
--Child3
--Child4
parent2:
--Child4
--Child5
Code snippet:
dataBound: function (e) {
console.log("DataBound", e.node);
var nodedataitem = $("#DimMeasTree").data("kendoTreeView").dataItem($(e.node));
if (nodedataitem.FieldKey === "SubGroup" && ($(e.node).attr("aria-expanded")) === "true") {
$("#DimMeasTree").data("kendoTreeView").collapse($(e.node));
}
}

I am subscribing to my own custom expand function (i.e. subgroup_expand()) after I initialize my treeview. It is as demonstrated below:
<div id="treeview"></div>
<script>
function subgroup_expand(e) {
if (typeof event === 'undefined') {
//If browser is Firefox, the subgroup will expand and do not close automatically.
// It is because Firefox does not support "event" attribute gloabally as in IE or in Google chrome.
}
else if (!!e.node && typeof(event) !== 'undefined' && event.type !== "click" && event.type !== "dblclick") {
var nodedataitem = $("#treeview").data("kendoTreeView").dataItem($(e.node));
if (nodedataitem.FieldKey === "SubGroup") {
// e.preventDefault();
setTimeout(function () {
//Collapse the subgroup if it was not expanded by click or dblclick.
$("#treeview").data("kendoTreeView").collapse($(e.node));
}, 50);
}
}
}
$("#treeview").kendoTreeView({
dataSource: modeldata
});
var treeview = $("#treeview").data("kendoTreeView");
treeview.bind("expand", subgroup_expand);
</script>

Related

EXTJS Grid inside a tab - filter only works on page refresh

we are using extjs 4.1 and i have run into a weird issue. We have a group of 3 tabs and those tabs contains extjs grids. extjs grids in the first 2 are loading the data fine and also have filter option. extjs grid in the 3rd tab, loads the data but does not show the filter menu. However, when the page is refreshed while on the 3rd tab, the grid shows the filter menu. i am new to extjs. please help.
ext-all-debug-w-comments-v4.1.2a.js:144232 Uncaught TypeError: Cannot read property 'enable' of null
at constructor.showMenuBy (ext-all-debug-w-comments-v4.1.2a.js:144232)
at constructor.onHeaderTriggerClick (ext-all-debug-w-comments-v4.1.2a.js:141028)
at constructor.onElClick (ext-all-debug-w-comments-v4.1.2a.js:142124)
at HTMLDivElement.eval (eval at cacheableFunctionFactory (ext-all-debug-w-comments-v4.1.2a.js:683), <anonymous>:6:13)
at HTMLDivElement.wrap (ext-all-debug-w-comments-v4.1.2a.js:15197)
the following line is where the error is happening but this line is within extjs
showMenuBy: function(t, header) {
var menu = this.getMenu(),
groupMenuItem = menu.down('#groupMenuItem'), //this menu is null and therefore the error but this is auto generated by extjs
groupMenuMeth = header.groupable === false ? 'disable' : 'enable',
groupToggleMenuItem = menu.down('#groupToggleMenuItem');
groupMenuItem[groupMenuMeth](); //this statement is null and throws the error
if (groupToggleMenuItem) {
groupToggleMenuItem[this.view.store.isGrouped() ? 'enable' : 'disable']();
}
Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);
}
//code from the onHeaderTrigerClick stackTrace:
onHeaderTriggerClick: function(header, e, t) {
// generate and cache menu, provide ability to cancel/etc
var me = this;
if (header.fireEvent('headertriggerclick', me, header, e, t) !== false && me.fireEvent("headertriggerclick", me, header, e, t) !== false) {
me.showMenuBy(t, header);
}
}
//code from onElClick:
onElClick: function(e, t) {
// The grid's docked HeaderContainer.
var me = this,
ownerHeaderCt = me.getOwnerHeaderCt();
if (ownerHeaderCt && !ownerHeaderCt.ddLock) {
// Firefox doesn't check the current target in a within check.
// Therefore we check the target directly and then within (ancestors)
if (me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl.dom || e.within(me.triggerEl))) {
ownerHeaderCt.onHeaderTriggerClick(me, e, t);
// if its not on the left hand edge, sort
} else if (e.getKey() || (!me.isOnLeftEdge(e) && !me.isOnRightEdge(e))) {
me.toggleSortState();
ownerHeaderCt.onHeaderClick(me, e, t);
}
}
}
in every case, parameter header is null.
see Ext.grid.feature.Grouping.
It seems probably a css issue, might be caused by the order in which components load and height applied to header.
Use below css:
.ui-grid-header-cell{
height:60px;
max-height:60px;
}
Identify the id of header component and use it in place of .ui-grid-header-cell

With protractor, how can I determine the class of a parent element to see if it is disabled?

Using Protractor, I'm struggling with the syntax for selecting the parent element of a found item. We have a paginated list of records, and what I'd like to do is see if the right-arrow for that list is disabled. The trick though, is that the arrow gets disabled by its parent div element.
Here is the abbreviated markup.
<px-page-navigation id="myList" name="myList" page-number="vm.pageNumber" total-page-count="vm.totalPageCount" show-first-last-page="vm.showFirstLastPage" class="ng-isolate-scope">
<div>
<!-- Other pagination button markup omitted for clarity -->
<div class="btn-group px-margin-top-xx-small px-margin-left-xx-small px-font-link-disabled" ng-class="vm.arrowFont(vm.pageNumber + 1)" ng-click="vm.setPage(vm.pageNumber + 1)">
<!-- Find this "right arrow" element, then get the parent div and determine if it has a class of "px-font-link-disabled" -->
<i class="fa fa-chevron-right px-image-font bold"></i>
</div>
</div>
</px-page-navigation>
I've been able to successfully grab this element and click it.
<i class="fa fa-chevron-right px-image-font bold"></i>
But after capturing that element, I need to examine the parent container div to determine if that div has a class of "px-font-link-disabled". In cases where it's disabled, then the test will not click the right arrow. Otherwise the right-arrow icon gets clicked.
These are a couple of attempts that I've made to find the parent element.
// Attempt A
function findNewInstrument(newInstrumentName: string) {
element(by.linkText(newInstrumentName)).isPresent().then(function (exists) {
if (exists === true) {
console.log('found it');
return element(by.linkText(newInstrumentName));
} else {
var RIGHT_ARROW = '#myList i.fa-chevron-right';
// TODO: Is the right arrow enabled?
element(by.css('#myList > i.fa-chevron-right: parent')).then(function (arrowContainer) {
if (page.hasClass(arrowContainer, 'px-font-link-disabled')) {
// Paginated through all pages, and didn't find the instrument.
} else {
// Instrument was not on this page. Click the right-arrow and look for it again.
element(by.css(RIGHT_ARROW)).getLocation().then(function (location) {
browser.executeScript('window.scrollTo(' + location.x + ',' + location.y + ')').then(function () {
element(by.css(RIGHT_ARROW)).click();
});
});
findNewInstrument(newInstrumentName);
}
});
}
});
}
// Attempt B
function findNewInstrument(newInstrumentName: string) {
element(by.linkText(newInstrumentName)).isPresent().then(function (exists) {
if (exists === true) {
console.log('found it');
return element(by.linkText(newInstrumentName));
} else {
// TODO: Is the right arrow enabled?
var RIGHT_ARROW = '#myList i.fa-chevron-right';
element(by.css(RIGHT_ARROW)).then(function (rightArrow) {
var parentDiv = rightArrow[0].findElement(by.xpath('ancestor::div'));
if (page.hasClass(parentDiv, 'px-font-link-disabled')) {
// The right arrow is disabled.
} else {
// Click the right arrow, then inspect the grid again.
element(by.css(RIGHT_ARROW)).getLocation().then(function (location) {
browser.executeScript('window.scrollTo(' + location.x + ',' + location.y + ')').then(function () {
element(by.css(RIGHT_ARROW)).click();
});
});
findNewInstrument(newInstrumentName);
}
});
}
});
}
This hasClass function gets called by the above. This is in our base pageObject.
public hasClass(elm: protractor.ElementFinder, theClass: string) {
return elm.getAttribute('class').then(function (classes) {
return classes.split(' ').indexOf(theClass) !== -1;
});
};
In both cases (attempts A and B) the error that appears is:
TypeError: element(...).then is not a function
Once I've captured the right-arrow element, what syntax would I use to examine the classes of the parent div to determine if that div has the disabled class? If there is a simpler approach than the path I'm pursuing, then I'd be open to that as well. Thanks for your help.
TypeError: element(...).then is not a function
You cannot resolve the ElementFinder with then() anymore - this was an intentional breaking change in Protractor 2.0.0.
The following is how I would check the class of the parent div element:
var rightArrow = $('#myList i.fa-chevron-right');
var parentDiv = rightArrow.element(by.xpath('ancestor::div'));
expect(parentDiv).toHaveClass("px-font-link-disabled");
where toHaveClass is a convenient custom matcher:
beforeEach(function() {
jasmine.addMatchers({
toHaveClass: function() {
return {
compare: function(actual, expected) {
return {
pass: actual.getAttribute("class").then(function(classes) {
return classes.split(" ").indexOf(expected) !== -1;
})
};
}
};
},
});
});

Marionette prevent region destroy

I am using Marionette region to display templates based on user radio input:(text/file).
Here is my itemview
var fileTemplateView = Marionette.ItemView.extend({
template : "#file-upload-template"
});
and region defined as
regions : {
composeRegion : "#compose-region",
}
and event declared as
events : {
"click #msg-input-type input:radio" : "changedRadio"
}
and event trigger function is
changedRadio : function(evt) {
var self = this;
var checkedObject = evt.currentTarget;
console.log('Radio Change Event'+checkedObject.value);
if (checkedObject.value === "file") {
if (self.fileView === undefined) {
self.fileView = new fileTemplateView();
}
this.composeRegion.show(self.fileView, { preventDestroy: true });
} else if (checkedObject.value === "text") {
if (self.textView === undefined) {
self.textView = new textTemplateView();
}
this.composeRegion.show(self.textView, { preventDestroy: true });
}
But preventDestroy method may not be working as defined where template is resetting on everytime radio event happen.
Your help is appreciated.
The preventDestroy option prevents the swapped view from being destroyed. This doesn't mean that is won't be re-rendered the next time it is shown. Make sure you are saving the state of the view so it can be used to reconstruct the view properly the next time.

CheckAll/UncheckAll issue with Subscribe ? Knockout

I been trying to do checkbox Checkall and UnCheckall using subscribe and i'm partially successful doing that but i am unable to find a fix in couple of scenarios when i am dealing with subscribe .
Using subscribe :
I am here able to checkAll uncheckAll but when i uncheck a child checkbox i.e test1 or test2 i need my parent checkbox name also to be unchecked and in next turn if i check test1 the parent checkbox should be checked i.e keeping condition both child checkboxes are checked .
For fiddle : Click Here
ViewModel :
self.selectedAllBox.subscribe(function (newValue) {
if (newValue == true) {
ko.utils.arrayForEach(self.People(), function (item) {
item.sel(true);
});
} else {
ko.utils.arrayForEach(self.People(), function (item) {
item.sel(false);
});
}
});
The same scenario can be done perfectly in easy way using computed but due some performance issues i need to use subscribe which is best way it wont fire like computed onload .
Reference : Using computed same thing is done perfectly check this Fiddle
I tried to use change event in individual checkbox binding but its a dead end till now.
Any help is appreciated .
Your subscription only applies to edits on the selectedAllBox. To do what you want, you'll need subscriptions on every Person checkbox as well, to check for the right conditions and uncheck the selectedAllBox in the right situations there.
It strikes me as odd that this would be acceptable but using computed() is not. Maybe you should reconsider that part of your answer. I would much rather compute a "isAllSelected" value based on my viewModel state, then bind the selectedAllBox to that.
I solved a similar problem in my own application a couple of years ago using manual subscriptions. Although the computed observable method is concise and easy to understand, it suffers from poor performance when there's a large number of items. Hopefully the code below speaks for itself:
function unsetCount(array, propName) {
// When an item is added to the array, set up a manual subscription
function addItem(item) {
var previousValue = !!item[propName]();
item[propName]._unsetSubscription = item[propName].subscribe(function (latestValue) {
latestValue = !!latestValue;
if (latestValue !== previousValue) {
previousValue = latestValue;
unsetCount(unsetCount() + (latestValue ? -1 : 1));
}
});
return previousValue;
}
// When an item is removed from the array, dispose the subscription
function removeItem(item) {
item[propName]._unsetSubscription.dispose();
return !!item[propName]();
}
// Initialize
var tempUnsetCount = 0;
ko.utils.arrayForEach(array(), function (item) {
if (!addItem(item)) {
tempUnsetCount++;
}
});
var unsetCount = ko.observable(tempUnsetCount);
// Subscribe to array changes
array.subscribe(function (changes) {
var tempUnsetCount = unsetCount();
ko.utils.arrayForEach(changes, function (change) {
if (change.moved === undefined) {
if (change.status === 'added') {
if (!addItem(change.value))
tempUnsetCount++;
} else {
if (!removeItem(change.value))
tempUnsetCount--;
}
}
});
unsetCount(tempUnsetCount);
}, null, 'arrayChange');
return unsetCount;
}
You'll still use a computed observable in your viewmodel for the the select-all value, but now it'll only need to check the unselected count:
self.unselectedPeopleCount = unsetCount(self.People, 'Selected');
self.SelectAll = ko.pureComputed({
read: function() {
return self.People().length && self.unselectedPeopleCount() === 0;
},
write: function(value) {
ko.utils.arrayForEach(self.People(), function (person) {
person.Selected(value);
});
}
}).extend({rateLimit:0});
Example: http://jsfiddle.net/mbest/dwnv81j0/
The computed approach is the right way to do this. You can improve some performance issues by using pureComputed and by using rateLimit. Both require more recent versions of Knockout than the 2.2.1 used in your example (3.2 and 3.1, respectively).
self.SelectAll = ko.pureComputed({
read: function() {
var item = ko.utils.arrayFirst(self.People(), function(item) {
return !item.Selected();
});
return item == null;
},
write: function(value) {
ko.utils.arrayForEach(self.People(), function (person) {
person.Selected(value);
});
}
}).extend({rateLimit:1});
http://jsfiddle.net/mbest/AneL9/98/

wait for backbone template to be appended to the dom

I am trying to bind events to elements that are placed by appending a backbone template:
appendEditTemplateAndSetEvents: function() {
var associatedCollection = App.Helpers.findAssociatedCollection(this.allCollections, this.associatedCollectionId);
var template = this.setEditTemplateForElement(associatedCollection.type);
var modalBody = this.$el.find('.modal-body');
modalBody.empty();
var firstModel = associatedCollection.at(0);
if(template.mainTemplate !== null) {
modalBody.append($('#edit-form-element-frame').html());
//each mode in collection
associatedCollection.each(function(model){
if(model.get('positionInContainer') === 1) {
firstModel = model;
}
console.log(model.attributes);
modalBody.find('.elements-in-editmodal-wrapper').append(template.mainTemplate(model.toJSON()));
});
}
if( template.templateValidation.length !== 0 ) {
modalBody.append('<hr><h3>Validateregels</h3>');
_.each(template.templateValidation, function(val, index) {
modalBody.append(val(firstModel.toJSON()));
});
}
//set listeners and handlers that apply when a edit modal is open
this.validationEventsForEditModal(firstModel);
this.editErrorMessagesInModal(firstModel);
},
Now the problem is that when the last two functions are called the html of the templates isn't appended yet so the the events are binded to an object with a length of 0.
Does anyone have a decent solution for this async problem? I tried $.Defferred but that did not work, but maybe someone get's it working.
I solved this by using this.$el.find(...) in the functions:
this.validationEventsForEditModal(firstModel);
this.editErrorMessagesInModal(firstModel);
I don't know if it's still an async problem, but this solves it.

Resources