How to bind properties with the collection Using OdataModel in SAPUI5 - odata

Hello Guys please i need your help.
Using a Mockserver i'm abel to get access zu OData like this way
var oModel = this.getOwnerComponent().getModel("oServiceModel");
var sReservationPath = "/Book_Regal(Bibliothek='BE1',ReservationNr='4162')/Reserve_Book";
I bind my Form like this:
var formA = this.getView().byId("FormA");
formA.setModel(oModel, "Bibliothek");
formA.bindElement({path: "/Reserve_Book(Bibliothek='BE1',ReservationNr='4162')", model: "Bibliothek"});
My MockData(BookReserved.json) looks like this:
[
{
"results": [
{
"__metadata": {
"id": "http://MyWebService/Reserve_Book(BibliothekName='BE1',ReservationNr='4162')",
"uri": "http://MyWebService/Reserve_Book(BibliothekName='BE1',ReservationNr='4162')",
"type": "infos.Reserve_BookType"
},
"iScomfirmed": true,
"BibliothekName": "BE1",
"ReservationNr": "4162",
"BookType": "3021",
"BookCategory": "2"
}
]
}
]
My SimpleForm looks like this
<Input value="{Bibliothek/results[0]/>BibliothekName}" editable="false">
</Input>
What do i wrong ? Please can you help me ?

Assuming your mock server works there are two errors.
The binding syntax is {MODEL_NAME>PATH} (if the name of the model is undefined or '' you can omit the first part and just use {PATH}).
In your case MODEL_NAME is Bibliothek. /results[0]/ is part of a path and not the model name so it can never appear before the > symbol.
Then when dealing with OData you will never use array indices (like [0]). OData entries have a proper key which has to be used (and you did already use it when calling bindElement).
Your form is then connected to your binding path. All elements below the form also have access to this information and can use it by setting a relative binding path so your code should look like
<Input value="{Bibliothek>BibliothekName}">
Just some examples which are all basically the same
Hardcoded absolute binding of element
This only works if the entity has been already loaded in another request
<form:SimpleForm>
<Input value="{Bibliothek>/Reserve_Book(Bibliothek='BE1',ReservationNr='4162')/BibliothekName}" />
<form:SimpleForm />
Hardcoded absolute binding of parent, relativ binding of child
This can trigger it's own request if the entity hasn't been already loaded
<form:SimpleForm binding="{Bibliothek>/Reserve_Book(Bibliothek='BE1',ReservationNr='4162')}">
<Input value="{Bibliothek>BibliothekName}" />
<form:SimpleForm />
Dynamic absolute binding of parent (in JS), relativ binding of child
This behaves the same as the second example
this.byId("form").bindElement({path: "/Reserve_Book(Bibliothek='BE1',ReservationNr='4162')", model: "Bibliothek"});
<form:SimpleForm id="form">
<Input value="{Bibliothek>BibliothekName}" />
<form:SimpleForm />

Related

New to React: Why is one array treated differently than the other?

I'm working on a React app that is fed data from a Rails api. I'm currently working on a form that includes a nested association (i.e. in the model_a has many model_b's and you can create them in the same form).
The problem I'm having is that Rails expects nested association with a certain naming convention and the same field that controls how the parameter is named when its sent to rails also controls how React finds the right data when the Rails API responds.
This becomes problematic on the edit page because I want to show the models_a's (Retailers) already existing model_b's (SpendingThresholds in this case) and when I change the 'name' field to suit the rails side, React doesn't know where to look for that data anymore. When I try to pass the data directly it comes in as a different type of array and certain functions fail.
I think its easier to show than tell here so
initially I had this
<FieldArray
name="spending_thresholds"
component={renderSpendingThresholds}
/>
and data was coming through like
Object {_isFieldArray: true, forEach: function, get: function, getAll: function, insert: function…
to my React app from the Rails API, which worked, however that 'name' isn't to Rails liking (Rails wants it to be called 'spending_thresholds_attributes' for accepts_nested_attributes to work) so I changed it to
<FieldArray
name="spending_thresholds_attributes"
fields={this.props.retailer.spending_thresholds}
component={renderSpendingThresholds}
/>
and data start coming through to the renderSpendingThresholds component in this format
[Object]
0:Object
length:1
__proto__:Array(0)
which React doesn't like for some reason.
Anyone know how to fix this/why those two objects, which hold the same information from the Rails side anyway, are being treated differently?
EDITS
renderSpendingThresholds component
The fields attribute in the renderSpendingThresholds component is the object that's coming through differently depending on how I input it
const renderSpendingThresholds = ({ fields }) => (
<ul className="spending-thresholds">
<li>
<Button size="sm" color="secondary" onClick={(e) => {
fields.push({});
e.preventDefault();
}
}>
Add Spending Threshold
</Button>
</li>
{fields.map((spending_threshold, index) => (
<li key={index}>
<h4>Spending Threshold #{index + 1}</h4>
<Button
size="sm"
color="danger"
title="Remove Spending Threshold"
onClick={() => fields.remove(index)}
>
Remove
</Button>
<Field
name={`${spending_threshold}.spend_amount`}
type="number"
component={renderField}
label="Spend Amount"
placeholder="0"
/>
<Field
name={`${spending_threshold}.bonus_credits`}
type="number"
component={renderField}
label="Bonus Credits"
placeholder="0"
/>
</li>
))}
</ul>
);
It looks like you are passing fields through props and then destructuring the fields out of the props in the callback of the renderSpendingThresholds and discarding the rest. According to the docs, a specific redux-form object is passed through to the render callback. You're essentially overwriting this. Try changing {field} to something like member or spending_threshold. Then you can use the specific map function to iterate over the spending_threshold items. Your field prop should still be available under member.fields or something similar.
For the code that you currently show, who exactly handles the submission?
you use the original flow of form submit?
if so, so please handle that by yourself.
** this line of code, looks weird:
onClick={() => fields.remove(index)}
as you interact directly with the state values...
you need to update the state through
this.setState({fields: FIELDS_WITHOUT_ITEM})
and now when you need to handle your own submission, you don't really care of the input names. Because you are using the state as input.
ie:
class FormSpending extends Component {
handleSubmit() {
var fieldsData = this.state.fields.map(field => {
return {
whateverkey: field.dontcare,
otherKey: field.anotherDontCare
};
});
var formData = {
fields: fieldsData
};
ajaxLibrary.post(URL_HERE, formData).....
}
render() {
return (
...
<form onSubmit={()=>this.handleSubmit()}>
...
</form>
...
);
}
}

Binding to a Dictionary<string, string> where the Key contains square brackets

I am trying to bind items that belong to a Dictionary and it all works fine, until I run into KeyValuePairs where the Key contains square brackets. For example, imagine this contrived scenario:
model = new MyModel {
MyDict = new Dictionary<string, string> {
{ "Apple[1]", "Green" }
}
}
I then create a form as follows:
Html.EditorFor(model => model.MyDict["Apple[1]"])
As expected, the field looks like this:
<input id="MyDict_Apple_1__" name="MyDict[Apple[1]]"> type="text" value="Green">
The problem is that when the form is submitted, the ModelBinder drops the last bracket: ModelBindingContext.ModelName is "MyDict[Apple[1]".
Is this a bug in the DefaultModelBinder, or am I missing something?
Update:
I found that there's a bug in how FormValueProvider reads keys from the data. Specifically, the bug is in the GetKeyFromEmptyPrefix method of Microsoft.AspNetCore.Mvc.Internal.PrefixContainer.
For now I'm working around the issue by using the old binding syntax, with separate fields for the Key and Value:
<input type="hidden" name="MyDict[0].Key" value="Apple[1]" />
<input type="text" name="MyDict[0].Value" value="Green" />

how to pass params using action button in grails

been having toruble with the button that has action. I have several btns which I want to know its paramaeter. In grails tutorial it says it should be like this:
<g:actionSubmit action="action" value="${message(code: 'default.button.edit.label', default: 'Edit')}" params="['actionTaken':editPhone]"/>
I tried using remotelink, submitButton, submitToRemote tags but none works. I always get null when I try parsing it in my controller:
def action=
{
def actionTaken = params.actionTaken
def employeeId= params.employeeId
MySession session = MySession.getMySession(request, params.employeeId)
profileInstance = session.profileInstance
switch(actionTaken)
{
case "editPhone" :
isEditPhone=true
break
case "editEmail" :
isEditEmail=true
break
}
render(view:"profile", model:[profileInstance:session.profileInstance, isEditPhone:isEditPhone, isEditEmail:isEditEmail])
}
What am I missing? is my params code wrong? Is my code in parsing params wrong? this just gets me in circles with no progress. help. thanks.
The Grails documentation doesn't list params as one of the attributes accepted by actionSubmit.
It is possible to inject the value you want in your params list in the controller by exploiting what that tag actually does:
def editPhone = { forward(action:'action', params:[actionTaken: 'editPhone'])}
def editEmail = { forward(action:'action', params:[actionTaken: 'editEmail'])}
You may also just want to just code completely separate logic into the editPhone and editEmail actions if that makes your code cleaner.
Updated View Code:
<g:actionSubmit action="editPhone" value="${message(code: 'default.button.edit.label', default: 'Edit')}" />
The params attribute is parsed as a map, where the keys are treated a strings but the values are treated as expressions. Therefore
params="['actionTaken':editPhone]"
is trying to define the key named actionTaken to have the value from the variable named editPhone in the GSP model. Since there is no such variable you're getting null. So the fix is to move the quotes:
params="[actionTaken:'editPhone']"
which will set the value to the string "editPhone".
You could also pass the parameter inside the POST-data using
<input type="hidden" name="actionTaken" value="editPhone" />
inside the form. Then it is also accessible through the params variable.
It works for me.
I just had a similar problem (I needed to submit a delete together with an id) and found a solution using HTML5's "formaction" attribute for submit-inputs.
They can be given a value that can include a controller, action, additional parameters, etc.
In General, to add a parameter to a submit button such as a edit of a specific sub-object on a form would looks like this:
<input type="submit" formaction="/<controller>/<action>/<id>?additionalParam1=...&additionalParam2=..." value="Action" >
and in your example:
<input type="submit" formaction="action?actionTaken=editPhone" value="${message(code: 'default.button.edit.label', default: 'Edit')}" >
In my situation, I had a single form element with multiple actions on each row of a table (i.e. data table edit buttons). It was important to send the entire form data so I couldn't just use links. The quickest way I found to inject a parameter was with javascript. Not ideal, but it works:
function injectHiddenField(name, value) {
var control = $("input[type=hidden][name='" + name + "']");
if (control.size() == 0) {
console.log(name + " not found; adding...");
control = $("<input type=\"hidden\" id=\"" + name + "\" name=\"" + name + "\">");
$("form").append(control);
}
control.val(value);
}
Your button can look like this:
<g:each in="${objects}" var="object">
...
<g:actionSubmit value="edit" action="anotherAction"
onclick="injectHiddenField('myfield', ${object.id})"/>
</g:each>

a list of checkboxes

I have two domain classes
class Contract {
String number
static hasMany = [statements:Statement]
}
class Statement {
String code
static hasMany = [contracts:Contract]
}
I would like to show all statements available in my gsp with a checkbox next to each, allowing the user to choose which statements are applicable to the contract. So something like:
[ ] Statement Code 1
[ ] Statement Code 2
[ ] Statement Code 3
I started off with this:
<g:each in="${Statement.list()}" var="statement" status="i">
<g:checkBox name="statements[${i}].id" value="${statement.id}" checked="${contractInstance.statements.contains(statement.id)}" />
<label for="statements[${i}]">${statement.code}</label>
</g:each>
But i just cannot get a list of checked statements to the controller (there are null elements in the list, there are repeated statements...).
Any idea how to achieve this?
This is possible, but it does require a bit of a hack. First off, every checkbox must have the same name, "statements":
<g:each in="${org.example.Statement.list(sort: 'id', order: 'asc')}" var="statement" status="i">
<g:checkBox name="statements" value="${statement.id}" checked="${contract.statements.contains(statement)}" />
<label for="statements">${statement.content}</label>
</g:each>
Second, in the controller you have to remove the "_statements" property before binding:
def contract = Contract.get(params.id)
params.remove "_statements"
bindData contract, params
contract.save(failOnError: true)
The check box support hasn't been designed for this use case, hence the need for a hack. The multi-select list box is the one typically used for this type of scenario.
I personally prefer to get the list of Id's in this case.
<g:each var="book" in="${books}">
<g:checkBox name="bookIds" value="${book.id}" ...
</g:each>
Command Object:
class BookCommand {
List<Serializable> bookIds
}
In controller action:
BookCommand bc ->
author.books = Book.getAll(bc.bookIds)
Change the checkbox to something like this.
<g:checkBox name="statements.${statement.id}" value="true" checked="${contractInstance.statements.contains(statement)?:''}" />
and then in the controller, in params.statements you will get a list with the IDs of the checked statements.
Also notice the ?:'' in the checked property, it's a good idea to add it because any value(even 'false') in the checked property is interpreted as checked.
Are you mapping request directly to Contract? It's much more secure to map incoming request into an Command object.
As about mapping a list - values are mapped only to existing elements. I mean it cannot create new list elements. You need to prepare it before mapping. If you know that there is always 3 elements, you can make:
class ContractCommand {
List statements = [
new Statement(),
new Statement(),
new Statement(),
]
}
and map request to this object

Grails: checkbox not being set back to false

I am developing a Grails (1.0.4) app where I want to edit a collection of collections on a single page in a grid view. I got it to work quite well depending only on the indexed parameter handling of Spring MVC, except for one thing:
boolean (or, for that matter, Boolean) values in the grid can be set via checkbox, but not unset, i.e. when I check the checkbox and update, the value is set to true, but afterwards when I edit again, uncheck the checkbox and update, it remains true.
This is the GSP code of the checkbox:
<g:checkBox name="tage[${indexTag}].zuweisungen[${indexMitarb}].fixiert" value="${z.fixiert}" />
And this is the HTML that is generated:
<input type="hidden" name="tage[0].zuweisungen[0]._fixiert" />
<input type="checkbox" name="tage[0].zuweisungen[0].fixiert" checked="checked" id="tage[0].zuweisungen[0].fixiert" />
I've found a Grails bug that describes exactly this effect, but it's marked as fixed in 1.0.2, and the problem mechanism described there (underscore in hidden field name is put in the wrong place) is not present in my case.
Any ideas what could be the reason?
This is the solution a guy named Julius Huang proposed on the grails-user mailing list. It's reusable but relies on JavaScript to populate a hidden field with the "false" response for an unchecked checkbox that HTML unfortunately does not send.
I hack GSP to send "false" when
uncheck the box (true -> false) with
custom TagLib.
By default checkBox send nothing when
uncheck, so I use the checkBox as
event handler but send hidden field
instead.
"params" in Controller can handle
"false" -> "true" without any
modification. eg. Everything remain
same in Controller.
The Custom Tag Usage in GSP (sample usedfunc_F is "true"),
<jh:checkBox name="surveyList[${i}].usedfunc_F" value="${survey.usedfunc_F}"></jh:checkBox>
Here is what the Tag generate,
<input type="hidden" name="surveyList[#{i}].usedfunc_F" id="surveyList[#{i}].usedfunc_F" value="false" />
<input type="checkbox" onclick="jhtoggle('surveyList[#{i}].usedfunc_F')" checked="checked" />
The Javascript
<script type="text/javascript">
function jhtoggle(obj) {
var jht = document.getElementById(obj);
jht.value = (jht.value !='true' ? 'true' : 'false');
}
</script>
This is my own solution, basically a workaround that manually does what the grails data binding should be doing (but doesn't):
Map<String,String> checkboxes = params.findAll{def i = it.key.endsWith("._fixiert")} // all checkboxes
checkboxes.each{
String key = it.key.substring(0, it.key.indexOf("._fixiert"))
int tagIdx = Integer.parseInt(key.substring(key.indexOf('[')+1, key.indexOf(']')))
int zuwIdx = Integer.parseInt(key.substring(key.lastIndexOf('[')+1, key.lastIndexOf(']')))
if(params.get(key+".fixiert"))
{
dienstplanInstance.tage[tagIdx].zuweisungen[zuwIdx].fixiert = true
}
else
{
dienstplanInstance.tage[tagIdx].zuweisungen[zuwIdx].fixiert = false
}
}
Works, requires no change in grails itself, but isn't reusable (probably could be made so with some extra work).
I think that the simplest workaround would be to attach a debugger and see why Grails is failing to populate the value. Considering Grails is open source you'll be able to access the source code and once you figure out the solution for it you can patch your version.
I have also found this other bug GRAILS-2861 which mentions the issue related to binding to booleans (see Marc's comment in the thread). I guess that is exactly the problem you are describing.
I would create a small sample app that demonstrates the problem and attach it to the Grails bug (or create a new one). Someone here may be able to debug your sample app or you'll have shown the bug isn't really fixed.
Try this out, set the logs to DEBUG, frist try the first 3 if they don't show the problem up, flip them all to DEBUG:
codehaus.groovy.grails.web.servlet="error" // controllers
codehaus.groovy.grails.web.pages="error" // GSP
codehaus.groovy.grails.web.sitemesh="error" // layouts
codehaus.groovy.grails."web.mapping.filter"="error" // URL mapping
codehaus.groovy.grails."web.mapping"="error" // URL mapping
codehaus.groovy.grails.commons="info" // core / classloading
codehaus.groovy.grails.plugins="error" // plugins
codehaus.groovy.grails.orm.hibernate="error" // hibernate integration
This should allow you to see exactly when and how the parameters setting is failing and probably figure out a work around.

Resources