Why do I have to declare DataContext when binding to relative source - mvvm

When I bind I normally just enter the name of the property which exists in the DataContext
EG
Text = {Binding MyProp}"
When I use relative source, I have to use DataContext
Text = "{Binding RelativeSource={RelativeSource AncestorLevel=1, AncestorType={x:Type UserControl}, Mode=FindAncestor}, Path=DataContext.MyProp }" />
I must be missing some basic understanding as I don't see why I need to include the word DataContext in the path: Path=DataContext.MyProp, I had assumed it has implicit. If I remove DataContext from the string, then it won't bind.

RelativeSource will change the target to, in this instance, UserControl. This way you can bind to elements on the UserControl, such as its Width/Height/etc. In these cases having an implicit DataContext would be counter-productive.

Related

Correct Syntax of Element binding in XML view with JSON model

Trying to get right syntax for context binding in XML view. I have a JSON model and set the model to view with name "company" inside controller. When I use absolute path, it works but when I use relative path, it doesn't. It seems, view is unable to access the model in second case.
My Code
<Text text ="{company>/data/name}" width="200px"/>
<Input binding="{company>/data}" value ="{name}" width="200px"/>
When binding a property, you also have to provide the name of the model. Otherwise the "nameless" default model is assumed. But since all your data is in the company model you have to explicitly state that name for your value.
<Input binding="{company>/data}" value="{company>name}" width="200px"/>

DataTriggerBehavior Binding to Property on ViewModel

I'm trying to bind a DataTriggerBehavior to a Property on my ViewModel, but it doesn't ever fire.
I've used DataTriggerBehaviors bound to various Properties of Controls with no trouble but can't get the VM binding to work.
DataContext is set to the VM.
I can see the binding value in debug but nothing triggers.
I've tested the InvokeCommandAction by changing the DataTriggerBehavior to an EventTriggerBehavior so that works fine.
<AppBarButton Icon="Library">
<i:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding HelpPhase}" ComparisonCondition="Equal" Value="Add" >
<core:InvokeCommandAction Command="{Binding DataContext.StoreRateCommand, ElementName=LayoutRoot}"/>
</core:DataTriggerBehavior>
</i:Interaction.Behaviors>
</AppBarButton>
In VM (inherits VMBase that implements IPCN)
Private mHelpPhase As String
Public Property HelpPhase() As String
Get
Return Settings.HelpPhase
End Get
Set(value As String)
SetProperty(Settings.HelpPhase, value)
End Set
End Property
The EventTriggerBehavior listens for a specific event on its source and executes an action when the event is fired. It is different from the DataTriggerBehavior.
The DataTriggerBehavior performs an action when the data the behaviors is bound to meets a specified condition. In your question, when the bound data of the HelpPhase's value change to "Add", the behavior triggers an action to fire the command.
You should be able check if you have bind the HelpPhase to the DataTriggerBehavior and set the "Add" to the HelpPhase. You can bind the HelpPhase to Text property of TextBlock if the TextBlock show "Add".
There is an official DataTriggerBehavior sample, please refer the XamlBehaviors sample.

Aggregation Binding to Child Collection

In an XML view I have a JSON model bound to the page with the name 'foo'. The model's object has a 'name' field and child collection 'bar' (that has a 'code' field) that I want to show in a list. This is modeled as such:
JS Code
var foo = { name:'My Name', bar:[{ code:'Code 1' }, { code:'Code 2' }] }
var fooModel = new sap.ui.model.JSONModel(foo);
page.setModel(fooModel, 'foo');
page.bindElement('foo>/');
XML Markup
<Label text='{foo>name}'/>
<List items='{foo>bar}'>
<StandardListItem title='{foo>code}'/>
</List>
Notice that the list item's values for the child 'bar' array are resolved via the name 'foo'. At least this works for me and I have found no other way to reference them in the list.
But the problem is how do I get the top-level 'foo' object data in my list-item also? Say I wanted to show the 'name' field also in the list items?
In other words, is there a way to do the equivalent of the following, where I can reference the child collection by a different name? Is there some way to achieve this?
<List items="{ path:'foo>bar', name='bar' }">
<StandardListItem title='{bar>code}' info='{foo>name}' />
</List>
You need to use an absolute path to bind the name property. Absolute means the complete path to the property within your model. The opposite is a relative binding path. Here you just use a property somewhere within your model and set a binding context to tell the runtime where your property is located within the model. You do this by using:
page.bindElement("foo>/");
Now the runtime will apply this information to all relative bindings against the model foo within this page. Therefore you can write foo>bar and the runtime automatically look up foo>/bar. However within the item aggregation this does not work, because the bar object does not have a property name. Therefore you need to use a absolute binding path to bind the property.
<List items="{foo>bar}">
<StandardListItem title='{foo>code}' info='{foo>/name}' />
</List>
You find an explanation of the binding syntax for JSONModel in the documenation.

Complex Binding in SAPUI5 XML View

I am trying to convert a string value to Boolean while binding it from a JSONModel. Ideally the value in my model is "true"/"false" and I want to bind it to the visible property of an item. The model is defined to be TwoWay binding but I guess that does not matter in this case
I have declared "complex binding" in the index.html.
data-sap-ui-xx-bindingSyntax="complex"
Then I create my XML view and bind the property from the model as below:
<P13nColumnsItem>
columnKey="{tableVariantAFModel>Fieldname}"
visible="{path:'tableVariantAFModel>Visible', type: 'sap.ui.model.type.Boolean', mode: 'sap.ui.model.BindingMode.TwoWay'}"
index="{tableVariantAFModel>DisplayOrder}">
<P13nColumnsItem>
When I run my app,it throws the below error:
Is there any step I am missing?
Also, I need to add this app to the Fiori Launchpad, so I need to define the complex binding in manifest.json file rather than in index.html . Where can I define it in the manifest file.
For simple use cases like this you can use an expression binding instead of implementing additional logic somewhere.
<P13nColumnsItem>
columnKey="{tableVariantAFModel>Fieldname}"
visible="{= ${tableVariantAFModel>Visible} === 'true'}"
index="{tableVariantAFModel>DisplayOrder}">
<P13nColumnsItem>
i would advise to use a formatter. See here. In the formatter you could write:
visible="{path:'tableVariantAFModel>Visible', formatter: '.formatter.stringToBoolean'}"
in the formatter you could create the function like:
stringToBoolean: function(_stringBoolean){
(_stringBoolean === "true") ? return true : return false;
}
You have to make sure that you instantiate the formatter in your controller, or optionally you could choose a function in your controller itself.

Checking size of collection that is a property of form bean in struts

I have a collection that is a property of Form Bean in struts.
I need to write a check on the size of that collection in jsp.
in C:IF or logic:equals, I didn't find how can I specify the form name and property and the size comparison.
Anybody good in struts.
You can either use the notEmpty tag from the logic tag-library (which is provided by the struts framework) just like #Uchenna suggested.
Or, you can directly use the c:if tag (with EL ${}) from JSTL, which is also used by the logic:notEmpty behind the scene.
Sample:
<!-- This resolves to false even if myCollection is null -->
<c:if test="${! empty myForm.myCollection}">
</c:if>
Or instead of !, you can also use the not keyword (or operator).
Docs:
logic:notEmpty
c:if
When you forward to a jsp, the form name will be on the scope of that jsp. So just reference the form name in the jsp and the jsp will see it. Let say the name you gave the form class in struts-config.xml is MyFormName and the list property name is myList:
<logic:notEmpty name='MyFormName' property='myList'>
</logic:notEmpty>
Let me know if this solves your issue.