# @NotifyChange make all related @DependsOn be ignored

trapongx
32 3

[Here Description (question is at the end)]

I've a lot of problem with complicate combobox chaining in my project so I give it a simple POC to allow me to understand it better from the basic. And then I found one behavior which perhaps be by design intentionally but it does not make sense to me. So I post what I found here. What I did is creating this ZUL

<zk>
<vlayout viewModel="@id('vm') @init('TestVm')">
LEVEL 1
LEVEL 2
</vlayout>
</zk>


The TestVM look like this (It's in Kotlin but I hope it is not to understand what is what in each line)

// This one work perfectly
class TestVm {
var level1: String? = null
set(value) {
field = value
level2 = null
}

var level2: String? = null
@DependsOn("level1") get

@Immutable
fun getLevel1Options(): List<String> = listOf("A", "B", "C")

@DependsOn("level1")
fun getLevel2Options(): List<String> = when (level1) {
null -> emptyList()
else -> (1..3).map { "${level1}-${it}" }
}
}


This chained drop down work as expected in this very simple scenario.

The problem come after this. I think of the scenario when level2 is not an immediate property in VM but a nested property in some complex object like "level2.nested1.nested2.nested3". In such case I sometime may not have access to the source code to add annotation to the getter of "level2.nested1.nested2.nested3" so the alternative way is to use @NotifyChange at the setter of what it depends on, for this example it's "level1". So I change my code to this

class TestVm {
var level1: String? = null
@NotifyChange("level2") // It will work perfectly if I change it to @NotifyChange("level2", "level2Options")
set(value) {
field = value
level2 = null
}

var level2: String? = null

@Immutable
fun getLevel1Options(): List<String> = listOf("A", "B", "C")

@DependsOn("level1") // This line has no effect, I can remove it
fun getLevel2Options(): List<String> = when (level1) {
null -> emptyList()
else -> (1..3).map { "${level1}-${it}" }
}
}


Here it does not work like before. When level1 is changed, it only notify change of level2. But not for "getLevel2Options()". It's obvious that the @DependsOn("level1") annotated to "getLevel2Options()" is being ignored!!!

Base on my guessing, I try and am successful on solving this problem by changing the annotation at setter of level1 from @NotifyChange("level2") to @NotifyChange("level2", "level2Options"). Of course, I can remove @DependsOn("level1") from "getLevel2Options()" without affecting anything.

But I cannot afford to apply this solution in all situation. It is not practical to let the setter of level1 know every expression that want to depend on it. Also, it's hard to maintain.

[Here Question 1]

I wonder why ZK does not just, when level1 is changed, notify change of properties both those that specified using @NotifyChange and those that specified using @DependsOn.

I may just don't know it much enough or miss some important point. If it's the case, please lighten me up on this.

There's one important thing I forgot. While nested property using dot-notation is possible in EL expression, it is not supported in @NotifyChange and @DependsOn. So this does not work (now Java)

ZUL

<label value="@load(vm.family.father.name)"/>


VM

@NotifyChange("family.father.name")
@Command
public void assignFatherName(String name) {
this.family.father.name = name
}


I cannot just put the long property path in @NotifyChange and wait for change to appear in the label component. What I have to do is

@Command
public void assignFatherName(String name) {
this.family.father.name = name
BindUtils.postNotifyChange(null, null, this.family.father, "name");
}


It seems there's a good reason that dot-notation is not supported in @NotifyChange and @DependsOn (Please correct me if I'm wrong). If it does support, it would reduce a lot of boiler plate code.

Another finding

When I have this in my VM

var test2: String? = null
@DependsOn("test1") get


And I do this in VM

BindUtils.postNotifyChange(null, null, this, "test1")


All component that bind to "test1" will get updated. But not components that bind to "test2".

[Here Question 2]

Should I expect that the change notifications be chained based on dependency defined in @DependsOn ?

delete retag edit
Be the first one to answer this question!
[hide preview]