Sencha Extjs - Overriding methods properly? - javascript

I have a simple overriding class. That looks like this:
Ext.define('Ext.overrides.form.Panel',{
override: 'Ext.form.Panel',
constructor: function() {
this.callParent(arguments);
},
listeners: {
afterrender: {
fn:function (component, eOpts) {
var fields = component.getForm().getFields();
var field = fields.getAt(0);
if(typeof field !== 'undefined' && field.isFocusable()) {
//need to wait before you can focus a field.
Ext.defer(function() {
field.focus(true,200);
},1);
}
}
}
}
});
As you can see, I am attempting to override the afterRender event so that the first field in the form will have focus once the form has been rendered.
This works, except for some forms in my application that are implementing some code in the afterrender events for themselves. It appears that they are overriding this method I have defined above.
I can copy this piece of code in to those forms, but wouldn't it be better if I could call the code above using something like this.callParent(arguments); in those forms?
I've actually tried that, but the problem is that my after render code for those forms is in view controller scope. So the 'this' reference will have the wrong scope of Ext.Base.

This should work:
Ext.define('Ext.overrides.form.Panel',{
override: 'Ext.form.Panel',
constructor: function() {
this.callParent(arguments);
this.on({
afterrender: function (component, eOpts) {
var fields = component.getForm().getFields();
var field = fields.getAt(0);
if(typeof field !== 'undefined' && field.isFocusable()) {
//need to wait before you can focus a field.
Ext.defer(function() {
field.focus(true,200);
},1);
}
}
});
}
});

Related

KendoUI ComboBox Change Event Runs Multiple Times

I have an MVC Control for a KendoUI ComboBox that does NOT setup the Change Event ahead of time. Upon rendering, a page controller sets-up & shims-in its' own Change Event.
Oddly, this event gets called TWICE:
When I change the Selected Item
When I click away from the control
Q: What am I doing wrong?
Q: Is this HOW we should over-write the change event on an existing Kendo ComboBox?
MVC CONTROL:
As you can see, I am NOT defining any client-side events here...
#(Html.Kendo().ComboBox()
.Name("ddlTechnician")
.Filter("contains")
.Placeholder("Select Technician...")
.DataTextField("Text")
.DataValueField("Value")
.BindTo(new List<SelectListItem>() {
new SelectListItem() { Text = "Frank", Value = "1" },
new SelectListItem() { Text = "Suzie", Value = "2" },
new SelectListItem() { Text = "Ralph", Value = "3" }
})
.Suggest(true)
.HtmlAttributes(new { style = "width:300px;" }))
PAGE CONTROLLER:
And, I am only defining the event ONCE here. I have also confirmed the event isn't already firing BEFORE setting it in the Page Controller
$(document).ready(function () {
var PageController = (function ($) {
function PageController(options) {
var that = this,
empty = {},
dictionary = {
elements: {
form: null
},
instances: {
ddlTechnician: null
},
selectors: {
form: 'form',
ddlTechnician: '#ddlTechnician'
}
};
var initialize = function (options) {
that.settings = $.extend(empty, $.isPlainObject(options) ? options : empty);
dictionary.elements.form = $(dictionary.selectors.form);
// Objects
dictionary.instances.ddlTechnician = $(dictionary.selectors.ddlTechnician, dictionary.elements.form).data('kendoComboBox');
// Events
dictionary.instances.ddlTechnician.setOptions({ change: that.on.change.kendoComboBox });
};
this.settings = null;
this.on = {
change: {
kendoComboBox: function (e) {
// This is getting called MULTIPLE TIMES
console.log('kendoComboBox RAN');
}
}
}
};
initialize(options);
}
return PageController;
})(jQuery);
var pageController = new PageController({});
});
I was able to reproduce your problem on a Kendo JQuery Combobox when I set the event handler through setOptions, which is not the recommended way after the widget has been rendered. Instead you should use the "bind" method as shown in the documentation's example for change events.
Try changing the line of code where you set your event handler to this:
dictionary.instances.ddlTechnician.bind("change", that.on.change.kendoComboBox);
Here's a dojo that shows the difference: http://dojo.telerik.com/iyEQe
Hope this helps.

Render Component into summary row

How can I efficiently render a component into the grid's summary row?
The summaryRenderer only returns the raw value, which is then put into the template. So this is my summary renderer:
summaryRenderer:function(summaryValue, values, dataIndex) {
return summaryValue + '<br><div id="btn-' + dataIndex + '">';
}
And somehow I have to insert a component after the renderer is through. I have tried to do it in store.load callback, but the renderer is done only after the load.
me.getStore().load({
callback:function(records, operation, success) {
Ext.each(me.getColumns(),function(column) {
Ext.create('Ext.Button',{
text:'Use this column',
handler:function() {
me.createEntryFromColumn(column);
}
}).render('btn-'+column.dataIndex);
// throws "Uncaught TypeError: Cannot read property 'dom' of null",
// because the div is not yet in the dom.
});
}
});
Which event can I attach to, that is fired only after the summaryRenderer is through?
Maybe you can do it this way:
init: function () {
this.control({
'myGrid': {
afterrender: this.doStuff
}
});
},
doStuff: function (myGrid) {
var self = this;
myGrid.getStore().on({
load: function (store, records) {
// do stuff
}
});
myGrid.getStore().load();
}
I suggest to check at : Component Template (http://skirtlesden.com/ux/ctemplate) for the component rendering.
I did not check myself, but just looking in the code: https://docs.sencha.com/extjs/6.0/6.0.1-classic/source/Column2.html#Ext-grid-column-Column-method-initComponent (ExtJs 6.0.1), you can see that the summary is render via a function.
See in initComponent
me.setupRenderer('summary');
Then in setupRenderer
// Set up the correct property: 'renderer', 'editRenderer', or 'summaryRenderer'
me[me.rendererNames[type]] = me.bindFormatter(format);
And bindFormatter produce a function.
So I suppose, you can extend column, and extend initComponent, after the this.callParent(), you can override me[me.rendererNames[type]] and put your own function which take v (the value) in parameter and which return the product of a CTemplate.
Something like
me["summaryRenderer"] = function(v) { return ctpl.apply({component:c, value:v})}
I think #CD.. suggestion to set a fiddle is a good idea.

When on enter keypress is triggered by jquery my model is undefined

I'm using require.js with backbone.js to structure my app. In one of my views:
define(['backbone', 'models/message', 'text!templates/message-send.html'], function (Backbone, Message, messageSendTemplate) {
var MessageSendView = Backbone.View.extend({
el: $('#send-message'),
template: _.template(messageSendTemplate),
events: {
"click #send": "sendMessage",
"keypress #field": "sendMessageOnEnter",
},
initialize: function () {
_.bindAll(this,'render', 'sendMessage', 'sendMessageOnEnter');
this.render();
},
render: function () {
this.$el.html(this.template);
this.delegateEvents();
return this;
},
sendMessage: function () {
var Message = Message.extend({
noIoBind: true
});
var attrs = {
message: this.$('#field').val(),
username: this.$('#username').text()
};
var message = new Message(attrs);
message.save();
/*
socket.emit('message:create', {
message: this.$('#field').val(),
username: this.$('#username').text()
});
*/
this.$('#field').val("");
},
sendMessageOnEnter: function (e) {
if (e.keyCode == 13) {
this.sendMessage();
}
}
});
return MessageSendView;
});
When keypress event is triggered by jquery and sendMessage function is called - for some reason Message model is undefined, although when this view is first loaded by require.js it is available. Any hints?
Thanks
Please see my inline comments:
sendMessage: function () {
// first you declare a Message object, default to undefined
// then you refrence to a Message variable from the function scope, which will in turn reference to your Message variable defined in step 1
// then you call extend method of this referenced Message variable which is currently undefined, so you see the point
var Message = Message.extend({
noIoBind: true
});
// to correct, you can rename Message to other name, e.g.
var MessageNoIOBind = Message.extend ...
...
},
My guess is that you've bound sendMessageOnEnter as a keypress event handler somewhere else in your code. By doing this, you will change the context of this upon the bound event handler's function being called. Basically, when you call this.sendMessage(), this is no longer your MessageSendView object, it's more than likely the jQuery element you've bound the keypress event to. Since you're using jQuery, you could more than likely solve this by using $.proxy to bind your sendMessageOnEnter function to the correct context. Something like: (note - this was not tested at all)
var view = new MessageSendView();
$('input').keypress(function() {
$.proxy(view.sendMessageOnEnter, view);
});
I hope this helps, here is a bit more reading for you. Happy coding!
Binding Scopes in JavaScript
$.proxy

Access values after form-pre-filling (with Ember.js)

I have one form for saving and editing records. On clicking on a record, the form should be filled with the data. After filling, I want to do some UI actions (call jQuery Plugin etc.).
The pre-filling works, but when I'm trying to access the values, it works only at the second click. On the first click, the values are empty or the ones from the record clicked before.
This action is stored in the controller:
edit: function(id) {
var _this = this;
// prefill form for editing
var customer = this.store.find('customer', id).then(function(data) {
_this.set('name',data.get('name'));
_this.set('number',data.get('number'));
_this.set('initial',data.get('initial'));
_this.set('description',data.get('description'));
_this.set('archived',data.get('archived'));
// store user for save action
_this.set('editedRecordID',id);
_this.set('isEditing',true);
$('input[type="text"]').each(function() {
console.log(this.value)
});
});
},
I need a generic way to check if the input field is empty, because I want to include this nice UI effect: http://codepen.io/aaronbarker/pen/tIprm
Update
I tried to implement this in a View, but now I get always the values from the record clicked before and not from the current clicked element:
View
Docket.OrganizationCustomersView = Ember.View.extend({
didInsertElement: function() {
$('input[type="text"]').each(function() {
console.log(this.value)
});
}.observes('controller.editedRecordID')
});
Controller
Docket.OrganizationCustomersController = Ember.ArrayController.extend({
/* ... */
isEditing: false,
editedRecordID: null,
actions: {
/* ... */
edit: function(id) {
var _this = this;
// prefill form for editing
var customer = this.store.find('customer', id).then(function(data) {
_this.set('name',data.get('name'));
_this.set('number',data.get('number'));
_this.set('initial',data.get('initial'));
_this.set('description',data.get('description'));
_this.set('archived',data.get('archived'));
// store user for save action
_this.set('editedRecordID',id);
_this.set('isEditing',true);
});
},
/* ... */
});
Update 2
OK, I think I misunderstood some things.
At first, my expected console output should be:
1.
2.
3.
but is:
1.
3.
2.
Secondly: I can use any name, even foobar, for the observed method in my view. Why?
Controller
edit: function(id) {
var _this = this;
// prefill form for editing
var customer = this.store.find('customer', id).then(function(data) {
_this.set('name',data.get('name'));
_this.set('number',data.get('number'));
_this.set('initial',data.get('initial'));
_this.set('description',data.get('description'));
_this.set('archived',data.get('archived'));
console.log('1.')
// store user for save action
_this.set('editedRecordID',id);
_this.set('isEditing',true);
console.log('2.')
});
},
View
Docket.OrganizationCustomersView = Ember.View.extend({
foobar: function() {
console.log('3.')
$('input[type="text"]').each(function() {
console.log(this.value)
});
}.observes('controller.editedRecordID')
});
Update 3
I think I "figured it out" (but I don't know why):
Docket.OrganizationCustomersView = Ember.View.extend({
movePlaceholder: function() {
$('input[type="text"], textarea').bind("checkval",function() {
var $obj = $(this);
setTimeout(function(){
console.log($obj.val());
},0);
}.observes('controller.editedRecordID')
});
setTimeout(function(){ ... }, 0); does the trick. But why?!
You can convert use that jquery code in a component, this is the best way to create a reusable view, without putting ui logic in controllers, routers etc.
Template
<script type="text/x-handlebars" data-template-name="components/float-label">
<div class="field--wrapper">
<label >{{title}}</label>
{{input type="text" placeholder=placeholder value=value}}
</div>
</script>
FloatLabelComponent
App.FloatLabelComponent = Ember.Component.extend({
onClass: 'on',
showClass: 'show',
checkval: function() {
var label = this.label();
if(this.value !== ""){
label.addClass(this.showClass);
} else {
label.removeClass(this.showClass);
}
},
label: function() {
return this.$('input').prev("label");
},
keyUp: function() {
this.checkval();
},
focusIn: function() {
this.label().addClass(this.onClass);
},
focusOut: function() {
this.label().removeClass(this.onClass);
}
});
Give a look in that jsbin http://emberjs.jsbin.com/ILuveKIv/3/edit

Implementation knockout custom binding with afterRender functionality

I'm working with a list of images. The images are loaded dynamically; the list of references is stored in observableArray.
After a full load of the image list I want to connect handlers of DOM-elements. My implementation at the moment:
in View:
<div class="carousel_container" data-bind="template: { 'name': 'photoTemplate', 'foreach': ImageInfos, 'afterRender': renderCarousel }">
<script type="text/html" id="photoTemplate">
//...content of template
</script>
in ViewModel:
self.counterCarousel = 0;
self.renderCarousel = function (elements) {
var allImagesCount = self.ImageInfos().length;
self.counterCarousel++;
if (self.counterCarousel >= allImagesCount) {
self.counterCarousel = 0;
// ... add handlers here
}
}
This is a very ugly approach. In addition, user can add / delete images, so after each addition or removal is required remove all handlers and connect it again. How can I organize a custom binding to handle this scenario?
I don't see why this approach would not work -
ko.utils.arrayForEach(ImageInfos(), function (image) {
// ... add handlers here
});
Or better yet, bind an event to each item with a class of 'image-info' so that you don't have to redo the bindings when items are added or changed -
var afterRender = function (view) {
bindEventToImages(view, '.image-info', doSomething);
};
var bindEventToImages= function (rootSelector, selector, callback, eventName) {
var eName = eventName || 'click';
$(rootSelector).on(eName, selector, function () {
var selectedImage = ko.dataFor(this);
callback(selectedImage);
return false;
});
};
function doSomething(sender) {
alert(sender);
// handlers go here
}
This binds an event to every class 'image-info' and on-click handles the calling element, executing doSomething.

Categories

Resources