Hi is it possible to set the time range of a Dojo textTimeBox to 09:00 - 18:30.
I can't find anything in either the Zend or Dojo documentation that show how this can be done or if it can be done.
Many thanks in advance.
You can set max and min constraints for widget:
new dijit.form.TimeTextBox({
name: "prog_val",
value: new Date(),
constraints: {
timePattern: 'HH:mm:ss',
clickableIncrement: 'T00:15:00',
visibleIncrement: 'T00:15:00',
visibleRange: 'T01:00:00',
min:'T09:00:00',
max:'T18:30:00'
}
},
"prog_val");
It does not allow the user to enter data beyond the allowed values.
However this still allows user to scroll to the disabled times, user just cannot select them.
For hiding disabled times you should do some hack :)
You should override _getFilteredNodes method of dijit._TimePicker. For example :
dojo.declare("my._TimePicker", dijit._TimePicker, {
// extend the default show() method
_getFilteredNodes: function (/*number*/start, /*number*/maxNum, /*Boolean*/before) {
// summary:
// Returns an array of nodes with the filter applied. At most maxNum nodes
// will be returned - but fewer may be returned as well. If the
// before parameter is set to true, then it will return the elements
// before the given index
// tags:
// private
var nodes = [], n, i = start, max = this._maxIncrement + Math.abs(i),
chk = before ? -1 : 1, dec = before ? 1 : 0, inc = before ? 0 : 1;
do {
i = i - dec;
var date = new Date(this._refDate);
var incrementDate = this._clickableIncrementDate;
date.setHours(date.getHours() + incrementDate.getHours() * i,
date.getMinutes() + incrementDate.getMinutes() * i,
date.getSeconds() + incrementDate.getSeconds() * i);
if (!this.isDisabledDate(date)) {
n = this._createOption(i);
if (n) { nodes.push(n); }
}
i = i + inc;
} while (nodes.length < maxNum && (i * chk) < max);
if (before) { nodes.reverse(); }
return nodes;
}
});
And you need to set this new class ('my._TimePicker') as a popupClass property of your text time box:
dojo.addOnLoad(function () {
dijit.byId("prog_val").popupClass = "my._TimePicker";
});
And you can see : it works!
Related
I use Google sheet and Cryptofinance.ai to retreive cryptocurrency prices. I have one script that works well : it appends data periodically from one row in tab A to save them in a tab B so I can make graphs and charts.
Now I'd like to add date + 1 year in the next row. Idealy, each time the script is triggered, it make two new rows : The one with the data as it is now and the one with the date + 1 year.
If you curious and want to know why I want to do that is is to make projection price using this
formula in another tab : =TREND(filter(B2:B,B2:B<>""),filter(A2:A,B2:B<>""),filter(A2:A,(N(B2:B)=0)*(A2:A>0)))
Here is my script now:
// [START modifiable parameters]
var rangeToLog = 'Portefeuille!A28:L28';
var sheetToLogTo = 'Archive BTC/USD KRAKEN';
// [END modifiable parameters]
////////////////////////////////
/**
* Appends a range of values to the end of an archive sheet.
* A timestamp is inserted in column A of each row on the archive sheet.
* All values in rangeToLog go to one row on the archive sheet.
*
* #OnlyCurrentDoc
*/
function appendValuesToArchiveBTCUSDKRAKENSheet() {
// version 1.4, written by --Hyde, 30 January 2020
// - use Array.prototype.some() to skip empty rows when concating
// - see https://support.google.com/docs/thread/27095918?msgid=27148911
// version 1.3, written by --Hyde, 26 January 2020
// - see https://support.google.com/docs/thread/26760916
var ss = SpreadsheetApp.getActive();
var valuesToLog = ss.getRange(rangeToLog).getValues();
var logSheet = ss.getSheetByName(sheetToLogTo);
if (!logSheet) {
logSheet = ss.insertSheet(sheetToLogTo);
logSheet.appendRow(['Date time', 'Data']);
}
var rowToAppend = [new Date()].concat(
valuesToLog.reduce(function concatArrays_(left, right) {
var arrayContainsData = right.some(function isNonBlanky_(element, index, array) {
return element !== null && element !== undefined && element !== '';
});
return arrayContainsData ? left.concat(right) : left;
})
);
logSheet.appendRow(rowToAppend);
}
NOW :
What I want to do:
The easy fix is to simply add another appendRow() call at the end of your function with the one year from now value.
function appendValuesToArchiveBTCUSDKRAKENSheet() {
// ...
logSheet.appendRow(rowToAppend);
logSheet.appendRow([new Date(new Date().setFullYear(new Date().getFullYear() + 1))]);
}
A more complex solution, but with better execution time, would have you print both rows in a single setValues() call. This follows the best practice of using batch operations, but I suspect that the easier solution above is adequate for your purpose. I do encourage you, however, to try implementing the batch operation if you want to improve your apps script skills.
Finally, after some research I came to this :
function expCalc(){
delLastNRows();
appendValuesToArchiveBTCUSDKRAKENSheet();
}
function delLastNRows(n){
var n=n || 1;//allows you to delete last three rows without passing function a parameter.
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Archive BTC/USD KRAKEN');
var lr=sh.getLastRow();
if(lr>=n){
for(var i=0;i<n;i++){
sh.deleteRow(sh.getLastRow());
}
}
}
// [START modifiable parameters]
var rangeToLog = 'Portefeuille!A28:L28';
var sheetToLogTo = 'Archive BTC/USD KRAKEN';
// [END modifiable parameters]
////////////////////////////////
/**
* Appends a range of values to the end of an archive sheet.
* A timestamp is inserted in column A of each row on the archive sheet.
* All values in rangeToLog go to one row on the archive sheet.
*
* #OnlyCurrentDoc
*/
function appendValuesToArchiveBTCUSDKRAKENSheet() {
// version 1.4, written by --Hyde, 30 January 2020
// - use Array.prototype.some() to skip empty rows when concating
// - see https://support.google.com/docs/thread/27095918?msgid=27148911
// version 1.3, written by --Hyde, 26 January 2020
// - see https://support.google.com/docs/thread/26760916
var ss = SpreadsheetApp.getActive();
var valuesToLog = ss.getRange(rangeToLog).getValues();
var logSheet = ss.getSheetByName(sheetToLogTo);
var sheet = ss.getSheets()[0]
if (!logSheet) {
logSheet = ss.insertSheet(sheetToLogTo);
logSheet.appendRow(['Date time', 'Data']);
}
var rowToAppend = [new Date()].concat(
valuesToLog.reduce(function concatArrays_(left, right) {
var arrayContainsData = right.some(function isNonBlanky_(element, index, array) {
return element !== null && element !== undefined && element !== '';
});
return arrayContainsData ? left.concat(right) : left;
})
);
logSheet.appendRow(rowToAppend);
logSheet.appendRow([new Date(new Date().setFullYear(new Date().getFullYear() + 1))]);
}
Works like a charm !
I would like it so that when one or more of the options in my checkboxlist has been ticked, the value of said option will then update the amount field.
I'm sort of cheating as I'm using the key value to be the price of the field which I would then like to update the amount field with.
This already works perfectly with the dropdown fields and this does calculate the amount field depending on what has been selected.
As I've had to make the checkboxlist jasonable, the way in which this returns the value is different.
Jobs model:
type_website:
label: Website Package
span: right
type: dropdown
placeholder: '-- Select Website Package --'
trigger:
action: show
condition: value[1]
field: type
type_add_pages:
label: 'Additional Pages'
span: right
type: dropdown
trigger:
action: show
condition: value[1]
field: type
type_addons_get:
label: 'Addons'
span: right
type: checkboxlist
trigger:
action: show
condition: value[1]
field: type
amount:
label: 'Total Amount'
span: left
type: text
readOnly: true
dependsOn:
- type_add_pages
- type_website
- type_addons_get
Jobs.php
protected $jsonable = [
'type_addons_get'
];
public function getTypeAddonsGetOptions()
{
return [
'30' => 'Blog',
'50' => 'Editable Website'
];
}
// Get the value for the amounts field
public function filterFields($fields, $context = null)
{
$typePages = $this->attributes['type_add_pages'];
$typeAddons = array_get($this->attributes, 'type_addons_get');
$typeWebsite = array_get($this->attributes, 'type_website');
return $fields->amount->value = $typeAddons + $typePages + $typeWebsite;
}
As a test, if I just return the following in Jobs.php:
return $fields->amount->value = $typeAddons;
I then get the following result:
Any help would be extremely helpful, thanks in advance!
I will be honest and admit I don't work with the back end forms very often. I actually generate front end systems for clients and myself. So I solved an issue like this with using javascript/jquery in the controller pages (create and update). This is what I think you are trying to do. Here is an example:
Backend in form where I have total price set to read only number input field:
<script>
$('#Form-field-Products-price').change(doThing);
$('#Form-field-Products-set_size').change(doThing);
$('#Form-field-Products-set_color').change(doThing);
$('#checkbox_Form-field-Products-add_ons_1').change(doThing);
$('#checkbox_Form-field-Products-add_ons_2').change(doThing);
$('#checkbox_Form-field-Products-add_ons_3').change(doThing);
function doThing(){
var x = $('#Form-field-Products-price').val();
var y = $('#Form-field-Products-set_color').val();
switch (y) {
case 'silver':
color = 0;
break;
case 'bronze':
color = ".50";
break;
case 'gold':
color = "1.00";
break;
default:
color = 0;
}
var z = $('#Form-field-Products-set_size').val();
switch (z) {
case 'fullset':
size = 0;
break;
case 'partialset':
size = "1.00";
break;
default:
size = 0;
}
if ($('#checkbox_Form-field-Products-add_ons_1').prop('checked') == true)
{
var a = "1.00";
} else
{
var a = 0;
}
if ($('#checkbox_Form-field-Products-add_ons_2').prop('checked') == true)
{
var b = "1.00";
} else
{
var b = 0;
}
if ($('#checkbox_Form-field-Products-add_ons_3').prop('checked') == true)
{
var c = "1.50";
} else
{
var c = 0;
}
$("#Form-field-Products-total_price").val(Number(x) + Number(color) + Number(a) + Number(b) + Number(c) - Number(size));
console.log( Number(x) + ' ' + Number(color) + ' ' + Number(a) + ' ' + Number(b) + ' ' + Number(c) + ' ' + Number(size) );
}
</script>
Here you can see that I am searching the document for these ID's then getting their value. Turning those values into numbers I then push them into the Total Price field.
Here is an adjusted price while filling out the form:
I have a table column which displays customers full names (first name + last name). However, I want to sort this column first by first name and then by last name. Therefore I added some comparator function in my controller:
_customNameComparator: function(value1, value2) {
// Separate all words of the full name
var aWordsName1 = value1.split(" ");
var aWordsName2 = value2.split(" ");
// Get the last and first names of the two names
var sFirstName1 = value1.substring(0, value1.lastIndexOf(" "));
var sLastName1 = aWordsName1[aWordsName1.length - 1];
var sFirstName2 = value2.substring(0, value1.lastIndexOf(" "));
var sLastName2 = aWordsName2[aWordsName2.length - 1];
// 0 values are equal
// -1 value1 smaller than value2
// 1 value1 larger than value2
if (sLastName1 === sLastName2) {
if (sFirstName1 === sFirstName2) {
return 0;
} else if (sFirstName1 > sFirstName2) {
return 1;
} else {
return -1;
}
} else if (sLastName1 > sLastName2) {
return 1;
} else {
return -1;
}
}
When the column Header is clicked, I try to call
var aSorter = [];
aSorter.push(new sap.ui.model.Sorter("FullName", bDescending, false, this._customNameComparator));
var oBinding = this.byId("tableTargetGroupDetails").getBinding("items");
oBinding.sort(aSorter);
The comparator does not work like this. The sorting is just as usual (by the full name). What do I do wrong?
And btw: I know that this can lead to some wrong sorting (e.g. for last names containing out of two or more words), but since it's "only" the sorting this is fine for me at the moment.
Unless your binding's operationMode is Client, your comparator will probably not work. You can set the mode where you do your binding using { parameters: { operationMode: 'Client' } }.
I am try to setup the max number to 10 in this form enter link description here
Please how it is possible to do that ?
Thank you
Please try to add a validation in your code:
$('.qtyplus').click(function(e){
// Stop acting like a button
e.preventDefault();
// Get the field name
fieldName = $(this).attr('name');
// Get its current value
var currentVal = parseInt($('input[name='+fieldName+']').val());
// If is not undefined
if (!isNaN(currentVal) && currentVal < 10) {
// Increment
$('input[name='+fieldName+']').val(currentVal + 1);
} else {
// Otherwise put a 0 there
$('input[name='+fieldName+']').val(1);
}
});
I am trying to find a specific element from my page using ExtJS 4 so I can do modifications on it.
I know its id so it should not be a problem BUT
-I tried Ext.getCmp('theId') and it just return me undefined
-I tried to use down('theId') method by passing through the view and I still get a nullresponse.
As I know the id of the element I tried again the two methods by setting manually the id and it didn't work neither.
Do these two methods not function?
How should I do?
Here is the concerned part of the code :
listeners: {
load: function(node, records, successful, eOpts) {
var ownertree = records.store.ownerTree;
var boundView = ownertree.dockedItems.items[1].view.id;
var generalId = boundView+'-record-';
// Add row stripping on leaf nodes when a node is expanded
//
// Adding the same feature to the whole tree instead of leaf nodes
// would not be much more complicated but it would require iterating
// the whole tree starting with the root node to build a list of
// all visible nodes. The same function would need to be called
// on expand, collapse, append, insert, remove and load events.
if (!node.tree.root.data.leaf) {
// Process each child node
node.tree.root.cascadeBy(function(currentChild) {
// Process only leaf
if (currentChild.data.leaf) {
var nodeId = ""+generalId+currentChild.internalId;
var index = currentChild.data.index;
if ((index % 2) == 0) {
// even node
currentChild.data.cls.replace('tree-odd-node', '')
currentChild.data.cls = 'tree-even-node';
} else {
// odd node
currentChild.data.cls.replace('tree-even-node', '')
currentChild.data.cls = 'tree-odd-node';
}
// Update CSS classes
currentChild.triggerUIUpdate();
console.log(nodeId);
console.log(ownertree.view.body);
console.log(Ext.getCmp(nodeId));
console.log(Ext.getCmp('treeview-1016-record-02001001'));
console.log(ownertree.view.body.down(nodeId));
console.log(ownertree.view.body.down('treeview-1016-record-02001001'));
}
});
}
}
You can see my console.log at the end.
Here is what they give me on the javascript console (in the right order):
treeview-1016-record-02001001
The precise id I am looking for. And I also try manually in case...
h {dom: table#treeview-1016-table.x-treeview-1016-table x-grid-table, el: h, id: "treeview-1016gridBody", $cache: Object, lastBox: Object…}
I checked every configs of this item and its dom and it is exactly the part of the dom I am looking for, which is the view containing my tree. The BIG parent
And then:
undefined
undefined
null
null
Here is the item I want to access:
<tr role="row" id="treeview-1016-record-02001001" ...>
And I checked there is no id duplication anywhere...
I asked someone else who told me these methods do not work. The problem is I need to access this item to modify its cls.
I would appreciate any idea.
You are looking for Ext.get(id). Ext.getCmp(id) is used for Ext.Components, and Ext.get(id) is used for Ext.dom.Elements. See the docs here: http://docs.sencha.com/extjs/4.2.1/#!/api/Ext-method-get
Ok so finally I used the afteritemexpand listener. With the ids I get the elements I am looking for with your Ext.get(id) method kevhender :).
The reason is that the dom elements where not completely loaded when I used my load listener (it was just the store) so the Ext.get(id) method couldn't get the the element correctly. I first used afterlayout listener, that was correct but too often called and the access to the id was not so easy.
So, here is how I did finally :
listeners: {
load: function(node, records, successful, eOpts) {
var ownertree = records.store.ownerTree;
var boundView = ownertree.dockedItems.items[1].view.id;
var generalId = boundView+'-record-';
if (!node.tree.root.data.leaf) {
// Process each child node
node.tree.root.cascadeBy(function(currentChild) {
// Process only leaf
if (currentChild.data.leaf) {
var nodeId = ""+generalId+currentChild.internalId;
var index = currentChild.data.index;
if ( (index % 2) == 0 && ids.indexOf(nodeId) == -1 ) {
ids[indiceIds] = nodeId;
indiceIds++;
}
console.log(ids);
}
});
}
},
afteritemexpand: function( node, index, item, eOpts ){
/* This commented section below could replace the load but the load is done during store loading while afteritemexpand is done after expanding an item.
So, load listener makes saving time AND makes loading time constant. That is not the case if we just consider the commented section below because
the more you expand nodes, the more items it will have to get and so loading time is more and more important
*/
// var domLeaf = Ext.get(item.id).next();
// for ( var int = 0; int < node.childNodes.length; int++) {
// if (node.childNodes[int].data.leaf && (int % 2) == 0) {
// if (ids.indexOf(domLeaf.id) == -1) {
// ids[indiceIds] = domLeaf.id;
// indiceIds++;
// }
// }
// domLeaf = domLeaf.next();
// }
for ( var int = 0; int < ids.length; int++) {
domLeaf = Ext.get(ids[int]);
if (domLeaf != null) {
for ( var int2 = 0; int2 < domLeaf.dom.children.length; int2++) {
if (domLeaf.dom.children[int2].className.search('tree-even-node') == -1){
domLeaf.dom.children[int2].className += ' tree-even-node';
}
}
}
}
},
With ids an Array of the ids I need to set the class.
Thank you for the method.