Как то форуме sencha возник вопрос: «Кто-нибудь знает, как добавить поле ввода в форму, которое бы являлась Ace Editor’ом?». Поэтому я решил реализовать такое поле.
В данной статье я попытался реализовать новое поле формы в базе класса Ext.form.field.Base и сделать похожим на другие поля формы extjs. Поле запускает событие «change», имеет проверки и информацию, ошибки и предупреждения. Надеюсь, код скажет сам за себя.
Ext.define('AceEditorTextArea', { extend: 'Ext.form.field.Base', alias: 'widget.aceeditortextarea', fieldSubTpl: [ '<div foo="aaa" id="{aceWrapperDivId}" ', 'style="min-height: {minHeight}px; height: {height}px"', '<tpl if="fieldStyle"> style="{fieldStyle}"</tpl>', '<tpl if="fieldCls"> class="{fieldCls}"</tpl>', '>', '<div id="{aceDivId}" ', 'style="min-height: {minHeight}px; height: {height}px"', '></div>', '</div>', ], defaultAceEditorOptions: { highlightActiveLine: true, showPrintMargin: false }, aceOptions: {}, value: '', lastValue: '', minHeight: 58, allowBlank: true, blankText: "This field is required", extraFieldBodyCls: Ext.baseCSSPrefix + 'form-text-wrap-default', invalidCls: Ext.baseCSSPrefix + 'form-invalid-ace-field-default', initComponent: function () { this.on('afterrender', this.initAceEditor, this); this.callParent(); }, getAceEditor: function () { if (!this.aceEditor) { this.aceEditor = ace.edit(this.getAceDivId()); } return this.aceEditor; }, initAceEditor: function () { var me = this, aceOptions = Ext.Object.merge( this.defaultAceEditorOptions, this.aceOptions ); this.getAceEditor().setOptions(aceOptions); this.getAceEditor().getSession().setValue(this.getValue()); this.getAceEditor().getSession().setMode(this.defaultAceEditorOptions.mode); this.getAceEditor().getSession().on('change', function () { me.onChange.call(me); }); }, onChange: function () { this.lastValue = this.value; this.value = this.getAceEditor().getSession().getValue(); this.isCodeValid(); this.fireEvent('change', this, this.value, this.lastValue); this.callParent([this.value, this.lastValue]); }, isCodeValid: function () { var annotations = this.getAceEditor().getSession().getAnnotations(); if (annotations.length > 0) { this.fireEvent('annotation', annotations); } Ext.Array.each(annotations, function (annotation) { if (annotation.type == 'info') { this.fireEvent('infoannotation'); return false; } }, this); Ext.Array.each(annotations, function (annotation) { if (annotation.type == 'error') { this.fireEvent('errorannotation'); return false; } }, this); Ext.Array.each(annotations, function (annotation) { if (annotation.type == 'warning') { this.fireEvent('worningannotation'); return false; } }, this); return this; }, getValue: function () { var value = this.value; return value; }, getSubmitValue: function () { var value = this.value; return value; }, getSubTplData: function (fieldData) { fieldData.aceWrapperDivId = this.getAceWrapperDivId(); fieldData.aceDivId = this.getAceDivId(); fieldData.height = this.height || this.minHeight; fieldData.minHeight = this.minHeight; fieldData.fieldCls = this.fieldCls; return fieldData; }, getAceWrapperDivId: function () { return this.getId() + '-aceDivWrapperId'; }, getAceWrapperEl: function() { return Ext.get(this.getAceWrapperDivId()); }, getAceDivId: function () { return this.getId() + '-aceDivId'; }, getErrors: function (value) { var errors = this.callParent([value]); if (!this.allowBlank && this.value.length == 0) { errors.push(this.blankText); } if(errors && errors.length > 0) { this.getAceWrapperEl().addCls(this.invalidCls); } else { this.getAceWrapperEl().removeCls(this.invalidCls); } return errors; } });
Я также добавил тривиальные события, чтобы отключить / включить или проверить поле. Если вы будете использовать этот код и захотите добавить некоторые новые функции, эти события могут помочь вам в тестировании.
Fiddle: