Thymeleaf: handling maybe-existing properties of a model? - spring-mvc

Problem:
I have a Thymeleaf template in Spring MVC which I want to use for two things:
1) For creating a new entity in the database
2) For updating an existing entity in the database
There is only one tiny difference between those two use cases: The entity in the database has a version (optimistic locking) attribute set, so I want to remember this version as a hidden field in the template, which is easy to do.
To understand better where i am coming from here is my handler code for creating a new entity:
#GetMapping("/new")
String showMapFormForAdd( Model model ) {
model.addAttribute( "map", null ); // PASS null into it
return "map-form"; // same template
}
And here is the code for updating an existing entity:
#GetMapping("/{mapId}/update")
public String showMapFormForUpdate( #PathVariable Long mapId, Model model ) {
GameMap map = mapService.findMap( mapId );
model.addAttribute( "map", map ); // PASS the real map into it (with version!!!)
return "map-form"; // same template
}
And here is the code for the hidden version field in the Thymeleaf template:
<input type="hidden" th:value="${map?.version}" name="version" />
However, when the template is rendered after the first handler (creating), I get an Exception, that the property version does not exist (its true, it doesn't exist).
Question:
How can I tell Thymeleaf, to only query the version and set it to value in the hidden field, only if the property version does exist in the "map" model which is injected into Thymeleaf?

You can add conditional th:value
<input type="hidden" th:value="${map.version != null ? map.version : ''}" name="version" />

Related

Trim White Spaces From Beginning and End of Form Fields in React-JsonSchema-Form

I'm using react-jsonschema-form 1.2.1 to build a form based on a JsonSchema (v7). I want to automatically trim leading and trailing white spaces from certain text box input fields when a user presses submit on the form. Since the form is completely rendered by the <Form> element of the react-jsonschema-form module, I don't know how I can do this with JavaScript code.
Does react-jsonschema-form have trim capabilities?
Thanks in advance!
There a few different ways to do this, none as simple as flag to trim all string data though.
You can define a custom widget and use the uiSchema to designate specific fields to use this widget. This widget can then trim the value before using the native onChange function to notify the form that its value was changed, refer to: https://react-jsonschema-form.readthedocs.io/en/latest/advanced-customization/#custom-widget-components
You can define your own TextWidget (reserved name, refer to: https://github.com/mozilla-services/react-jsonschema-form/blob/master/src/components/widgets/TextWidget.js => https://github.com/mozilla-services/react-jsonschema-form/blob/master/src/components/widgets/BaseInput.js) and then use this TextWidget to replace all string-type fields:
const myWidgetOverrides = { TextWidget };
render() {
return (
<Form schema={schema}
widgets={this.myWidgetOverrides}
/>
);
}
You can override the validate function inside a new class component that extends the React JSONSchema Form class:
class TrimmedStringForm extends Form {
validate(formData, schema) {
formData = trimAllStrings(formData);
return super.validate(formData, schema);
}
}
or define your own validation function (refer to:
https://react-jsonschema-form.readthedocs.io/en/latest/validation/#custom-validation) to trim all/specific string-type fields from formData before it is passed into the submit function.

How To Update Database Using Angular Material Reactive Form And Restful API

Let Me take famous Book Example To explain My question.
I have a angular material reactive form build based on Book Model in my BookService.ts. When I change some field In this form and Submit it to my back end to update according record using Angular HttpClient PUT method It is not updating my database and comes back with and error. When I debug it, It shows ID is not defined, When I console.log(BookForm.value) I get This out put: {$key: 1234, Title: "Book Title1", Topic: "Topic1"} , No need to say my Angular HttpClient PUT Restful API needs that ID in order to be able to update that particular record in My Database table. Bellow Is My Simplified Mocked Code To Explain It.
BookModel.ts File, My Model
export interface Book{
ID: number;
Title: string;
Topic: string;
}
BookService.ts File, My Service
BookForm: FormGroup = new FormGroup({
$key: new FormControl(null),
Title: new FormControl('', Validators.required),
Topic: new FormControl('', Validators.required),
});
UpdateBook(bookObj: Book): Observable<Book>
{
return this.http.put<Book>(`...api/book/${bookObj.ID}`, bookObj,{
headers: new HttpHeaders({
'Content-Type: 'application/json'
})
})
}
Note: This Throw Error, ID Undefined
Book-form.component.html File
<form [formGroup] = "BookService.BookForm" class="FormCls">
<mat-grid-list rowHeight="200px">
<mat-grid-tile>
<div class="form-controles-container">
<input type="hidden" formControlName="$key" />
<mat-form-field>
<input formControlName="Title" matInput placeholder="Title*" />
<mat-error>Title Needed</mat-error>
</mat-form-field>
<mat-form-field>
<input formControlName="Topic" matInput placeholder="Topic*" />
<mat-error>Topic Needed</mat-error>
</mat-form-field>
<div class="button-row">
<button mat-raised-button color="primary" type="submit" (click)="onSubmit()">Submit</button>
</div>
</div>
</mat-grid-tile>
</mat-grid-list>
</form>
Book-form.component.ts File
onSubmit(): void
{
BookService.UpdateBook(BookService.BookForm.value).subscribe(
b => alert(`Book Updated Successfully`),
err => alert(`Exception While Updating: ${err}`)
);
}
For sure I know I need to some how convert my form value to my Book model and make sure I have that ID inside that before I pass it to my http put service. but I dont know how to do that, Im fairly new in both Angular and Typescript world and I am learning. I love reading before I ask, so went thru lots of Articles but none worked for me. For Example I tried below Article on stackoverfelow but did not work for me
Reactive Forms correctly convert Form Value to Model Object
I really Appreciate you professionals and Thank For Your Time And Help.
when you call any AbstractControl's .value method, you are gettin an object with prop: value pairs for each input in your form. As you can see, you will get an IDless object, maybe the reason is because the form has no 'ID' property, or maybe it is because the value is null (when you transform an object to JSON, all properties that have null value get deleted).
If you know the id from the HTML, you can pass it in the function
<button mat-raised-button color="primary" type="submit" (click)="onSubmit(sth.id)">
if that's not the case (I think it isn't), you could add the ID before calling your service with the .patchValue({ id: idInfo }) method that any AbstractControl has (in this case, you won't need to add a hidden input, unless you add it for another reason).
Do a patch before calling the service and it should work.
I'm not sure if this is enough info to help you, feel free to ask any questions.
edit (to add a code example):
onSubmit(): void
{
// the lines I added
const bf = BookService.BookForm;
bf.patchValue({ ID: 'anyIdYouLike' });
// what stays the same (except the bf.value shortened version)
BookService.UpdateBook(bf.value).subscribe(
b => alert(`Book Updated Successfully`),
err => alert(`Exception While Updating: ${err}`)
);
}

use the same form for create and read operation

I have a master and a child component. The child component persists the data for the create mode as well as the edit mode. The child has a data section as follows which is being used when the component is in create mode
data() {
return {
title: '',
description: '',
organizer: '',
startdate: '',
enddate: '',
email: '',
phone: ''
}
},
and my inputs in create mode are as follows
<input type="text" placeholder="enter event title here" class="form-control" v-model="title">
In the edit mode, I am updating a prop value on the client as follows, which is
props:['currentevent']
The value of the currentevent is being passed from the master component to the child component and is also the value that is currently being edited.
so, the complete code for handling an input value looks like as follows
<input type="text" placeholder="enter event title here" class="form-control" v-if="currentevent" :value="currentevent.title">
<input type="text" placeholder="enter event title here" class="form-control" v-else v-model="title">
and in my save method (in the child component), I am checking if currentevent is empty or not. If it is empty then I trigger the add code otherwise, I trigger the update code.
Question : This works , but I have a large form and having to do this for each and every component is not a clean design . Can you please let me know what should I be doing ?
I totally appreciate your predicament. The best way to handle form data is to make it create/update agnostic. Here's what I'd recommend you try:
Instead of maintaining all the data fields as disparate properties, contain them in a single object, in this case I'm calling it eventObj for clarity:
data () {
return {
eventObj: {}
}
}
Then in your markup you'd reference them via the object:
<input type="text" placeholder="..." class="form-control" v-model="eventObj.title">
You'd then need to define a prop for passing in the data (as an object) from the parent component if you are editing:
props: {
currentevent: Object
}
And then you'd just need to map the incoming prop to the child component's data:
created() {
Object.assign(this.eventObj, this.currentevent || {})
}
Now when your inputs like <input v-model="eventObj.title"> are processed, if there is a saved title (that was passed in with currentevent) the field will be prepopulated with it, otherwise it will be blank.
I think this should help you in the right direction toward solving the complexity you're trying to figure out. There are other logistical issues involved with this kind of stuff in general, but I won't drone on. :)
The issue I see is you want to remove the v-if/else in the form. I will recommend here is keep your local data of child to be in sync with the props passed and only use local variable in the form.
One way to do this can be put a watcher on props and whenever props changes, update local variables and only use those variables in form.
watch: {
currentevent: function(newVal){
title = newVal.title,\
description = newVal.description
...
}
}

Knockout.js "options" binding not updating in my Durandal app

I'm building a Durandal app, and the view I'm currently working on has two <select> boxes. I've got both of them bound to a ko.observableArray and their value to another ko.observable as follows:
<select data-bind="options: dateOptions, optionsText: 'display', value: selectedDate></select>
<select data-bind="options: buyerOptions, optionsText: 'display', value: slectedBuyer"></select>
The second one is dependent on the value of the first one, so I'm populating them at different times. For the first, I'm querying my data source during the activate() call and then passing the data to a separate method to populate the array the data (in the form of simple JS objects) when the promise returned by the request is resolved:
var populateDateOptions = function(dates) {
$.each(dates, function() {
dateOptions.push({
display: dateToString(this.pbDateOpt),
date: this.pbDateOpt
});
});
};
That works fine - the <select> has values ready for me when the view is rendered. But, after that, I can't get either <select> to respond to changes in their respective observable arrays. The next <select> is populated in a nearly-identical fashion once a value is selected in the first <select>, and I can verify that the buyerOptions array is indeed being populated, but the <select> doesn't change. I also tried adding a value to the first <select> by pushing an object into its array via dev tools and get the same result: the array is updated, but the UI doesn't change.
I've used the "options" binding in several other apps and never had this issue before, but this is my first Durandal app so I'm wondering if maybe there's something I'm missing?
Thanks!
Update
I added some more bound elements to the view and none of them are working, so there must be something weird going on with the composer. Not sure where to start looking for an issue (the viewmodel and view are very similar in structure to another pair that is working fine). Any tips?
Just as a reference, this isn't a Durandal issue - this is a Knockout issue.
Also, a way that I have found most efficient to do this is the following the same way you have it -
<select data-bind="options: dateOptions, optionsText: 'display', value: selectedDate></select>
<select data-bind="options: buyerOptions, optionsText: 'display', value: selectedBuyer"></select>
but in your view model make the buyerOptions dependent directly on the dateOptions like so -
var buyerOptions = ko.computed(function () {
var opts = ko.observableArray();
if (!selectedDate()) { opts.push(display: '<Select a Date>'); }
else { opts(getBuyersOptions(selectedDate().id()); }
return opts;
});
This way if your selectedDate observable is empty (one hasn't been selected) then no buyerOptions appear, but if you select a date it will populate it based off some value in selectedDate, like Id. It will also automatically update whenever a new date is chosen, without you having to explicitly tell it to with JavaScript or w/e
Turning on Durandal's debug mode by setting system.debug(true) in main.js helped me discover some code errors that weren't presenting themselves via console warnings. With those resolved, everything bound/worked correctly.
Lesson learned - leave debug mode on when you're in development!

How do I bind queried data to a Django form in a view and render it in template?

This has been driving me nuts and I can't get it to work. It's a simple scenario: I want to present the user with a dynamically generated list of users that I pull from the database so the user can select one of the users from a ChoiceField (rendered as an HTML select) and click a "Get Data" button.
I don't want to create a list of the users in the form because new users will come along all the time. I want to query for the users in my view and bind that list of users to the form so I can always render the latest list of users. So here's my form:
class ViewUsersForm(forms.Form):
user_choice = forms.ChoiceField()
And here's my view:
from django.contrib.auth.models import User
from mystuff.forms import ViewUsersForm
def site_admin_view_user_groups_page(request):
if request.method == 'POST':
<...handling POST for this question is unimportant...>
else:
users = User.objects.all()
data = {'user_choice': users}
form = ViewUsersForm(data) # binding the list of users here
variables = RequestContext(request, {'form': form,})
return render_to_response('school/admin/view_user_groups_page.html', variables)
And finally, here's the template I'm rendering to:
<form action="/site/viewusers/" method="post">{% csrf_token %}
{{ form.user_choice }}
<button type="submit" name="getDataButton">Get Data</button>
</form>
So when the template renders the bound form (form = ViewUsersForm(data)), the ChoiceField comes up empty. How do I get the data in the ChoiceField to render in my HTML??? What really simple thing am I missing here? Or am I going about this the completely wrong way?
The ChoiceField takes 'An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this field.' So you would need to setup your choices as ((value1, 'name1'),(value2, 'name2'),())
https://docs.djangoproject.com/en/dev/ref/forms/fields/#django.forms.ChoiceField
You could also use the ModelChoiceField to use your queryset as-is
https://docs.djangoproject.com/en/dev/ref/forms/fields/#django.forms.ModelChoiceField

Resources