I've found strange behaviour of zk checkbox in MVVM. I made a MVVM form that shows list of items and detailed view of selected item. I placed checkbox on detailed view and bind it to boolean property of my POJO. And when I select an item with true value of this property checkbox is displayed checked, but next when I select an item with false value of property and then select an item with true value again checkbox is displayed unchecked.
I can illustrate this issue using ZK's MVVM tutorial http://books.zkoss.org/wiki/ZK_Getting_Started/Get_ZK_Up_and_Running_with_MVVM (source code http://sourceforge.net/projects/zkbook/files/GettingStarted/getzkup-20131127.zip/download )
Add to Car class boolean property:
private Boolean cool = false;
public Car(Integer id, String model, String make, String description, String preview, Integer price, boolean cool){
this.id = id;
this.model = model;
this.make = make;
this.preview = preview;
this.description = description;
this.price = price;
this.cool = cool;
}
public Boolean getCool() {
return cool;
}
public void setCool(Boolean cool) {
this.cool = cool;
}
Change CarServiceImpl.java to initialize boolean property for our demo:
carList.add(
new Car(id++,
"Camry",
"Toyota",
"The Toyota Camry is a midsize car ... ",
"/img/car3.png",
24170, true));
carList.add(
new Car(id++,
"Century",
"Toyota",
"The Toyota Century is ... " ,
"/img/car4.png",
28730, true));
Change searchMvvm.zul (add lines labeled as INSERTED):
<window title="Search" width="600px" border="normal" apply="org.zkoss.bind.BindComposer"
viewModel="#id('vm') #init('tutorial.SearchViewModel')">
<hbox align="center">
Keyword:
<textbox value="#bind(vm.keyword)" />
<button label="Search" image="/img/search.png" onClick="#command('search')" />
</hbox>
<listbox height="160px" model="#bind(vm.carList)" emptyMessage="No car found in the result"
selectedItem="#bind(vm.selectedCar)">
<listhead>
<listheader label="Model" />
<listheader label="Make" />
<listheader label="Price" width="20%"/>
<listheader label="Cool" /> <!-- INSERTED -->
</listhead>
<template name="model">
<listitem>
<listcell label="#bind(each.model)"></listcell>
<listcell label="#bind(each.make)"></listcell>
<listcell>$<label value="#bind(each.price)" /></listcell>
<listcell><checkbox checked="#bind(each.cool)" /></listcell> <!-- INSERTED -->
</listitem>
</template>
</listbox>
<hbox style="margin-top:20px">
<image width="250px" src="#bind(vm.selectedCar.preview)" />
<vbox>
<label value="#bind(vm.selectedCar.model)" />
<label value="#bind(vm.selectedCar.make)" />
<label value="#bind(vm.selectedCar.price)" />
<label value="#bind(vm.selectedCar.description)" />
<checkbox checked="#bind(vm.selectedCar.cool)" label="Cool" /> <!-- INSERTED -->
</vbox>
</hbox>
After that start Tomcat and enter localhost:8080/tutorial/searchMvvm.zul in your browser. When you click on Toyota Camry item everything is ok, but when you click on Nissan Cifiro and then on Toyota Camry again checkbox in detaled view will be displayed unchecked. But checkbox in listbox works fine.
Do you know any workaround to solve this problem?
I have the same problem, but it was fixed when I change getCool() with isCool() (on ZK V7)
I'm using ZK7 and I had the same problem.
I got a :
Property 'standard' not readable on type
on the Boolean standard, which has getter isStandard and setter setStandard
The solution is:
When using Boolean (the object) then you have to use get-Prefix
When using boolean (the primitive) then you can use is-Prefix
That's it !
You should use
checkbox.setCheck(Boolean.valueOf(true));
Related
I am having trouble getting data binding to work with custom components.
I have created an IncrementValue property that gets incremented with every button click.
The changes are reflected when binded to a Label.
However they do not work when I bind it to a Bindable property in a custom component.
In the example, I have built a custom component called Card which has two bindable properties CardTitle and CardIncrement
Is there something I'm missing as I'm new to MAUI and even Xamarin.
Github link of code snippets below: https://github.com/814k31/DataBindingExample
Card.xaml.cs
namespace DataBindingExample;
public partial class Card : VerticalStackLayout
{
public static readonly BindableProperty CardTitleProperty = BindableProperty.Create(nameof(CardTitle), typeof(string), typeof(Card), string.Empty);
public static readonly BindableProperty CardIncrementProperty = BindableProperty.Create(nameof(CardIncrement), typeof(int), typeof(Card), 0);
public string CardTitle
{
get => (string)GetValue(CardTitleProperty);
set => SetValue(CardTitleProperty, value);
}
public int CardIncrement
{
get => (int)GetValue(CardIncrementProperty);
set => SetValue(CardIncrementProperty, value);
}
public Card()
{
InitializeComponent();
BindingContext = this;
}
}
Card.xaml
<?xml version="1.0" encoding="utf-8" ?>
<VerticalStackLayout
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:databindingexample="clr-namespace:DataBindingExample"
x:DataType="databindingexample:Card"
x:Class="DataBindingExample.Card"
Spacing="25"
Padding="30,0"
VerticalOptions="Center"
BackgroundColor="red"
>
<Label
Text="{Binding CardTitle}"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center"
/>
<Label
Text="{Binding CardIncrement}"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center"
/>
</VerticalStackLayout>
MainPage.xml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingExample.MainPage"
xmlns:DataBindingExample="clr-namespace:DataBindingExample"
xmlns:ViewModels="clr-namespace:DataBindingExample.ViewModels"
x:DataType="ViewModels:MainPageViewModel"
>
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center"
>
<Label
Text="{Binding IncrementedValue}"
SemanticProperties.HeadingLevel="Level2"
FontSize="18"
HorizontalOptions="Center"
/>
<!-- Why doesnt this work? -->
<DataBindingExample:Card CardIncrement="{Binding IncrementedValue}" />
<Button
x:Name="CounterBtn"
Text="Click Me"
SemanticProperties.Hint="Counts the number of times you click"
Command="{Binding IncrementValueCommand}"
HorizontalOptions="Center"
/>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
When making a custom component (that includes XAML), DO NOT set BindingContext = this;.
REASON: You want the component to use the SAME BindingContext as the page it is placed in. This happens automatically, if you do NOT set a BindingContext in the custom component.
HOWEVER, removing this line breaks all your component's xaml Bindings; you'll need to add something to the xaml, to fix this.
Or to put it another way: How refer to the card's Properties from its XAML? See the next section.
ACCESS COMPONENT PROPERTIES VIA x:Name
Solution: Give the card an x:Name, and make that the "Source" of those bindings:
<VerticalStackLayout
...
x:Name="me" <-- IMPORTANT! Change name as desired.
x:Class="DataBindingExample.Card"
>
...
<Label Text={Binding CardIncrement, Source={x:Reference me}}"
...
Notice the two parts to this solution:
In component's xaml header, define x:Name="mynamehere".
In each Binding, say that the component is the source:
, Source={x:Reference mynamehere}.
OPTIONAL: If custom component has a "ViewModel":
To have a custom component be "data-driven", pass in a parameter that controls its behavior.
This parameter could be considered a "ViewModel", but above I have specified:
DO NOT set a BindingContext (so that component has easy access to the page's BindingContext).
So unlike other uses of ViewModel, in this technique, we don't set the ViewModel as the BindingContext.
How access this ViewModel?
By saving it as a property of the component; e.g.:
public partial class MyComponent : ContentView
{
private MyViewModel VM;
public void MyComponent(MyViewModel vm)
{
InitializeComponent();
VM = vm;
}
public class MyViewModel : ObservableObject
{
[ObservableProperty]
SomeType someProperty; // This is field. Property "SomeProperty" is generated.
}
Then in xaml, we access properties of VM, using . notation:
<Label Text={Binding VM.SomeProperty, Source={x:Reference me}}"
I'm using TableSelectDialog to view some Carrier details. All I need is: If I select any item (row), then all the values from that row should get automatically populated to the form input fields.
In the attached image, if Carrier Name is selected from TableSelectDialog, then the remaining fields should be populated based on that value.
You can achieve the required functionality using the Binding context that you receive from the TableSelcect Dialog.
But for binding context to work properly, both form and the table select dialog should refer to the same Model.
Below is the working code:
VIEW.XML:
Has a Button to trigger the table select dialog.
Has a form.
<l:VerticalLayout class="sapUiContentPadding" width="100%">
<l:content>
<Button class="sapUiSmallMarginBottom" text="Show Table Select Dialog"
press="handleTableSelectDialogPress">
</Button>
<VBox class="sapUiSmallMargin">
<f:SimpleForm id="SimpleFormDisplay354">
<f:content>
<Label text="Supplier Name" />
<Text id="nameText" text="{SupplierName}" />
<Label text="Description" />
<Text text="{Description}" />
<Label text="ProductId" />
<Text text="{ProductId}" />
<Label text="Quantity" />
<Text id="countryText" text="{Quantity}" />
</f:content>
</f:SimpleForm>
</VBox>
</l:content>
</l:VerticalLayout>
Controller:
onInit: function () {
// set explored app's demo model on this sample
var oModel = new JSONModel(jQuery.sap.getModulePath("sap.ui.demo.mock", "/products.json"));
this.getView().setModel(oModel);
},
handleTableSelectDialogPress: function (oEvent) {
if (!this._oDialog) {
this._oDialog = sap.ui.xmlfragment("sap.m.sample.TableSelectDialog.Dialog", this);
}
this.getView().addDependent(this._oDialog);
// toggle compact style
this._oDialog.open();
},
handleClose: function (oEvent) {
var aContexts = oEvent.getParameter("selectedContexts");
if (aContexts && aContexts.length) {
// MessageToast.show("You have chosen " + aContexts.map(function(oContext) { return oContext.getObject().Name; }).join(", "));
this.byId('SimpleFormDisplay354').setBindingContext(aContexts[0]);
}
oEvent.getSource().getBinding("items").filter([]);
}
Now, we also have a Select dialog and on click of any Item we call method : handleClose
In handleClose, we obtain the clicked Item binding context and then tell the form : hey! refer to this context ( which is present in the model). Binding context has a path which tells the form from where to do the relative binding.
Please feel free to contact for more information.
I am trying to get the value of a textbox on a zul page by using some kind of getValue method. I should handle this on the zul page, not on a controller. I need to assign a listbox cell (which is the first cell of the list box below) with a value coming from the textbox.
<listcell>
<label value="" />
</listcell>
<listcell>
<toolbarbutton visible="true"
image="/resources/images/icons/1616/page_text.gif" />
</listcell>
<listcell>
<label value="#{file.name}" />
</listcell>
<listcell>
<toolbarbutton forward="onClick=onRemoveMultipleFiles"
visible="true" id="newFileAndCommentRemove" image="/resources/images/icons/1616/delete.png" />
</listcell>
</listitem>
If what you want is that after the textbox is filled then the first cell will fill with its value you can do it like this:
put an id into the label in the cell
put an onChange operation in the textbox so when the textbox change you can put its value into the cell
like this:
<textbox id="textbox" onChange="label.setValue(self.getValue())"/>
<listbox id="newFileAndComment">
<listhead>
<listheader label="1" width="30px" />
</listhead>
<listitem self="#{each=file}">
<listcell>
<label id="label"/>
</listcell>
</listitem>
I am looking to add views (or workflow) to a roadmap steps in sapui5. I am new to sapui5, can any one help me out with this?
My code:
<script>
//create the RoadMap control
var oRMap = new sap.ui.commons.RoadMap("rMap");
//create the RoadMap steps
var oStep1 = new sap.ui.commons.RoadMapStep("step1", {label: "Step 1"});
var oStep2 = new sap.ui.commons.RoadMapStep("step2", {label: "Step 2"});
var oStep3 = new sap.ui.commons.RoadMapStep("step3", {label: "Step 3"});
//add steps to the RoadMap
oRMap.addStep(oStep1);
oRMap.addStep(oStep2);
oRMap.addStep(oStep3);
//Set the first step as selected
oRMap.setSelectedStep("step1");
//Set the number of visible steps
oRMap.setNumberOfVisibleSteps(3);
//Place the control on the UI
oRMap.placeAt("sample1");
</script>
This will show three steps in my application. What I want is to add views to each of the steps.
Say I want to add a date picker for first step, table for second step and so on..
How can I do this?
You can achieve this in many ways. I would create a container below your roadmap, where you display different views, one for each step. You could use a NavContainer which handles the navigation
EDIT: it can be as simple as this (I used XMLView notation since I find these a far easier to write, but the same applies for JSViews of course):
<VBox>
<c:RoadMap id="roadMap">
<c:steps>
<c:RoadMapStep id="step1" label="Step 1" />
<c:RoadMapStep id="step2" label="Step 2" />
<c:RoadMapStep id="step3" label="Step 3" />
</c:steps>
</c:RoadMap>
<NavContainer width="100%" height="20rem">
<Page title="Step 1">
<DatePicker />
<Button icon="sap-icon://feeder-arrow" text="Next step" press="doNext" />
</Page>
<Page title="Step 2">
<Text text="Some data"/>
<Button icon="sap-icon://nav-back" text="Previous step" press="doPrevious" />
<Button icon="sap-icon://feeder-arrow" text="Next step" press="doNext" />
</Page>
<Page title="Step 3">
<Text text="Some more data"/>
<Button icon="sap-icon://nav-back" text="Previous step" press="doPrevious" />
<Button icon="sap-icon://feeder-arrow" text="Next step" press="doNext" />
</Page>
</NavContainer>
</VBox>
In the doNext and doPrevious you then increment/decrement the roadmap's selectedStep property with the correct step ID, and you perform a nav.to(target) or nav.back()
See https://sapui5.hana.ondemand.com/sdk/explored.html#/sample/sap.m.sample.NavContainer/preview for info in the NavContainer
I am trying to hide/show a row in a grid (filled dynamically from Java code) upon clicking on it's previous row. To simulate a simple MasterDetail component. I get the right index of the clicked row in the java code, however changing visibility of the row doesn't work! Can anyone help me with this or is there a similar way to to this?
Thanks,
Pooya
Here is the code for the ZUL:
<?xml version="1.0" encoding="UTF-8"?>
<zk xmlns="http://www.zkoss.org/2005/zul">
<window id="callbackLogWindow"
apply="CallbackLogWindowComposer"
border="none" height="100%" width="100%"
xmlns:w="http://www.zkoss.org/2005/zk/client">
<grid id="callbackLogGrid" oddRowSclass="non-odd" height="100%">
<columns>
<column label="Logging Name" />
<column label="Status" />
<column label="DateTime" />
<column label="Subject" />
<column label="Replies" />
</columns>
<rows>
<zk forEach="${callbackLogWindow$composer.callbacks}">
<row sclass='${forEachStatus.index % 2 != 0 ? "z-grid-odd" : ""}'
onClick="callbackLogWindow$composer.toggleRow(self.index)">
<custom-attributes callback="${each}"/>
<cell><label value="${callback.loggingName}" /></cell>
<cell><label value="${callback.resolved}" /></cell>
<cell><label value="${callback.callbackTime}" /></cell>
<cell><label value="${callback.subject}" /></cell>
<cell><label value="${callback.resolvedItemCount}" /></cell>
</row>
<row sclass='${forEachStatus.index % 2 != 0 ? "z-grid-odd" : ""}'>
<cell colspan="5">
<include src="callbackItem.zul" callback="${each}"/>
</cell>
</row>
<row>
<custom-attributes callback="${each}"/>
<button onClick="callbackLogWindow$composer.saveCallbackItems(callback)">
Save
</button>
</row>
</zk>
</rows>
</grid>
</window>
</zk>
And the controller:
public class CallbackLogWindowComposer extends SelectorComposer<Window> {
#Inject private CallbackDao callbackDao;
#Wire Grid callbackLogGrid;
private List<Callback> callbacks = new ArrayList<Callback>();
#Override
public void doAfterCompose(Window window) throws Exception {
super.doAfterCompose(window);
}
public List<Callback> getCallbacks() {
callbacks = callbackDao.findAll();
return callbacks;
}
public void toggleRow(int i) {
Component row = callbackLogGrid.getRows().getChildren().get(i+1);
row.setVisible(row.isVisible());
callbackLogGrid.renderAll();
}
public void saveCallbackItems(Callback cb) {
callbackDao.saveInTransaction(cb);
}
}
I am not sure and not test but i can suggest one thing here to you Zk have visible="true/false" attribute you can apply this attribute in each row and bind this with your databean variable For more detail i can suggest let us suppose you have a List<A> list
and list contain all the record which you want to display now add another variable like display in class A and control it by your self .And onClick on any row change update any other item of list .
I have the same problem, and the solution I found was:
row visible=""
<grid model="#bind(vm.total)" vflex="1" emptyMessage="No records exist" width="315px" >
<columns>
<column width="45%"/>
<column width="25%" />
</columns>
<template name="model">
<row visible="#load(each.visible)">
<checkbox label="#load(each.label)" style="font-weight:bold" if="${each.checkbox}"/>
<label value="#load(each.label)" style="font-weight:bold" unless="${each.checkbox}"/>
<doublebox value="#bind(each.value)" sclass="textBoxNumber" locale="us" readonly="true" />
</row>
</template>
</grid>
</groupbox>