Hello again, @user2022!
Originally Posted by
user2022
I guess that the "ViewModel(true)" setting allow the databinding working within the form context but not with the store data, the label hide/show properly if I use: "{!chkIsActive.checked}", but it not work if I define the store property there "{!IsActive}".
And yes, it can't simply guess what's to track. If you do
ViewModel(true)
you enable tracking of references within that context, just how adding
.Reference("chkIsActive")
to the checkbox promptly makes it able to be queried.
Without specifying anything to the store, it will be... well... a store! So you need to, somehow, add a reference to its "IsActive" state. Now another problem is raised: a store can contain various records, how should even I know how it should say "IsActive" is true? I suppose it's the loaded record in the form which, well, would just be record loaded in the form, then.
Anyway, to simplify our first iteration, I'll just consider that the first record (if any) in the store, is to give the form its "is checked" state to show the label.
So I'll add another label to the form: the existing one will reflect the checkbox state in the form itself, the other shall be displayed if and only if the first record in the store has its
IsActive
field as
true
.
Then, in summary:
- add the store to the viewmodel, so it becomes aware of the store as its tracked data (instead of
true
, the
ViewModel()
will receive an object setting it up)
- add a
Formula to fetch the 1st record
IsActive
state as the store changes
- add the extra label, rewording the labels to be clearer which one is displayed in which situation
With that, extra formatting, and stripping the need to a layout from your general test case, it became:
@{
Layout = null;
var X = Html.X();
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Component State - Ext.Net Examples</title>
<script type="text/javascript">
Ext.onReady(function () {
App.storeData.insert(0, {
Code: '001',
Name: 'company 001',
IsActive: true
});
App.storeData.commitChanges();
App.frmEdit.loadRecord(App.storeData.getAt(0));
});
const controller = {
event: {
onUpdateData: function () {
App.frmEdit.updateRecord();
console.log('updating data: ', App.frmEdit.getRecord().getData());
}
}
}
</script>
</head>
<body>
<div>
@Html.X().ResourceManager()
<h1>Component State</h1>
<p>This example shows how to use basic data binding to bind the state of one component to the state of another.</p>
@(
X.Store()
.ID("storeData")
.Model(X.Model()
.Fields(f =>
{
f.Add(new string[] { "Code", "Name" });
f.Add("IsActive", ModelFieldType.Boolean);
})
)
)
@{
var storeBindingModel = new
{
stores = new
{
storeData = JRawValue.From("App.storeData")
},
formulas = new
{
firstRecordActive = new
{
bind = new
{
bindTo = "{storeData}",
deep = true
},
get = JRawValue.From("function (store) { return store.count() > 0 ? store.getAt(0).data.IsActive : false; }")
}
}
};
}
@(
X.FormPanel()
.ID("frmEdit")
.Height(800)
.Margin(5)
.Layout(LayoutType.Border)
.Padding(0)
.Frame(true)
.StyleSpec("background-color: #fff;")
.ViewModel(storeBindingModel)
.FieldDefaults(fd =>
{
fd.LabelAlign = LabelAlign.Left;
fd.MsgTarget = MessageTarget.Qtip;
fd.LabelWidth = 150;
})
.Items(
X.FieldSet().ColumnWidth(1).Items(
X.DisplayField().DataIndex("Code").FieldLabel("Code"),
X.TextField().Name("Name").DataIndex("Name").FieldLabel("Name"),
X.Checkbox().InputValue("true").UncheckedValue("false").Name("IsActive").DataIndex("IsActive").Reference("chkIsActive"),
X.Label().Html("The loaded record in the form has <pre style=display:inline;>IsActive</pre> as <pre style=display:inline;>true</pre>!").Bind(c => c.Add(new Parameter("hidden", "{!chkIsActive.checked}"))),
X.Label().Html("<br />The store's first record has <pre style=display:inline;>IsActive</pre> as <pre style=display:inline;>true</pre>!").Bind(c => c.Add(new Parameter("hidden", "{!firstRecordActive}"))) )
)
.TopBarItem(
X.Button().ID("cmdSave").Text("Save").OnClientClick("controller.event.onUpdateData()"
)
)
)
</div>
</body>
</html>
So:
- In
lines 54 thru 74 I set up the
ViewModel
config object, in which
-
lines 57-60 binds it to the external store (it is necessarily
wrapped in a stores
property)
-
Lines 61-72 defines a "formula" to do whatever math is necessary to retrieve the status we want to consider while checking for the second label's
hidden
state
-
Line 97 adds the additional label, checking the formula result in real-time
So when you play the example, the labels follows the states as they are updated (as long as the only record in the store is the displayed one).
You can use two-way binding to the
App.frmEdit
's
Code field to pass it as a filter to the formula / store query, as to retrieve the exact loaded record while checking how to fill its true/false result.
A remark about all this would be, in case the grid has several records, any change to the grid will trigger re-evaluation of the formula, and this can become cumbersome. If performance becomes a concern, syncing the state via usual
events (
Listeners
,
DirectEvents
) would be the way to go. Store binding usually is more meaningful when any data change is likely to cause changes to the bound page.
You can also link specific records (and update them) directly in the
ViewModel (
see documentation on Ext.app.ViewModel here). But again, you're already binding the record to the form itself, so perhaps best (performance-wise) in case that label is to be shown when data is saved, would be to simply change its state when the form is loaded (contents changed) -and- when the
save button is clicked.
The "performatic" event-based solution in the paragraph above would miss, though, if something else in the same page could change the store data (a button to reload the store, maybe, pulling changed data from the server). But then, that very change probably should trigger an event to sync the data loaded in form and ensure its data (like, the name, or if the code still exists) are up-to-date; and this same event would also update the
IsActive
label in the form.
Notwithstanding, if the store data is displayed in a grid, you could also bind the form contents to the selection of the grid, thus checking the form could automatically propagate the change to the store through the grid. See
a complete example of this functionality here; it also binds the data with charts in the page.
One way or another, I hope this helps you develop your view with the best options.