How to enable entry from the checkbox DLGIdentifier? - checkbox

In the previous question's answer, I showed how to enable the entry while changing checkbox state. This time, I want to create multiple entries which are controlled by the corresponding checkbox.
For each checkbox I used DLGIdentifier to label it, then used DLGEnabled(self.LookupElement("entryId"), self.LookupElement("cb_ID").DLGGetValue()) to enable/disable it, but it fails Q.Q Does Anyone know how to solve it? Thank you in advance.
Here is my code.
class TestUI : UIFrame{
number true, false, n_items
TestUI(object self){
true=1; false=0;
result("TestUI ["+self.ScriptObjectGetID()+"] constructed\n")
};
~TestUI(object self){
result("TestUI ["+self.ScriptObjectGetID()+"] destructed\n")
};
void SelectAction(object self, TagGroup tgItem){
for(number i=0;i<n_items;i++){
// change state, but it failed.
DLGEnabled(self.LookupElement("num2_"+(i+1)),self.LookupElement("cb_"+(i+1)).DLGGetValue())
// the values of each checkbox are retured correctly.
result(i+":"+self.LookupElement("cb_"+(i+1)).DLGGetValue()+"\n")
};
};
TagGroup InputGenerator(object self, number i){
// Entry template
TagGroup tgBox = DLGCreateBox("Box_"+(i+1))
TagGroup tgLable = DLGCreateLabel("#")
// each elements are using DLGIdentifier to label it
TagGroup tgNumber1 = DLGCreateRealField(10,5,0).DLGIdentifier("num1_"+(i+1))
TagGroup tgNumber2 = DLGCreateRealField(10,10,0).DLGIdentifier("num2_"+(i+1))
TagGroup tgGroup = DLGGroupItems(tgLable,tgNumber1,tgNumber2).DLGTableLayout(3,1,0)
tgBox.DLGAddElement(tgGroup)
TagGroup cb = DLGCreateCheckBox("IsCheck", false,"SelectAction").DLGIdentifier("cb_"+(i+1))
tgBox.DLGAddElement(cb)
DLGEnabled(tgNumber2,cb.DLGGetValue())
return tgBox
}
void CreateDialog(object self){
TagGroup main = DLGCreateDialog("Test")
n_items = 3
for (number j=0; j<n_items; j++){
//create multiple entries by for loop
TagGroup item=self.InputGenerator(j)
main.DLGAddElement(item)
};
self.init(main).Display("TestUI")
};
};
{
alloc(TestUI).CreateDialog()
};

You may want to replace your SelectAction method with the below:
void SelectAction(object self, TagGroup tgItem){
// tgItem is the taggroup of the element that fired the action
// so no for-loop for checking all is needed here.
// tgItem.TagGroupOpenBrowserWindow("",0)
number checked = tgItem.DLGGetValue()
string IDcheck
if (!tgItem.DLGGetIdentifier(IDcheck)) return
string IDField = "num2_" + right( IDcheck, len(IDcheck)-3 )
// The DLGEnable() command only changes the taggroup of the dialog
// but doesn't do anything with the dialog window itself.
// There is a separate command to enable/disable an element of the dialog.
// void SetElementIsEnabled( ScriptObject dialogUI, String item_identifier, Boolean is_enabled )
self.SetElementIsEnabled(IDField ,checked)
return
};
In essence, you need different commands to change the displayed dialog. It is documented, but a bit hard to find:

Related

Is there a better approach to implementing Google Sheet's like cell functionality?

I'm building out a system in React that has tabular data with cells. Those cells are editable via contentEditable divs. It's functionally similar to google sheets. I'm working on the functionality where single click on the cell allows the user to override the current value of the cell and double clicking allows them to edit the value.
The functionality involved is basically this:
When single click on cell override the current value. (No cursor visible?)
When double click on cell allow the user to edit the current value. (Cursor visible, can move left and right of chars with arrowKeys)
When double clicked into the cell reformat value (removes trailing zero's for cents: 8.50 becomes 8.5)
When double clicked start the caret position at the end of the input.
When user clicks out of the cells reformat the current value to its appropriate format (example is a price cell)
My cell component looks like this:
(Note* useDoubleClick() is a custom hook I wrote that works perfectly fine and will call single/double click action accordingly)
export default function Cell({ name, value, updateItem }) {
const [value, setValue] = useState(props.value), // This stays uncontrolled to prevent the caret jumps with content editable.
[isInputMode, setIsInputMode] = useState(false),
cellRef = useRef(null);
// Handle single click. Basically does nothing right now.
const singleClickAction = () => {
if(isInputMode)
return;
}
// Handle double click.
const doubleClickAction = () => {
// If already input mode, do nothing.
if(isInputMode) {
return;
}
setIsInputMode(true);
setCaretPosition(); // Crashing page sometimes [see error below]
reformatValue();
}
// It's now input mode, set the caret position to the length of the cell's innerText.
const setCaretPosition = () => {
var range = document.createRange(),
select = window.getSelection();
range.setStart(cellRef.current.childNodes[0], cellRef.current.innerText.length);
range.collapse(true);
selectObject.removeAllRanges();
selectObject.addRange(range);
}
// Reformat innerText value to remove trailing zero's from cents.
const reformatValue = () => {
var updatedValue = parseFloat(value);
setValue(updatedValue);
}
const onClick = useDoubleClick(singleClickAction, doubleClickAction);
/*
* Handle input change. Pass innerText value to global update function.
* Because we are using contentEditable and render "" if !isInputMode
* we have override functionality.
*/
const onInput = (e) => {
props.updateItem(props.name, e.target.innerText);
}
// When cell is blurred, reset isInputMode
const onBlur = () => {
setIsInputMode(false);
cellRef.current.innerText = ""; // Without this single click will not override again after blur.
}
return (
<div
data-placeholder={value} // to view current value while isInputMode is false
class="cell-input"
contentEditable="true"
onClick={onClick}
onInput={onInput}
onBlur={onBlur}
ref={cellRef}
>
{isInputMode ? value : ""}
</div>
)
}
And here is some css so that the user can see the current value while isInputMode is false:
.cell-input {
:empty:before {
content: attr(data-placeholder);
}
:empty:focus:before {
content: attr(data-placeholder);
}
}
Now here are the issues I'm running into.
When I call the setCaretPosition function, there are no childNodes because I'm rendering the empty value ("") and crashes the page sometimes with the error- TypeError: Argument 1 ('node') to Range.setStart must be an instance of Node.
I have a $ inside cells that contain a price and I was setting that in the css with ::before and content: '$', but now I can't because of the data-placeholder snippet.
When double clicking into cell the cursor is not visible at all. If you click the arrows to move between characters it then becomes visible.
This solution has me pretty close to my desired output so I feel pretty good about it, but I think there might be a better way to go about it or a few tweaks within my solution that will be a general improvement. Would love to hear some ideas.

mousemove event, access to this context

Fisrt of all I'm using D3js inside a React component so I use some variable of my class to save datas e.g.: this.graphicalId = 'test';
I have two items in my d3 element, svgViewport which is a g element and streams which are path elements. I have .on('mousemove' event handle for each.
In the streams event I would like save the name of the current stream using d3.select(this) (note I'm in a function() and not an arrow function so this is local) in a global variable in order to use it in the svgViewport event.
My problem is that like I'm in a function() this is local and not link to my class instance so I can't save the value in a member variable this.currentStreamName.
A bit of code :
svgViewport.on('mousemove', function (d, i) {
if (mouseIsOverStream) {
let mousex = d3.mouse(this);
mousex = mousex[0];
// here I want this of the class instance context
this.nearestTickPosition, this.currentStreamName = findNearestTickPosition(mousex);
}
});
Do you have some advices to deal with it ?
Thanks.
You can use arrow functions to get access to the instance's this context and still acquire the current DOM element. For the DOM element you resort to the little-known and often overlooked third parameter of the event listener. As the docs have it (emphasis mine):
the specified listener will be evaluated for the element, being passed the current datum (d), the current index (i), and the current group (nodes)
Since the current index i is the pointer into the current group nodes you can refer to the current DOM element as nodes[i].
Your code thus becomes:
svgViewport.on('mousemove', (d, i, nodes) => {
if (mouseIsOverStream) {
let mousex = d3.mouse(nodes[i]); // get the current element as nodes[i]
mousex = mousex[0];
// this now refers to your instance
this.nearestTickPosition, this.currentStreamName = findNearestTickPosition(mousex);
}
});
Store the class context in a variable out of the event binding. Then, make an IIFE and bind it's context to the stored one.
componentDidMount() {
const ctx = this;
svgViewport.on('mousemove', function (d, i) {
if (mouseIsOverStream) {
let mousex = d3.mouse(this);
mousex = mousex[0];
!function () {
// here I want this of the class instance context
this.nearestTickPosition, this.currentStreamName = findNearestTickPosition(mousex);
}.bind(ctx)();
}
});
}
Also, this should work too:
componentDidMount() {
svgViewport = ...;
svgViewport.on('mousemove', (d, i) => {
if (mouseIsOverStream) {
let mousex = d3.mouse(svgViewport); // here
mousex = mousex[0];
this.nearestTickPosition, this.currentStreamName = findNearestTickPosition(mousex);
}
});
}

How to add legends in Amserial charts

I am using Amcharts in my AngularJS Application to create a simple bar chart.The following is my code in the controller:
let empChart;
let empBarGraph;
let empLine;
const writeemp = data => {
const {
total,
employees,
} = data;
empChart.dataProvider = e;
empChart.write('emp');
empChart.validateData();
};
AmCharts.handleLoad();
var configChart = function () {
empChart = new AmCharts.AmSerialChart();
empChart.categoryField = "state";
empChart.labelRotation = 90;
var yAxis = new AmCharts.ValueAxis();
yAxis.position = "left";
empChart.addValueAxis(yAxis);
empBarGraph = new AmCharts.AmGraph();
empBarGraph.valueField = "count";
empBarGraph.type = "column";
empBarGraph.fillAlphas = 1;
empBarGraph.lineColor = "#f0ab00";
empBarGraph.valueAxis = yAxis;
empChart.addGraph(empBarGraph);
empChart.write('empChart');
$http.get(hostNameService.getHostName()+"/dashboard/employees/statecount")
.then(response => writeemp(response.data));
}
Code in html:
<div class='panel-body'>
<div id="empChart"></div>
</div>
This would return me the values of State on x-axis and count on y-axis. I wanted to filter the chart based on the value of state and was not sure how to create the legends for this chart. could anyone suggest me on how to use legends. I want to create legends for the state value that is being returned.
You can add a legend using the OO-based syntax by creating a legend object through new AmCharts.AmLegend() and adding it to the class by calling the chart's addLegend method:
var legend = new AmCharts.AmLegend();
empChart.addLegend(legend);
If you want the legend to show values upon hovering over a column, you need to add a ChartCursor to your chart:
var cursor = new AmCharts.ChartCursor();
empChart.addChartCursor(cursor);
You can change what the legend displays upon column rollover by setting the valueText property. It allows for the same [shortcodes] used in fields like balloonText and labelText, e.g. legend.valueText = "[[category]]: [[value]]". You can also use set its valueFunction if you need to customize the text it returns dynamically like in your previous questions. All of the properties available in the legend object can be found in the AmLegend API documentation.
Updated:
Legends work off of graph objects only, so there isn't an out of the box method that allows you to represent each column as a legend item that toggles the other columns' visibility unless you're willing to reorganize your dataset and use different graph objects for each state. A workaround for this is to use the the legend's custom data array and add some event handling so that clicking on the custom data items adds/removes a toggle by unsetting your count valueField in the dataProvider.
The following annotated code accomplishes what you're trying to do:
//create the legend but disable it until the dataProvider is populated,
//since you're retrieving your data using AJAX
var legend = new AmCharts.AmLegend();
legend.enabled = false;
chart.addLegend(legend);
chart.toggleLegend = false;
// Callback that handles clicks on the custom data entry markers and labels
var handleLegendClick = function(legendEvent) {
//Set a custom flag so that the dataUpdated event doesn't fire infinitely
legendEvent.chart.toggleLegend = true;
// The following toggles the markers on and off.
// The only way to "hide" a column is to unset the valueField at the data index,
// so a temporary "storedCount" property is added to the dataProvider that stores the
// original value so that the value can be restored when the legend marker is toggled
// back on
if (undefined !== legendEvent.dataItem.hidden && legendEvent.dataItem.hidden) {
legendEvent.dataItem.hidden = false;
legendEvent.chart.dataProvider[legendEvent.dataItem.stateIdx].count = legendEvent.chart.dataProvider[legendEvent.dataItem.stateIdx].storedCount; //restore the value
} else {
// toggle the marker off
legendEvent.dataItem.hidden = true;
legendEvent.chart.dataProvider[legendEvent.dataItem.stateIdx].storedCount = legendEvent.chart.dataProvider[legendEvent.dataItem.stateIdx].count; //store the value
legendEvent.chart.dataProvider[legendEvent.dataItem.stateIdx].count = undefined; //set to undefined to hide the column
}
legendEvent.chart.validateData(); //redraw the chart
}
chart.addListener('dataUpdated', function(e) {
var legendDataItems; //used to store the legend's custom data array.
if (e.chart.toggleLegend === true) {
//is the user toggling a legend marker? stop here as the dataProvider will get updated in handleLegendClick
e.chart.toggleLegend = false;
return;
}
// if we're at this point, the data provider was updated.
// reconstruct the data array.
// initialize by grabbing the state, setting a color and stoing the index
// for toggline the columns later
legendDataItems = e.chart.dataProvider.map(function(dataElement, idx) {
return {
'title': dataElement.state,
'color': graph.lineColor,
'stateIdx': idx //used in toggling
}
});
// if the legend is not enabled, then we're setting this up for the first time.
// turn it on and attach the event handlers
if (e.chart.legend.enabled === false) {
e.chart.legend.enabled = true;
e.chart.legend.switchable = true;
e.chart.legend.addListener('clickMarker', handleLegendClick);
e.chart.legend.addListener('clickLabel', handleLegendClick);
}
// update the legend custom data and redraw the chart
e.chart.legend.data = legendDataItems;
e.chart.validateNow();
});
Here's a fiddle that illustrates this: http://jsfiddle.net/g254sdq5/1/

PHP Checkboxes won't undo disable attribute, but will uncheck

I have a few pages of long code, but I'm having difficulty getting this to work. The following script checks all and and disables all checkboxes with a certain ID using an onclick command on one main checkbox. When the user unclicks that box, the checkmarks disappear, but remain disabled. Even if they hit the reset button, the disabled boxes stay.
Javascript:
<script>
function checkAllbase(bx) {
var cbs = document.getElementsByTagName('input');
for(var i=0; i < cbs.length; i++) {
if(cbs[i].id == '1') {
cbs[i].checked = bx.checked;
cbs[i].disabled=true;
}
}
}
</script>
Give each checkbox input that is associated with a package a similar name or class. Without seeing your actual html, supplying sample html below
HTML
<input type="checkbox" class="package1" name="package1[]" value="vacation">
<input type="checkbox" class="package1" name="package1[]" value="bonus">
<input type="checkbox" class="package1" name="package1[]" value="fries">
JS
function togglePackage(isChecked,packageName) {
//Choose one of the two below to use
//Below selects the inputs by the class name
var cbs = document.querySelectorAll("input."+packageName);
//Or Below selects the inputs by the input name
var cbs = document.querySelectorAll("input[name^="+packageName+"]");
for(var i=0; i<cbs.length; i++) {
//Since they are either checked and disabled (both true)
//or unchecked and enabled (both false) we can just use
//isChecked to set both at once.
cbs.checked = isChecked;
cbs.disabled = isChecked;
}
}
//Then somewhere call the togglePackage function
//bx here would be the main checkbox that you want to
//have toggling the other boxes, and "package1" is obviously
//the name or the class you gave the checkboxes.
togglePackage(bx.checked,"package1");
The elements remain disabled because the value being set is a constant (literal) true.
What you can do is copy bx.disabled throughout, as you're doing with bx.checked:
cbs[i].checked = bx.checked;
cbs[i].disabled = bx.disabled;
Or add another argument to pass the desired disabled state:
function checkAllbase(bx, disabled) {
disabled = disabled !== false; // limit to `true` or `false`, prefer `true`
// ...
cbs[i].checked = bx.checked;
cbs[i].disabled = disabled;
// ...
}
// usage
checkAllbase(bx); // disables elements
checkAllbase(bx, true); // also disables elements
checkAllbase(bx, false); // enabled elements

ExtJs problem in filtering store

I'm having trouble in store filtering. My filter function is working fine and returning true/false as expected.. but in the end all the records are filtered out!!
The xstore is reference to store of Grid. I have also used the main store variable.. but no luck!! Any help is appriciated.
xstore.filterBy(function(rec){
app_rec = rec.get('APPNAME').toUpperCase(); //Record's value that needs to be checked'
Ext.each(elems,function(el){ //For each record, it checks 7 (dynamic) elements
//var ischecked = Ext.get(Ext.getCmp(el.id).teamName+'cb').dom.checked;
if(Ext.getCmp(el.id).teamName.toUpperCase() == app_rec)
{// If Element's attribute 'teamname' is matched then check if element's chkbox is chked/unched'
var ischecked = Ext.get(Ext.getCmp(el.id).teamName+'cb').dom.checked; //get the checkbox
//alert("app_rec: "+app_rec+"panelTeam: " + Ext.getCmp(el.id).teamName.toUpperCase()+"isChecked: "+ischecked );
if(ischecked) //if isChecked... keep record.. below alert if working as expected
{ alert("return true"+"app_rec: "+app_rec+"panelTeam: " + Ext.getCmp(el.id).teamName.toUpperCase()+"isChecked: "+ischecked);
return true;}
else //Else avoid record
{ //alert("return false");
return false;}
}
});
Thanks,
Tushar Saxena
Ext.each is different from a regular JavaScript for loop in that you can return false within the each call to stop iteration. The Ext.each documentation mentions this:
If the supplied function returns false, iteration stops and this
method returns the current index.
So when you're returning from within the each call, you're not returning true/false to the filterBy function like you expect, but to the each function.
Try keeping a handle on isChecked outside of the each loop, and then return true/false based on what was found inside the each function:
// excluded your other code to highlight area around Ext.each call
var isChecked = false;
Ext.each(elems, function(el){
if(Ext.getCmp(el.id).teamName.toUpperCase() == app_rec) {
ischecked = Ext.get(Ext.getCmp(el.id).teamName+'cb').dom.checked;
// can exit early if isChecked is true
if(isChecked){
return false; // this will exit the Ext.each method
}
}
});
// if this is true, filterBy will include the record
return isChecked;

Resources