ExtJS: Check if Component is direct descendant of Ext.TabPanel - javascript

So far EVERY ONE of my questions was already asked and answered here, to many thanks to all time machine help I already got here. :) But everything ends one day and so here is my first own question:
For error handling purposes, I'm trying to attach some generic listeners to all interesting components as they are added. This can be done with Ext.ComponentMgr.all.on('add', function (cnt, cmp) {}). Then I look what type of component it is, for example to add a click listener to every button.
What I now need to do is to add an activate listener to every panel which is added to a tabpanel. My problem is: I have no clue how to determine if a component is a direct descendant of a tabpanel and how to do it the right way.
I already tried this:
Ext.ComponentMgr.all.on('add', function (cnt, cmp) {
if (cmp.ownerCt != undefined && cmp.ownerCt.getXType () == 'tabpanel') {
console.log (cmp.getXType () + ' in tabpanel with id "' + cmp.getId ());
}
});
The bad thing is, some Ext doc reads "Do not rely on ownerCt" and it's right: Every other of my components have an ownerCt, but NOT panels when added late to tabpanels.
I know there is Ext.Panel.findParentByType(), but this finds containers at any level above and I don't think it's the right way (-> performance).
Thanks in advance for every answer!

As you said, the ownerCt may get poked in at any time during the construction depending on the order in which you do it. Why not create a tab panel subclass that automatically handles it for you?

I've done some more trial and error and found a solution for myself. I think I should share it with you, maybe someone can use it one day:
When Ext.ComponentMgr.all.on('add', function (cnt, cmp) {}) fires, ownerCt is of course empty, because the component only has been created, but not necessarily added to another component.
So I've done the following:
Ext.ComponentMgr.all.on('add', function (cnt, cmp) {
cmp.addListener ('added', function (cmp) {
var parent = cmp.findParentByType ('tabpanel');
if (parent != undefined) {
if (parent.items.contains (cmp)) {
cmp.addListener ('activate', function (cmp) {
doThings();
});
}
}
});
}
When the component is added to the component manager (new Ext.Panel()), create a listener for added to the component
When added, check if any parent is a tabpanel
If it is, check if you component is a direct descendant of the tabpanel (= is contained in its items)
???
Profit!

Related

fullcalendar event rendering performance issue

So, basically all my events(there's min. 360 of them) have team1 vs. team2 or - vs. team2 or team1 vs. - placeholders.
And on the initial render events change color depending on whether the event has one or two teams.
Orange color for the one team , and green for the two teams. Also, the event changes color on click.
But mostly, I'm interested in increasing performance with rendering events.
Rendering performance is going really bad in fullCalendar, and I couldn't find any solution to this problem.
So here's my code:
eventRender: function (event, element) {
$(element).append((event.teams[0] != null ? event.teams[0] : '-') + '</br> vs. </br>' + (event.teams[1] != null ? event.teams[1] : '-'));
if (event.teams.length === 1) {
$(element).css('background', 'orange');
}
else if (event.teams.length > 1) {
$(element).css('background', 'green');
}
}
My main issue is that when I click on event to change its color, the script automatically goes to the eventRender or eventAfterRender event, and its behavior is exactly like the for statement - it iterates over events and then it does the stuff that I want to do with the individual event, but only when the loop lands on the clicked event.
Also, in the eventClick I've called $('#myCalendar').fullcalendar('updateEvent',event) and I think there is a bug, because it automatically goes to the eventAfterRender or the eventRender, iterating over the whole events collection again.
Even tough 'updateEvent' parameter should instruct fullCalendar to update/render only the specific event.
Does anyone have any advice on this subject?
Fullcalendar now supports the renderEvents method: https://fullcalendar.io/docs/renderEvents.
Simply build your events list and send them all at once:
$("#calendar").fullCalendar('renderEvents', events, true);
I know this is an old question, but i solved the same performance problem in v5 of the fullcalendar with this configuration option:
https://fullcalendar.io/docs/rerenderDelay
It basically adds a delay after each operation that would trigger a render event.
if the framework detects another operation within that delay, it renders these events in one operation and thereby increases performance.
setting the value to 1 (so 1 millisecond delay) did the trick for me. I simply added it to the configuration in my angular component:
calendarOptions: CalendarOptions = {
...,
rerenderDelay: 1,
}
In fullcalendars source-code (at least in my version of it) there is the renderEvent-handler, that calls reportEvents -function which is the bottleneck of performance. I worked my way around this issue, by adding handling of mass-rendering events to the source-code.
I wrote a short function:
function massRenderEvents(events, stick) {
var i;
for (i = 0; i < events.length; i += 1) {
normalizeEvent(events[i]);
if (!events[i].source) {
if (stick) {
stickySource.events.push(events[i]);
events[i].source = stickySource;
}
cache.push(events[i]);
}
}
reportEvents(cache);
}
Under "EventManager" -function, and added it to EventManagers exports, like:
t.massRenderEvents = massRenderEvents;
Now, for every batch of rendered events, the heavy and slow reportEvents is called just once. Note, that massRenderEvents -function is very similar to the original renderEvent -function.
I have changed
$("#calendar").fullCalendar('renderEvent', eventData1, true);
to
$("#calendar").fullCalendar('addEventSource', eventData1, true);
and that worked for me. I have read the issue on several related website and as per their suggestion I have done this.
The main difference between renderEvent and addEventSource is that the first one tries to interact with calendar when even a single event created which take much time because of regular callback function, and the second one sends a bucket of JSON events to calendar which require only single callback function which improve the performance and take less time.

Is jQuery Infinite nesting bad for performance?

// page load
InitTeacherLinks()
function InitTeacherLinks()
{
$(".open-ungraded-test").click(function()
{
$.post("class_viewer.php", {
_open_lesson_direct : 1
}, function(data)
{
$("#content_display").html(data);
InitGradingActions(test_taken_id); // Notice this Call
});
})
}
function InitGradingActions(test_taken_id)
{
$("#save_grading").click(function()
{
$.post("class_viewer.php", {
_save_graded_test : 1
}, function(data)
{
$("#content_display").html(data);
InitTeacherLinks(); // Is this Circular logic?
});
});
}
Basically, I have a div called content_display that shows a list of tests. After I load it full of tests, I have to make each test link clickable. So I do that in this function: InitTeacherLinks() where they can view an individual test.
Well the user can exit the test and go back to the original test list. So I have to call the parent function again in the child function.
While this DOES work, I notice I do it often. Is this bad logic or bad for performance?
Note: I can only think of one possible reason why this may work. Please correct me if I am wrong. when save_grading is clicked, it effectively destroys reference to the original (parent function) so rather than creating a duplicated reference, we are simply reinitialize it. Is this right?
I don't think there's a stack overflow issue with the code, but it does look like there may be an error. Every time InitTeacherLinks() is executed, a new click handler is assigned to .open-ungraded-test. That means there is an additional ajax post made during that click for every time InitTeacherLinks() is run, which could be a lot.
At least that's how it looks from the code. This could depend on the structure of your document.
I ended up not changing anything and went with what I have above.
Because the click events are unbinded every time the element is destroyed, this was not creating an endless loop (or Stackoverflow error) which was my concern. The code in the question is correct.

How to animate unchanged ng-repeat with AngularJS

I have a template that looks like this:
<p ng-repeat="item in myobj.items" class="toAnimate">{{item}}</p>
and I would like to use the animate module do a jQueryUI addClass/removeClass animation on the element using the JavaScript method described in the docs:
ngModule.animation('.toAnimate', function() {
return {
enter: function(element) {
element.addClass('pulse').removeClass('pulse', 2000);
}
};
});
This works beautifully, but the problem is that, since I want to use the p.toAnimate element to display status messages, it will not change the content according to angular.
To break it down a little further, say I have a name field. When I click Save the message Name was saved successfully. is displayed. Now if I modify the name and click save again, assuming the save was successful, the message should be re-displayed to give the user feedback of the newly edited name. The pulse does not happen, however, because the items in myobj.items didn't technically change.
I realize that I could remove the item after a period of time (and that is probably the route I will take to implement the real solution), but I'm still interested to see if this sort of thing can be done using AngularJS.
What I want to do is register with angular that the message should be treated as new even though it is not. Is there any way to do this?
A fiddle to go along with this: http://jsfiddle.net/Jw3AT/
UPDATE
There is a problem with the $scope.$$phase approach in my answer, so I'm still looking for the "right" way to do this. Basically, $scope.$$phase is always returning $digest, which causes the conditional to fail. Removing the conditional gives the correct result in the interface, but throws a $rootScope:inprog.
One solution I found is to add a $apply in the middle of the controller function:
$scope.updateThingy = function () {
$scope.myobj.items = [];
if (!$scope.$$phase) {
$scope.$apply();
}
$scope.myobj.items = ['Your name was updated.'];
};
Updated fiddle: http://jsfiddle.net/744Rv/
May not be the best way, but it's an answer.

JavaScript architecture - mediators, when to use them?

This is more of a general question about the structure of my JavaScript code and if I'm going in the right direction towards well structured code.
The current code I've got:
(function (myNamespace, $, undefined) {
myNamespace.className = {
init:function { } // do stuff
}
} (window.myNamespace= window.myNamespace|| {}, jQuery)));
(function (myNamespace, $, undefined) {
myNamespace.className2 = {
init:function { } // do stuff
}
} (window.myNamespace= window.myNamespace|| {}, jQuery)));
Obviously with the above code, I can use the same Namespace (as per page/site section) and call them via myNamespace.className.init() etc. I can also combine these if I want to, but I'm encapsulating classes for readability.
Now, I've been reading http://addyosmani.com/largescalejavascript/ about the concept of mediators. My secondary question is when (and if) I should be using these? From className2 obviously I can do:
myNamespace.className2 = {
init:function { myNamespace.className.init() } // do stuff
}
So why would this ever subscribe to className like mediator.subscribe("classNameInit") and publish that event in className?
I'm highly open to suggestions about the structure of my code as this is something I need to get right whilst I'm changing the way I write my JavaScript.
You would use it when you have multiple pieces which will work together in unlimited combinations where you don't know all combinations ahead of time or where it's more efficient to assume all combinations.
Let's say you were building a social media app and you wrote a class to encapsulate a list of users. On some screens, clicking on a user in the list opens their profile, on another screen perhaps clicking a user searches for every comment they left, and on a third screen something else happens.
If you were to write this not using mediator/pubsub, what you'd end up with is a bunch of if statements in the onclick event...
UserList.prototype.onUserClick = function(user) {
// Check if we're supposed to open a popup
if (this.mode === 'profile')
// Check for something else
else if (this.mode === 'something else')
// Check for another case
else if (this.mode === 'foo')
}
Mediator is a solution to this problem because it doesn't require that UserList have knowledge of every single situation it might end up in. Instead, the above code in UserList could simply be refined to broadcast when a user is clicked on...
UserList.prototype.onUserClick = function(user) {
this.publish('user-click', user);
}
Then each of your other screens or UI pieces can simply listen for the user-click message...
// On pages where there needs to be a popup profile
Mediator.onMessage('user-click', function(data) {
showProfilePopup(data);
});
// Or perhaps on a search page
SearchBox.onMessage('user-click', function(data) {
this.searchByUser(data);
});
Furthermore, where mediator begins to shine is because these other UI components, like SearchBox are not interested in specifically when UserList fires a user-click, they're interested only when a user-click is published, other UI controls on the page can fire user-click as well and these pieces can react to it.
On a side note, className = { } isn't creating a class. What you probably want is className = function() { }.

Mootools - how to destroy a class instance

What I'm trying to do is create a class that I can quickly attach to links, that will fetch and display a thumbnail preview of the document being linked to. Now, I am focusing on ease of use and portability here, I want to simply add a mouseover event to links like this:
Testing
I realize there are other ways I can go about this that would solve my issue here, and I may end up having to do that, but right now my goal is to implement this as above. I don't want to manually add a mouseout event to each link, and I don't want code anywhere other than within the class (and the mouseover event creating the class instance).
The code:
TestClass = new Class({
initialize: function(anchor) {
this.anchor = $(anchor);
if(!this.anchor) return;
if(!window.zzz) window.zzz = 0;
this.id = ++window.zzz;
this.anchor.addEvent('mouseout', function() {
// i need to get a reference to this function
this.hide();
}.bind(this));
this.show();
},
show: function() {
// TODO: cool web 2.0 stuff here!
},
hide: function() {
alert(this.id);
//this.removeEvent('mouseout', ?); // need reference to the function to remove
/*** this works, but what if there are unrelated mouseout events? and the class instance still exists! ***/
//this.anchor.removeEvents('mouseout');
//delete(this); // does not work !
//this = null; // invalid assignment!
//this = undefined; // invalid assignment!
}
});
What currently happens with the above code:
1st time out: alerts 1
2nd time out: alerts 1, 2
3rd time out: alerts 1, 2, 3
etc
Desired behavior:
1st time out: alerts 1
2nd time out: alerts 2
3rd time out: alerts 3
etc
The problem is, each time I mouse over the link, I'm creating a new class instance and appending a new mouseout event for that instance. The class instance also remains in memory indefinitely.
On mouseout I need to remove the mouseout event and destroy the class instance, so on subsequent mouseovers we are starting fresh.
I could create a helper function for this to make sure that the class is only created once for each link, something like this:
function TestClassHelper(anchor) {
anchor = $(anchor);
if(!anchor) return;
if(!anchor.retrieve('TestClass')) anchor.store('TestClass', new TestClass(anchor));
anchor.retrieve('TestClass').show();
}
Testing
I may end up implementing it this way if I have to, but I'm curious as to how I can fix the other method.
This looks a lot more complex than it should be. But if you want to fix this, you need to save a reference to the bound function somewhere and later pass that to removeEvent.
For example:
// inside initialize
this.boundHandler = function() {
this.hide();
}.bind(this)
this.anchor.addEvent('mouseout', this.boundHandler);
// inside hide
this.removeEvent('mouseout', this.boundHandler);
See the removeEvent docs for examples of this very issue.
I wouldn't recommend event delegation here either for performance reasons. The best approach is to attach the handler in code rather than inline and only do it once, so unnecessary objects are not being created each time the user mouses over.

Categories

Resources