I am trying to develop a combobox that allows the user to enter value which get lookuped and if it is missing in the list it get viualize as a new value (I'd thought about a image left to the field) and in additon causes the field to be bound to another property of the model where it is assigned to.
In addition the user should be able to clear the combo value.
I started by using a provided class from this guy and extended it with the required settings. I managed to prevent a issue which causes the 'x' trigger to hide continious, but I couldn't manage the difference between userinput - selection - set - clear (set value by form)
Here is a snippet from what I've got so far
Ext.define('Ext.ux.form.field.InputCombo', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.inputcombo',
trigger2Cls: 'x-form-clear-trigger',
newRecordFieldname: null, // the name if this value is new
originName: null,
initComponent: function () {
var me = this;
me.addEvents(
/**
* #event beforeclear
* #param {Combo} Combo The combo that triggered the event
*/
'beforeclear',
/**
* #event beforeclear
* #param {Combo} Combo The combo that triggered the event
*/
'clear'
);
me.callParent(arguments);
me.on('specialkey', this.onSpecialKeyDown, me);
me.on('afterrender', function () {
me.onShowClearTrigger(false);
}, me);
this.on('change', function() {
this.createdRecord(this);
});
this.on('select', function() {
this.onSetExisting(this);
});
},
createdRecord: function(ref) {
ref.newRecord = true;
ref.selectedRecord = false;
ref.originName = ref.name;
ref.name = ref.newRecordFieldname;
ref.style = 'background:url(../resources/themes/images/default/dd/drop-add.gif) no-repeat left center;'; // does not work
},
setExisting: function(ref) {
ref.newRecord = false;
ref.selectedRecord = true;
ref.style = '';
ref.name = ref.originName;
},
/**
* #private onSpecialKeyDown
* eventhandler for special keys
*/
onSpecialKeyDown: function (obj, e, opt) {
if ( e.getKey() == e.ESC ) {
this.clear();
}
},
onShowClearTrigger: function (show) {
var me = this;
if (show) {
me.triggerEl.each(function (el, c, i) {
if (i === 1) {
el.setWidth(el.originWidth, false);
el.setVisible(true);
me.active = true;
}
});
} else {
me.triggerEl.each(function (el, c, i) {
if (i === 1) {
el.originWidth = el.getWidth();
if (el.originWidth !== 0) { // prevent double hide
el.setWidth(0, false);
el.setVisible(false);
me.active = false;
}
}
});
}
me.updateLayout();
},
/* lines remoced. see linked answer */
});
What I haven't get managed to get working (and the point where I need help) is to show a image for a new record and that the field behaves in the same manner regardless if I first enter something and clear or set a value, select a value and so on. By know the field behaves quiete different or does not work at all.
In additon it should be compatible to the upcoming 4.2 release. Dunno if this important to mention.
Any help appreciated!
Related
I'm looking for a method which makes a field invisible on js (i'm making a custom widget 'InvisibleIfEmptry').
I tried to override _check_visibility method when extending FormWidget.AbstractField class :
var core = require('web.core'),
form_common = require('web.form_common');
var InvisibleIfEmpty = form_common.AbstractField.extend({
start: function() {
this.on("change:effective_readonly", this, function() {
this._toggle_label();
this._check_visibility();
});
this.render_value();
this._toggle_label();
},
_check_visibility: function() {
if (this.get("effective_readonly"))
this.$el.toggleClass('o_form_invisible',true);
}
this.$el.toggleClass('o_form_invisible',false);
}
}, .....
but this makes invisible only the field's value, not the label.
my guess is to alter some of field_manager's values but i can't figure out which one ?
Thank you for your help :)
Here is my JS code for doing that :
odoo.define('myCustomModule', function(require)
{
'use strict';
var core = require('web.core'),
form_common = require('web.form_common'),
form_view = require('web.FormView');
form_common.AbstractField.include({
start: function() {
this._super();
// Check visibility logic below when content
// changes or the form swich to view mode
this.field_manager.on("view_content_has_changed", this, function() {
this._check_visibility();
});
this.on("change:effective_readonly", this, function() {
this._toggle_label();
this._check_visibility();
});
},
_check_visibility: function() {
// If the form is in view mode and the field is empty,
// make the field invisible
window.alert(this.);
if (this.field_manager.get("actual_mode") === "view" ) {
if(this.get("value") == false){
this.$el.toggleClass('o_form_invisible',true);
this.$label.toggleClass('o_form_invisible',true);
}else{
this.$el.toggleClass('o_form_invisible',this.get("effective_invisible"));
this.$label.toggleClass('o_form_invisible',this.get("effective_invisible"));
}
}else{
this.$el.toggleClass('o_form_invisible',this.get("effective_invisible"));
this.$label.toggleClass('o_form_invisible',this.get("effective_invisible"));
}
},
});
});
But this applies to all my modules.
Does Any one know how to get module/model name from AbstractField ?
I'm in a bit of a predicament, because I need to somehow update event listeners, when the page changes using ajax. I have a specific element that I need to update based on what page the ajax call injects. Here's my issue:
I have this slider control constructor:
UI.CONTROLS.SLIDER = function (select, action, actionWhenActive, actionWhenSet) {
'use strict';
var self = this;
this.action = action;
this.select = select;
this.area = select.find($('area-'));
this.fill = select.find($('fill-'));
this.value = 0;
this.active = false;
this.actionWhenActive = actionWhenActive;
this.actionWhenSet = actionWhenSet;
function eventlisteners(self) {
$(document).on('mousemove', function (event) {
self.move(event, self);
});
$(document).on('mouseup', function (event) {
self.drop(event, self);
});
self.area.on('mousedown', function (event) {
self.grab(event, self);
});
}
eventlisteners(self);
this.reselect = function (element) {
self.area = element.find($('area-'));
self.fill = element.find($('fill-'));
eventlisteners(self);
};
};
UI.CONTROLS.SLIDER.prototype = {
action: this.action,
width: function () {
'use strict';
var calcWidth = ((this.value * 100) + '%');
this.fill.width(calcWidth);
},
update: function (event, self) {
'use strict';
if (this.actionWhenActive === true) {
this.action();
}
var direction, percent, container, area;
direction = event.pageX - this.area.offset().left;
percent = Math.min(Math.max(direction / this.area.width(), 0), 1.0);
this.value = percent;
this.width();
},
move: function (event, self) {
'use strict';
if (this.active === true) {
this.update(event);
}
},
grab: function (event, self) {
'use strict';
this.active = true;
self.update(event);
event.preventDefault();
},
drop: function (event, self) {
'use strict';
if (this.active === true) {
this.active = false;
this.action();
}
},
setValue: function (value) {
'use strict';
if (this.active === false) {
this.value = value;
this.width();
if (this.actionWhenSet === true) {
this.action();
}
}
}
};
This can create new sliders based on the container (select) specified. In my website, I have an audio player. So using ajax you can navigate while this audio player plays. I have two states, viewing a track, and not viewing a track. When you're not viewing the track that is playing, a transport bar will pop down from the header containing the scrubber (slider control), this scrubber is also inside the track view (viewing the track) page.
This code checks if you're viewing the track that is playing. audioFromView gets updated on the ajax calls, it basically replaces it with what track you're viewing. It then compares it with audioCurrent which is the track currently playing, UI.PAGE.TYPE is what type of page you're viewing, in this instance a track:
var audioViewIsCurrent = function () {
'use strict';
if (audioCurrent.src === audioFromView.src && UI.PAGE.TYPE === 'track') {
return true;
} else {
return false;
}
};
So this code then updates the scrubber based on the above code's output (audioElementTrackView is the scrubber inside the track page, and audioElementTransport is the scrubber inside the transport panel):
var audioScrubber = {
element: {},
active: false,
action: function () {
'use strict';
audioScrubber.active = this.active;
var time = audioSource.duration * this.value;
if (this.active === false) {
audioCurrent.time(this.value * duration);
}
},
set: function () {
'use strict';
var container, slider;
if (audioElementTrackView.length === 1) {
container = audioElementTrackView.find(audioElementScrubber);
} else {
container = audioElementTransport.find(audioElementScrubber);
}
this.element = new UI.CONTROLS.SLIDER(container, this.action, true);
},
reselect: function () {
if (audioElementTrackView.length === 1) {
container = audioElementTrackView.find(audioElementScrubber);
} else {
container = audioElementTransport.find(audioElementScrubber);
}
this.element.reselect(container)
}
};
audioScrubber.reselect()
So this works fine with how I'm currently doing it, HOWEVER since I am adding new event listeners everytime I update my scrubber object (inside the ajax call) in order to keep the scrubber working I am also piling them up, making the old event listeners take up space and RAM eventually making the site slow down to a halt (if you navigate enough)
I tested this using console.log on mouseup, everytime I switched page, it would log it twice, and then thrice and so on.
How can I avoid this?
You can delegate events to the nearest static ancestor, so you don't need to rebind them everyime.
Read this article
https://davidwalsh.name/event-delegate
I am using Opnelayers control for selection of feature
OpenLayers.Control.SelectFeature
when I triggered the single click event then feature get selected and its working fine for me. Now I want to use double click event for another operation.
I have get the idea from this link feature to have both a single click and double click event?
I have used the same code and both click events working fine but I cannot get the feature on which click event performed. This is the code
handler = new OpenLayers.Handler.Click(
select,
{
click: function(evt)
{
var feature = this.layer.getFeatureFromEvent(evt);
console.log(feature); // output null
if(this.layer.selectedFeatures){
this.unselect(this.layer.selectedFeatures[0]);
}
},
dblclick: function(evt)
{
// some other operation
}
},
{
single: true,
double: true,
stopDouble: true,
stopSingle: true
}
);
handler.activate();
Any idea which thing is missing in this code?
Thank you
For The Above Question this might solve your answer with few changes
var handler = new OpenLayers.Handler.Click(
layerName, {
click: function(evt) {
console.log('click has triggered');
var feature = layerName.getFeatureFromEvent(evt);
}
,dblclick: function(evt) {
console.log('dblclick has triggered');
}
},{
single: true
,double: true
,stopSingle: true
,stopDouble: true
}
);
handler.activate();
I have been in a similar situation and used the getClosestFeatureToCoordinate method of the layer source. The code would be something like:
click: function(evt) {
var coord = evt.coordinate;
var source = this.layer.getSource();
var feature = source.getClosestFeatureToCoordinate(coord);
// do something with feature
}
I have done in this way.
click: function(evt) {
var pos = map.getLonLatFromPixel(xy);
var point = new OpenLayers.Geometry.Point(pos.lon, pos.lat);
var list = $.map(this.layer.features, function(v) {
return v.geometry.distanceTo(point);
}); // get distance of all features
var min = (Math.min.apply(null, list)); // minimum distance
var closest = $.map(this.layer.features, function(v) {
if (v.geometry.distanceTo(point) == min)
return v;
});
feature = closest[0];
}
we not like to add three word again in selected item.
we like to validate on drag and drop event and need to show message that u already added this record.
i try with below code but not able to fine relevant event for validate
listeners: {
added:function(obj,event){
console.log("added");
},change:function(obj,event){
console.log("change");
},removed:function(obj,event){
console.log("removed");
}, blur:function(obj,event){
console.log("blur");
}, click: function( obj) {
console.log('click');
}, select: function( obj) {
console.log('select');
}
}
please see attached image bellow.
I am Using Extjs 3.4
The change event fires when an item is selected or deselected... But at this stage, you won't be able to prevent it anymore. So, apparently, your best move is to override the onAddBtnClick method:
{
xtype: 'itemselector'
// ... config
,onAddBtnClick: function() {
var me = this,
selected = me.getSelections(me.fromField.boundList),
i, l, record;
var toStore = this.toField.boundList.getStore(),
idField = 'value', // or 'id', or whatever you want
selectedIds = Ext.pluck(Ext.pluck(toStore.getRange(), 'data'), idField),
accepted = [], rejected = [];
for (i=0, l=selected.length; i<l; i++) {
record = selected[i];
if (selectedIds.indexOf(record.get(idField)) === -1) {
accepted.push(record);
} else {
rejected.push(record);
}
}
if (rejected.length) {
// warning msg
}
me.moveRec(true, accepted);
me.toField.boundList.getSelectionModel().select(accepted);
}
}
How do I dynamically update the items in a drop down?
I have a custom plugin for CKEditor that populates a drop down menu with a list of items which I can inject into my textarea.
This list of items comes from a Javascript array called maptags, which is updated dynamically for each page.
var maptags = []
This list of tags gets added to the drop down when you first click on it by the init: function. My problem is what if the items in that array change as the client changes things on the page, how can I reload that list to the updated array?
Here is my CKEditor Plugin code:
CKEDITOR.plugins.add('mapitems', {
requires: ['richcombo'], //, 'styles' ],
init: function (editor) {
var config = editor.config,
lang = editor.lang.format;
editor.ui.addRichCombo('mapitems',
{
label: "Map Items",
title: "Map Items",
voiceLabel: "Map Items",
className: 'cke_format',
multiSelect: false,
panel:
{
css: [config.contentsCss, CKEDITOR.getUrl(editor.skinPath + 'editor.css')],
voiceLabel: lang.panelVoiceLabel
},
init: function () {
this.startGroup("Map Items");
//this.add('value', 'drop_text', 'drop_label');
for (var this_tag in maptags) {
this.add(maptags[this_tag][0], maptags[this_tag][1], maptags[this_tag][2]);
}
},
onClick: function (value) {
editor.focus();
editor.fire('saveSnapshot');
editor.insertHtml(value);
editor.fire('saveSnapshot');
}
});
}
});
I think I just solved this actually.
Change your init like this:
init: function () {
var rebuildList = CKEDITOR.tools.bind(buildList, this);
rebuildList();
$(editor).bind('rebuildList', rebuildList);
},
And define the buildList function outside that scope.
var buildListHasRunOnce = 0;
var buildList = function () {
if (buildListHasRunOnce) {
// Remove the old unordered list from the dom.
// This is just to cleanup the old list within the iframe
$(this._.panel._.iframe.$).contents().find("ul").remove();
// reset list
this._.items = {};
this._.list._.items = {};
}
for (var i in yourListOfItems) {
var item = yourListOfItems[i];
// do your add calls
this.add(item.id, 'something here as html', item.text);
}
if (buildListHasRunOnce) {
// Force CKEditor to commit the html it generates through this.add
this._.committed = 0; // We have to set to false in order to trigger a complete commit()
this.commit();
}
buildListHasRunOnce = 1;
};
The clever thing about the CKEDITOR.tools.bind function is that we supply "this" when we bind it, so whenever the rebuildList is triggered, this refer to the richcombo object itself which I was not able to get any other way.
Hope this helps, it works fine for me!
ElChe
I could not find any helpful documenatation around richcombo, i took a look to the source code and got an idea of the events i needed.
#El Che solution helped me to get through this issue but i had another approach to the problem because i had a more complex combobox structure (search,groups)
var _this = this;
populateCombo.call(_this, data);
function populateCombo(data) {
/* I have a search workaround added here */
this.startGroup('Default'); /* create default group */
/* add items with your logic */
for (var i = 0; i < data.length; i++) {
var dataitem = data[i];
this.add(dataitem.name, dataitem.description, dataitem.name);
}
/* other groups .... */
}
var buildListHasRunOnce = 0;
/* triggered when combo is shown */
editor.on("panelShow", function(){
if (buildListHasRunOnce) {
// reset list
populateCombo.call(_this, data);
}
buildListHasRunOnce = 1;
});
/* triggered when combo is hidden */
editor.on("panelHide", function(){
$(_this._.list.element.$).empty();
_this._.items = {};
_this._.list._.items = {};
});
NOTE
All above code is inside addRichCombo init callback
I remove combobox content on "panelHide" event
I repopulate combobox on "panelShow" event
Hope this helps