0

@NotifyChange make all related @DependsOn be ignored

asked 2017-04-06 08:03:31 +0800

trapongx gravatar image trapongx
32 3

updated 2017-04-06 10:22:19 +0800

[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
        <combobox selectedItem="@bind(vm.level1)" model="@load(vm.level1Options)"/>
        LEVEL 2
        <combobox selectedItem="@bind(vm.level2)" model="@load(vm.level2Options)"/>
    </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.

Thank you in advance for everybody's kind answers.

=====Update more information while waiting for the answer to question above=====

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 flag offensive retag edit
Be the first one to answer this question!
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

[hide preview]

Question tools

Follow
1 follower

RSS

Stats

Asked: 2017-04-06 08:03:31 +0800

Seen: 30 times

Last updated: Apr 06 '17

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More