I want to check if the textboxes created like this:
(function(arr) {
for (var i = 0; i < arr.length; i++) {
app.add(app.createLabel(arr[i] + " mail"));
app.add(app.createTextBox().setName(arr[i]));
}
})(["first", "second", "third"]);
have the same contents? I was looking for something like getElementByTagname or or getTextboxes, but there are no such functions.
So how to iterate thvrough them and show a label if they are all equal?
To access any widget values you need to add them as a callback element (or a parent panel) to the server handler that will process them. The values of each widget are populated on a parameter passed to the handler function and can be referenced by the widget name (that you already set).
You don't need to setId as suggested on another answer. Unless you want to do something with the widget itself (and not its value). e.g. change its text or hide it, etc.
var textBoxes = ["first", "second", "third"];
function example() {
var app = UiApp.createApplication().setTitle('Test');
var panel = app.createVerticalPanel();
textBoxes.forEach(function(name){
panel.add(app.createLabel(name + " mail"));
panel.add(app.createTextBox().setName(name));
});
panel.add(app.createLabel('Example Label').setId('label').setVisible(false));
var handler = app.createServerHandler('btnHandler').addCallbackElement(panel);
panel.add(app.createButton('Click').addClickHandler(handler));
SpreadsheetApp.getActive().show(app.add(panel));
}
function btnHandler(e) {
var app = UiApp.getActiveApplication(),
allEqual = true;
for( var i = 1; i < textBoxes.length; ++i )
if( e.parameter[textBoxes[i-1]] !== e.parameter[textBoxes[i]] ) {
allEqual = false; break;
}
app.getElementById('label').setVisible(allEqual);
return app;
}
Notice that ServerHandlers do not run instantly, so it may take a few seconds for the label to show or hide after you click the button.
When you create the textboxes, assign each one an id using setId(id).
When you want to obtain their reference later, you can then use getElementById(id).
Here is an example:
function doGet() {
var app = UiApp.createApplication();
app.add(app.createTextBox().setId("tb1").setText("the original text"));
app.add(app.createButton().setText("Change textbox").addClickHandler(app.createServerHandler("myHandler")));
return app;
}
function myHandler() {
var app = UiApp.getActiveApplication();
app.getElementById("tb1").setText("new text: in handler");
return app;
}
Related
Say I have a list of items in a LazyColumn and they can all be selected. How can I make sure when an item is selected, the others are deselected (so only 1 item can be selected at once)?
I was thinking about interactionSource but I'm not really sure how use it.
I'd extract this logic into a ViewModel, with your items being a specific UIModel that contains your current data of the items + their state, containing the isSelected state.
For ex:
data class YourUiModel(val id: Long, val isSelected: Boolean = false, ...)
private val _items = MutableStateFlow(emptyList<YourUiModel>())
val items: StateFlow<List<YourUiModel>>
get() = _items
private fun changeSelectionTo(uiModel: YourUiModel) {
_items.value = items.value.map { it.copy(isSelected = it.id == uiModel.id) }
}
...
Just store a variable holding the index of the selected variable
var sel by mutableStateOf(0)
Now you may want to use remember here if you are declaring it in your Composables, but I would highly recommend storing it inside a viewmodel. A simple example would be
class VM: ViewModel(){
var sel by mutableStateOf(0) //Assuming 0 is selected initially, change it to -1 if you please
//Create a setter optionally, for state-hoisting
fun setSel(index: Int){
sel = index
}
}
In the MainActivity,
class MainActivity{
val vm by viewmodels<VM>()
setContent{
MySelectableList(
list = ...,
selected = vm.sel,
onSelectionChange = vm::setSel // Just pass the viewmodel if you did not create the setter
)
}
#Composable
fun MySelectableList(
list: List<Any>,
selected: Int,
onSelectionChange: (int) -> Unit // or vm, if you did not create the setter
){
list.forEachIndexed {index, item ->
// Now create this Composable yourself
ItemComposable(
isSelected = index == selected,
onClick = { onSelectionChange(index) } // Just focus and have a look at what's happening here
)
}
}
}
I am using state-hoisting here, read this for reference. Def take this codelab if you haven't already
You will need to do an additional check if you set the initial value of sel to -1 earlier. Completely understand the code that is flowing through here.
You can try use remember {...}
#Composable
fun MyComposeList(data: List<MyItemData>,...) {
val (selectedItem, setSelectedItem) = remember { mutableStateOf(null // or maybe data.first() } // remember current selected by state delegate
lazyColumn() { // your lazy column
items (data) { item -> // item = each item
// your compose item view
MyComposeItemView (
data = item, // pass data by each item
isSelected = data == selectedItem // set is item selected by default
) {
// on item click
setSelectedItem(it)
}
}
}
}
#Composable
fun MyComposeItemView(
data: MyItemData,
isSelected: Boolean,
onSelect: (MyItemData) -> Unit = {}) {
// here set item selected state = isSelected maybe change color or something
// assume we have box as single item
Box(Modifier.clickable {
onSelect(data) // cal on click function and pass current data as parameter
})
}
Vaadin widgets are simple and awesome! But they are also poorly configurable.
I need my DateField widget to open calendar on focus event. I didn't find that functionality in official Vaadin documentation. I found some 3rd party widget here, but it's compiled for Vaadin 7.7 and I use latest Vaadin (8.0.6). Also it has Joda-time 2.1 dependency which is highly undesirable in my project. So, is there any simple way to tune stock vaadin DateField widget to open it's calendar on field focus, or do I need to write my own component for that? Any help is appreciated.
As I was saying in my comment, as far as I know, currently the framework does not offer an implicit way to programmatically open the calendar popup. The same thing goes for some other components such as the grid editor, or the combo item list.
One quick workaround I can think of, is to add a javascript extension that registers focus listeners for all date fields, and clicks the button when a date field is focused. Please find below a sample.
P.S. If you only need to apply this to only some date fields, you can add IDs and pass them to the JS, where you'll do something like document.getElementById('myDateFieldId') instead of document.getElementsByClassName("v-datefield").
1) Layout with components
public class MyDateFieldComponent extends HorizontalLayout {
public MyDateFieldComponent() {
// basic setup
DateField fromDateField = new DateField("From", LocalDate.of(2011, Month.FEBRUARY, 6));
DateField toDateField = new DateField("To", LocalDate.of(2018, Month.FEBRUARY, 6));
setSpacing(true);
addComponents(fromDateField, toDateField);
// add the extension
addExtension(new CalendarFocusPopupOpenerExtension());
}
}
2) Extension - java/server side
import com.vaadin.annotations.JavaScript;
import com.vaadin.server.AbstractJavaScriptExtension;
#JavaScript("calendar-focus-popup-opener-extension.js")
public class CalendarFocusPopupOpenerExtension extends AbstractJavaScriptExtension {
public CalendarFocusPopupOpenerExtension() {
// call the bind function defined in the associated JS
callFunction("bind");
}
}
3) Extension - js/client side
window.com_example_calendar_CalendarFocusPopupOpenerExtension = function () {
this.bind = function () {
if (document.readyState === "complete") {
// if executed when document already loaded, just bind
console.log("Doc already loaded, binding");
bindToAllDateFields();
} else {
// otherwise, bind when finished loading
console.log("Doc nod loaded, binding later");
window.onload = function () {
console.log("Doc finally loaded, binding");
bindToAllDateFields();
}
}
};
function bindToAllDateFields() {
// get all the date fields to assign focus handlers to
var dateFields = document.getElementsByClassName("v-datefield");
for (var i = 0; i < dateFields.length; i++) {
addFocusListeners(dateFields[i]);
}
}
function addFocusListeners(dateField) {
// when focusing the date field, click the button
dateField.onfocus = function () {
dateField.getElementsByTagName("button")[0].click();
};
// or when focusing the date field input, click the button
dateField.getElementsByTagName("input")[0].onfocus = function () {
dateField.getElementsByTagName("button")[0].click();
};
}
};
4) Result
LATER UPDATE
A second approach could be to assign some IDs to your fields, and then check periodically to see when all are visible, and as soon as they are, bind the focus listeners.
1) Layout with components
public class MyDateFieldComponent extends HorizontalLayout {
public MyDateFieldComponent() {
// basic setup
DateField fromDateField = new DateField("From", LocalDate.of(2011, Month.FEBRUARY, 6));
fromDateField.setId("fromDateField"); // use id to bind
fromDateField.setVisible(false); // initially hide it
DateField toDateField = new DateField("To", LocalDate.of(2018, Month.FEBRUARY, 6));
toDateField.setId("toDateField"); // use id to bind
toDateField.setVisible(false); // initially hide it
// simulate a delay until the fields are available
Button showFieldsButton = new Button("Show fields", e -> {
fromDateField.setVisible(true);
toDateField.setVisible(true);
});
setSpacing(true);
addComponents(showFieldsButton, fromDateField, toDateField);
// add the extension
addExtension(new CalendarFocusPopupOpenerExtension(fromDateField.getId(), toDateField.getId()));
}
}
2) Extension - java/server side
#JavaScript("calendar-focus-popup-opener-extension.js")
public class CalendarFocusPopupOpenerExtension extends AbstractJavaScriptExtension {
public CalendarFocusPopupOpenerExtension(String... idsToBindTo) {
// send the arguments as an array of strings
JsonArray arguments = Json.createArray();
for (int i = 0; i < idsToBindTo.length; i++) {
arguments.set(i, idsToBindTo[i]);
}
// call the bind defined in the associated JS
callFunction("bind", arguments);
}
}
3) Extension - js/client side
window.com_example_calendar_CalendarFocusPopupOpenerExtension = function () {
var timer;
this.bind = function (idsToBindTo) {
// check every second to see if the fields are available. interval can be tweaked as required
timer = setInterval(function () {
bindWhenFieldsAreAvailable(idsToBindTo);
}, 1000);
};
function bindWhenFieldsAreAvailable(idsToBindTo) {
console.log("Looking for the following date field ids: [" + idsToBindTo + "]");
var dateFields = [];
for (var i = 0; i < idsToBindTo.length; i++) {
var dateFieldId = idsToBindTo[i];
var dateField = document.getElementById(dateFieldId);
if (!dateField) {
// field not present, wait
console.log("Date field with id [" + dateFieldId + "] not found, sleeping");
return;
} else {
// field present, add it to the list
console.log("Date field with id [" + dateFieldId + "] found, adding to binding list");
dateFields.push(dateField);
}
}
// all fields present and accounted for, bind the listeners!
clearInterval(timer);
console.log("All fields available, binding focus listeners");
bindTo(dateFields);
}
function bindTo(dateFields) {
// assign focus handlers to all date fields
for (var i = 0; i < dateFields.length; i++) {
addFocusListeners(dateFields[i]);
}
}
function addFocusListeners(dateField) {
// when focusing the date field, click the button
dateField.onfocus = function () {
dateField.getElementsByTagName("button")[0].click();
};
// or when focusing the date field input, click the button
dateField.getElementsByTagName("input")[0].onfocus = function () {
dateField.getElementsByTagName("button")[0].click();
};
}
};
4) Result
I'm using dart to develop an app and i want to get the value of a clicked element. I.e:
Html:
<div id="blah">Value!!</div>
Dart:
void main(){
query("#blah").onClick.listen(showValue);
}
void showValue(Event e){
//Get #blah's innerHtml based on the Event(Something like e.target.innerHtml or e.target.value)
}
Anyone? I know how to do it in JS, but I want to know if there's a way to do it in DART!
Edit
Thanks to all of you, I'll try to be a little bit more clear. I have a loop that generates 9 elements dynamically and add them to a container.
Code:
var j = 0;
var optionsSquare = query("#optionsPanel");
while(j < 9){
DivElement minorSquare = new DivElement();
var minorSquareHeight = optionsSquare.clientHeight/3;
var minorSquareWidth = optionsSquare.clientWidth/3;
minorSquare
..attributes = {"class": "miniSugg"}
..style.width = (minorSquareWidth.round()-2).toString().concat("px")
..style.height = (minorSquareHeight.round()-2).toString().concat("px")
..style.float = "left"
..style.border = "1px solid"
..innerHtml = (j+1).toString();
..onClick.listen(showOptionsSquare);
optionsSquare.children.add(minorSquare);
j++;
}
If I try using #Pandian's way, it works, but I only get the value of the last element inside the loop. What I want is to somehow, track down the element clicked and get its value!
EDIT 2
Guys, just solved it!
I'll let the code here as a repo if someone needs this information:
void showOptionsSquare(Event e){
window.location.hash = "#optionsPanel";
mainDiv.style.opacity = (0.2).toString();
DivElement clicked = e.target;
window.alert(clicked.innerHtml);
}
[]'s
Using your example code you could just write
print(e.target.innerHtml);
inside of your showValue function and it would work. Do you get an error or something? If you are worried about the warning you could add a cast to Element or DivElement:
print((e.target as Element).innerHtml);
Try like below... it will help you...
DivElement div;
void showValue(){
window.alert(div.innerHTML);
}
void main() {
div = query('#blah');
div.on.click.add((Event e) => showValue());
}
I'm have a settings view where I'm using MT.D to build out my UI. I just got it to read elements from a database to populate the elements in a section.
What I don't know how to do is access each elements properties or values. I want to style the element with a different background color for each item based on it's value in the database. I also want to be able to get the selected value so that I can update it in the db. Here's the rendering of the code that does the UI stuff with MT.D. I can get the values to show up and slide out like their supposed to... but, styling or adding delegates to them to handle clicks I'm lost.
List<StyledStringElement> clientTypes = SettingsController.GetClientTypes ();
public SettingsiPhoneView () : base (new RootElement("Home"), true)
{
Root = new RootElement("Settings") {
new Section ("Types") {
new RootElement ("Types") {
new Section ("Client Types") {
from ct in clientTypes
select (Element) ct
}
},
new StringElement ("Other Types")
}
Here's how I handled it below. Basically you have to create the element in a foreach loop and then populate the delegate with whatever you want to do there. Like so:
public static List<StyledStringElement> GetClientTypesAsElement ()
{
List<ClientType> clientTypes = new List<ClientType> ();
List<StyledStringElement> ctStringElements = new List<StyledStringElement> ();
using (var db = new SQLite.SQLiteConnection(Database.db)) {
var query = db.Table<ClientType> ().Where (ct => ct.IsActive == true && ct.Description != "Default");
foreach (ClientType ct in query)
clientTypes.Add (ct);
}
foreach (ClientType ct in clientTypes) {
// Build RGB values from the hex stored in the db (Hex example : #0E40BF)
UIColor bgColor = UIColor.Clear.FromHexString(ct.Color, 1.0f);
var localRef = ct;
StyledStringElement element = new StyledStringElement(ct.Type, delegate {
ClientTypeView.EditClientTypeView(localRef.Type, localRef.ClientTypeId);
});
element.BackgroundColor = bgColor;
ctStringElements.Add (element);
}
return ctStringElements;
}
I am working on a class for building drop down buttons dynamically. Here is excerpt one of my code (located in the Class constructor):
_button.onRollOver = function()
{
this.gotoAndStop("over");
TweenLite.to(this.options,0.2 * optionCount,{_y:mask._y, ease:Strong.easeOut, onComplete:detectMouse, onCompleteParams:[button]});
function detectMouse(button:MovieClip)
{
button.options.onMouseMove = function()
{
for (var option:String in this._parent.children)
{
if (this._parent.children[option].hitTest(_root._xmouse, _root._ymouse, true))
{
if (!this._parent.children[option].active) {
this._parent.children[option].clear();
drawOption(this._parent.children[option], "hover");
this._parent.children[option].active = true;
}
}
}
};
}
};
I am attempting to call on the function drawOption() which is inside the same class and looks like so:
private function drawOption(option:MovieClip, state:String)
{
trace("yo");
switch (state)
{
case "hover" :
var backgroundColour:Number = _shadow;
var textColour:Number = 0xffffff;
break;
default :
var backgroundColour:Number = _background;
var textColour:Number = _shadow;
break;
}
option._x = edgePadding;
option._y = 1 + edgePadding + (optionPadding * (option.index)) + (optionHeight * option.index);
option.beginFill(backgroundColour,100);
option.lineStyle(1,_border,100,true);
option.moveTo(0,0);
option.lineTo(_optionWidth,0);
option.lineTo(_optionWidth,optionHeight);
option.lineTo(0,optionHeight);
option.endFill();
var textfield:TextField = option.createTextField("string", option.getNextHighestDepth(), 20, 2, _optionWidth, optionHeight);
var format:TextFormat = new TextFormat();
format.bold = true;
format.size = fontSize;
format.font = "Arial";
format.color = textColour;
textfield.text = option.string;
textfield.setTextFormat(format);
}
But because I am trying to call from inside an onRollOver it seems that it is unable to recognise the Class methods. How would I go about accessing the function without making a duplicate of it (very messy, do not want!).
In AS2 I prefer to use the Delegate class to add functions to event handlers whilst maintaining control over the scope.
You implement it like this:
import mx.utils.Delegate;
//create method allows you to set the active scope, and a handler function
_button.onRollOver = Delegate.create(this,rollOverHandler);
function rollOverHander() {
// since the scope has shifted you need to use
// the instance name of the button
_button.gotoAndStop("over");
TweenLite.to(_button.options,0.2 * optionCount,{_y:mask._y, ease:Strong.easeOut, onComplete:detectMouse, onCompleteParams:[button]});
}
everything in the onrollover relates to the button which is rolled over, to access the outer functions, you would have to navigate to the outer class before calling the function in exactly the same way that you are accessing the outer variables, eg:
if the parent of the button contains the function:
this._parent.drawOption(....)
ContainerMC class:
class ContainerMC extends MovieClip{
function ContainerMC() {
// constructor code
trace("Container => Constructor Called");
}
function Init(){
trace("Container => Init Called");
this["button_mc"].onRollOver = function(){
trace(this._parent.SayHello());
}
}
function SayHello():String{
trace("Container => SayHello Called");
return "Hellooooo World";
}
}
I then have a movieclip in the library with the Class ContainerMC and the identitfier Container_mc, which is added to the stage by this line in the main timeline:
var Container = attachMovie("Container_mc","Container_mc",_root.getNextHighestDepth());
Container.Init();
Edit: added working sample