Reputation: 3331
I have a rather advanced TabPanel
Wizard that I'm working on, and I can't seem to get it to behave properly. Basically, I have 4 tabs... each tab is its own form, as the tab needs to do validation on itself, so it can determine if it needs to disable the tabs to its right, or if the next tab should be enabled.
The main quirk that I'm running into is that a non-rendered tab thinks it's valid, when in fact, its field is set to allowBlank: false
, and it has an empty value. Then there's the inverse, where I have a value set, but it think it's invalid.
The other quirk is, I must call this.getViewModel().notify()
when the tab panel loads... otherwise, my activated form thinks it's invalid when I check the initial validity... although, I believe this has something to do with binding and the delay, so I can deal with this for right now.
I can use deferredRender: false
to solve this problem, but I don't want to because in my actual app, the tabs have quite a bit going on in each of them, so that's not very performant.
In my Fiddle, you'll see 3 tabs enabled, when in fact, it should've been 4 tabs that were enabled, as the 3rd tab has data from its model, but the tab believe it's not valid. Because the tab thinks it's invalid, it re-fires my checkValidity method because I check for isValid being false (this is to disable any tab to the right if the current tab goes invalid, and the user is forced to hit continue, which is by design). In the console:
Does anyone have any insight? Am I going about this terribly wrong?
Upvotes: 1
Views: 191
Reputation: 2206
This is happening because view models, and binding, don't do what you think they do.
First - when you create a field like this:
{ fieldLabel: 'Value3', xtype: 'textfield', name: 'value2', allowBlank: false,
bind: { value: '{model1.value2}' }
}
the field is created initially with no value. That's because you haven't defined any value - you've bound it. And the binding doesn't take affect immediately. For performance reasons, it won't normally be bound until the tab is rendered (which is why not deferring the rendering works for you)
The net result of this is that when you check the validity of Tab 3, it fails because the values of not been bound yet. You can see this more clearly if you change the log statement on line 24 to be this:
console.log(form.owner.title, form.getValues(), form.owner.rendered, isValid)
With this change, when you first render the tab panel, you get this output (sans comments):
afterrender // triggers the view model to notify.
activate
checking
tab1 Object {value: "blah"} true true // Value bound because it was rendered.
tab2 Object {value: ""} false true
tab3 Object {value2: ""} false false // Value not bound because it is not rendered
checking
tab4 Object {} false true // No properties yet...
Note that if you don't call the this.getViewModel().notify()
, then you get this output:
afterrender
activate
checking
tab2 Object {} false true
tab3 Object {} false true
tab4 Object {} false true
tab1 Object {value: "blah"} true true
Note the different orders.
So what's going on here? Well, firstly - the individual forms on the tabs have no data until the field values are bound in. With no fields, they are considered valid.
When you call isValid, it forces the fields to be defined - but they still aren't bound. So they have no value, and in the case of tab3, that makes it invalid. Because tab3 is invalid (no data bound yet), tab 4 is not enabled.
If you don't call the viewModel.notify()
, then it is tab1 that has no data bound yet during the first call to checkValidity()
. Thus, it is not valid (it has fields, but no values), and tab2, etc, are not enabled.
The validity is all eventually sorted out - but you only check the state when changing from true to false - not false to true. Which gives the behaviour that you see.
How to fix this? There's probably a few ways. Probably the most effective is to get the values out of the viewModel during initComponent for the panel, and assign them explicitly to the fields when making them. This way they are created with the correct state initially, and thus pass the validity check.
Upvotes: 2