How to update 1:1 relations in Extbase - typo3

I have 2 models as below
class Communication extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
/**
* #var Filter
*/
protected $filter;
}
class Filter extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
/**
* #var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Domain\Model\FrontendUserGroup>
*/
protected $usergroup;
}
Template as below
<f:form class="form-horizontal" name="communication" action="update" object="{communication}">
<div class="form-group">
<label class="col-xs-3 col-md-2 control-label" for="bodytext">{f:translate(key: 'text.usergroup')}</label>
<div class="col-xs-9 col-md-10">
<f:for each="{usergroups}" as="group">
<label class="checkbox-inline">
<f:form.checkbox property="filter.usergroup" value="{group.uid}" /> {group.title}
</label>
</f:for>
</div>
</div>
<div class="form-group">
<div class="col-xs-9 col-md-10 col-xs-offset-3 col-md-offset-2">
<button type="submit" class="btn btn-primary">{f:translate(key: 'button.update')}</button>
</div>
</div>
</f:form>
When I try to update a Communication with $this->communicationRepository->update($communication), it always creates a new Filter rather than updating its own. Why this?

This is because there is no relation to the filter that is already there. For keeping the filter you need to say what id it has. I found out it works with an __identity field:
EDIT: it is probably better to wrap the field with an if condition in case there is no filter set
<f:if condition="{communication.filter.uid}">
<f:form.hidden name="communication[filter][__identity]" value="{communication.filter.uid}" />
</f:if>
Not sure, but it is possible that is also works with an uid property field:
<f:form.hidden property="filter.uid" />

Related

Thymeleaf form not submitting to Spring boot controller

I am trying to submit a form to Spring boot controller
This is the thymeleaf part:
<form th:action="#{/change_password}" method="post">
<div class="row">
<div class="col-md-9 register-right">
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<h3 class="register-heading">Change password</h3>
<div class="row register-form">
<div class="col-md-6">
<div class="form-group">
<input type="email" th:name="email" id="email" class="form-control" placeholder="Email *" >
<span th:if="${notPresent}" class="alert alert-info" style="color:red; width: 100% !important; border: none; background-color: transparent !important;">This email does not exist!</span>
</div>
<div class="form-group">
<input type="password" th:name="password" id="password" class="form-control" placeholder="Password *" >
</div>
<div class="form-group">
<input type="password" th:name="confirmPassword" id="confirmPassword" class="form-control" placeholder="Confirm *" >
</div>
</div>
<div class="col-md-6">
<input type="submit" class="btnRegister" style="background-color: #ffa600 !important;" value="Change"/>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
This is the Spring boot controller method:
#PostMapping("/change_password")
public String changeUserPassword(HttpServletRequest request, Model model) {
String path = "";
User u = userService.findByEmail(request.getParameter("email"));
if(u == null || u.getActive() == 0) {
model.addAttribute("notPresent", true);
path = "redirect:/forgot_password";
} else {
u.setPassword(bCryptPasswordEncoder.encode(request.getParameter("password")));
userService.updateUser(u);
sendEmail(u.getEmail(), u.getFirstname());
path = "redirect:/login";
}
return path;
}
I don't get any errors so I am not sure what's wrong.
To submit a form you can use an object userPassword define in a Class like this:
package ....
imports ....
#Data #AllArgsConstructor #NoArgsConstructor #ToString
public class UserPassword {
private Integer id;
private String password;
... other fields you need
}
You add this object to the controller displaying the page with :
model.addAttribute("UserPassword", userPassword);
Then you add this object to your form:
<form th:action="#{/change_password}" th:object="${userPassword}" method="post">
You need to bind data to this object in your html page (here I bind password from userPassword to the input tag):
<input type="password" th:field="*{password}" class="form-control" placeholder="Password *" >
And Finnaly I retrieve the object in the controller:
#PostMapping("/change_password")
public String changeUserPassword(Model model, #ModelAttribute UserPassword userPassword) {
.....
}
You can find a complete tutorial on part 7 from https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html

Using a Thymeleaf form to construct POST request

I'm having trouble passing form input to an attribute of a nested object (object.subobject.property). Specifically I have an Institution institution object which contains a Country country object which has an attribute called countryName, accessed like institution.country.getCountryName() but I cannot get the Model to populated correctly upon form submission. Below I have three print statements during the #PostMapping. That output looks like:
Institution(instId=398, instName=Alfred Wegener Institute for Polar and Marine Research, abbrvName=AWI, country=null, action=finish institution edit)
{modelinstitution=Institution(instId=398, instName=Alfred Wegener Institute for Polar and Marine Research, abbrvName=AWI, country=null, action=finish institution edit), org.springframework.validation.BindingResult.modelinstitution=org.springframework.validation.BeanPropertyBindingResult: 0 errors}
org.springframework.validation.BeanPropertyBindingResult: 0 errors
Notice how the values for country= are null in both the Model and the institutionAttributes object. I was expecting something more like
country=Country(countryName=United States, countryId=12, parentCountry=)
Why is the form not populating the Country object and how can I make it do so?
I have a form:
<form id="lead_form" data-toggle="validator" th:action="#{/institutions}" th:object="${institution}" method="post" enctype="application/x-www-form-urlencoded">
<input type="hidden" id="action" name="action" value="finish institution edit"/>
<input type="hidden" id="instId" name="instId" th:value="${institution.instId}"/>
<div class="row">
<div class="col-md-2 text-left">
<label for="instIdDisplay" class="control-label">Institution ID: </label>
<input class="form-control border-input" th:value="${institution.instId}" id="instIdDisplay" name="instIdDisplay" type="number" disabled="true"/>
</div>
<div class="col-md-5 text-left">
<label for="instName" class="control-label">Institution Name: </label>
<input class="form-control border-input" th:value="${institution.instName}" placeholder="Required" id="instName" name="instName" type="text" required="true"/>
</div>
<div class="col-md-2 text-left">
<label for="abbrvName" class="control-label">Abbreviation: </label>
<input class="form-control border-input" th:value="${institution.abbrvName}" placeholder="Optional" id="abbrvName" name="abbrvName" type="text"/>
</div>
<div class="col-md-3 text-left">
<label for="countryName" class="control-label">Country: </label>
<input class="form-control border-input" th:value="${institution.country.countryName}" placeholder="Required" id="countryName" name="countryName" type="text"/>
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-4" align="left">
<button class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">Save & Close</button>
</div>
<div class="col-md-4"/>
<div class="col-md-4" align="right">
<!--<input id="form_button" class="btn btn-lg btn-success" type="submit" value="Save And Close"/>-->
<a th:href="#{/institutions}" class="btn btn-lg btn-danger">Cancel</a>
</div>
</div>
</form>
which issues a POST request from the controller:
#PostMapping(value = "")
String institutionsEditPOST(
#ModelAttribute("modelinstitution") Institution institutionAttributes,
Model model,
BindingResult result) {
System.out.println(institutionAttributes);
System.out.println(model);
System.out.println(result);
String newAction;
String action = institutionAttributes.getAction();
System.out.println("ENTER INSTITUTION POST BEFORE ACTION: "+action);
String instName = institutionAttributes.getInstName();
String abbrvName = institutionAttributes.getAbbrvName();
String countryName = institutionAttributes.getCountry().getCountryName();
model.addAttribute("instName", instName);
model.addAttribute("abbrvName", abbrvName);
model.addAttribute("countryName", countryName);
newAction = "institutions";
return "forms/fragments/"+newAction;
}
Why your code is not working: You have name="countryName" but the countryName is not property of institution, but of country. You must provide path to the property: name="country.countryName". But instead I suggest to use the convenient th:field.
Try this:
<input class="form-control border-input" th:field="*{country.countryName}" placeholder="Required" id="countryName" type="text"/>
It should properly set both name and value attributes (and also id). It normally saves you from specifying (Thymeleaf) attributes for these individually.
The th:field value must a selection expression (*) relative to the object specified in th:object.

DCE Container Loop

I'm using the excellent DCE Extension and want to loop twice in the Container through the Child-DCEs using the fields of each Child.
In Pseudocode something like this:
Container Template:
<div id="foo">
<f:for each="{dces}" as="dce">
{dce.fields.title}
</f:for>
</div>
<div id="bar">
<f:for each="{dces}" as="dce">
{dce.fields.bla}
</f:for></div>
How can I do that?
I've solved the problem with a Viewhelper:
class DcevalViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
/**
* #param ArminVieweg\Dce\Domain\Model\Dce object
*
* #return array
*
*/
public function render($dce) {
$contentObject = $dce->getContentObject();
$temp = GeneralUtility::xml2array($contentObject['pi_flexform']);
$temp = $temp['data']['sheet.tabGeneral']['lDEF'];
foreach($temp as $key=>$val) {
preg_replace( "/\r|\n/", "", $val['vDEF'] );
$dcedata[substr($key,9)]=$val['vDEF'];
}
$dcedata['uid']=$contentObject['uid'];
return $dcedata;
}
}
And in the Containertemplate (no need for a Child template)
{namespace dce=ArminVieweg\Dce\ViewHelpers}
{namespace tom=Mediagmbh\Tomediavh\ViewHelpers}
<f:layout name="DefaultContainer" />
<f:section name="main">
<div id="foo">
<ul>
<f:for each="{dces}" as="dce">
<f:alias map="{field:'{tom:Dceval(dce:dce)}'}">
<li>{field.header}</li>
</f:alias>
</f:for>
</ul>
</div
<div id="bar">
<f:for each="{dces}" as="dce">
<f:alias map="{field:'{tom:Dceval(dce:dce)}'}">
<div><f:format.raw>{field.text}</f:format.raw></div>
</f:alias>
</f:for>
</div>
</div>
</f:section>
In DCE container definition, just use
<div id="foo">
<f:for each="{dces}" as="dce">
{dce.get.title}
</f:for>
</div>
<div id="bar">
<f:for each="{dces}" as="dce">
{dce.get.bla}
</f:for>
</div>
and nothing at all in template (2nd tab of DCE defintion).

Laravel 5.4 view <form> not POSTing all of form inputs to the respective controller

So I've hit a bit of a wall - I'm working on a Laravel 5.4 application and I want to POST form data from a view that I have created to its respective Controller. I've tested, and the data is getting sent to the right place, but out of all the different inputs (lesson_module_id, title, and description), only the "title" input is sending back the actual form input - all the others are "null" in the array that is passed back.
Maybe I've just spent too long looking at this and my brain is fried, but does anyone see what I'm missing? P.S., I've made sure the route is correct and the $request is actually going through!
Here's my HTML/Blade from the view:
<form role="form" method="POST" action="{{ route('lessons.store') }}">
{{ csrf_field() }}
<div class="form-group {{ $errors->has('lesson_module_id') ? 'has-error' : '' }}">
<label for="lesson_module_id">Lesson Module</label>
<select id="lesson_module_id" class="form-control" required>
<option name="lesson_module_id" selected disabled>Please select a lesson module</option>
#foreach ($lesson_modules as $lm)
<option value="{{ $lm->id }}">{{ $lm->title }}</option>
#endforeach
</select>
#if ($errors->has('lesson_module_id'))
<span class="help-block">
<strong>{{ $errors->first('lesson_module_id') }}</strong>
</span>
#endif
</div>
<div class="form-group {{ $errors->has('title') ? ' has-error' : '' }}">
<label for="title">Title</label>
<input id="title" type="text" class="form-control" name="title" value="{{ old('title') }}" required autofocus>
#if ($errors->has('title'))
<span class="help-block">
<strong>{{ $errors->first('title') }}</strong>
</span>
#endif
</div>
<div class="form-group {{ $errors->has('description') ? ' has-error' : '' }}">
<label for="description">Description</label>
<textarea id="description" class="body-textarea medium-editor-textarea" rows="15" required></textarea>
#if ($errors->has('description'))
<span class="help-block">
<strong>{{ $errors->first('description') }}</strong>
</span>
#endif
</div>
<div class="form-group">
<div class="col-md-12 text-center">
<input class="btn btn-primary" name="Submit" type="submit" value="Create Module">
</div>
</div>
</form>
And here's my controller store logic:
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$lesson = new Lesson;
$lesson->lesson_module_id = $request->lesson_module_id;
$lesson->title = $request->title;
$lesson->description = $request->description;
$lesson->save();
}
Thanks in advance for the help!
you forgot the name attributes in your htm for both of them.

Angular 2 JS dropdown, select item from another component

I have two components one is employee and another is department. I want to make a dropdown so that employee can select a department.
I implemented a method for getting all departments inside my employee component:
getAllDepartment(){
this._employeeService.getAllDepartment()
.subscribe(data => this.departments = data.json());
}
in my employee html select looks like this:
<label for="editTitle">Department:</label><br>
<select [(ngModel)]="newItem.Department" (click)="getAll()" style="width: 180px">
<option *ngFor="#department of departments" [value]="department.departmentNo">{{department.departmentName}}</option>
</select>
but noting happens.
What is the best way for make this dropdown work and select items?
This is ahtml where I have select:
<form #f="ngForm" [hidden]="!showAddView" align="center">
<div>
<label for="editTitle">Employee No:</label><br>
<input type="text" [(ngModel)]="newItem.employeeNo" ngControl="employeeNo" required >
</div>
<div>
<label for="editAbrv">Employee name:</label><br>
<input type="text" [(ngModel)]="newItem.employeeName" ngControl="demployeeName" required >
</div>
<div>
<label for="editTitle">Department:</label><br>
<select [(ngModel)]="newItem.departmentNo" (click)="getAll()" style="width: 180px">
<option *ngFor="#department of departments" [value]="department.departmentNo">{{department.departmentName}}</option>
</select>
</div>
<div>
<label for="editAbrv">Salary:</label><br>
<input type="text" [(ngModel)]="newItem.salary" ngControl="salary" required>
</div>
<div>
<a href="javascript:void(0);" (click)="addEmployee(newItem)" title="Add">
Save
</a>
<a href="javascript:void(0);" (click)="showHide($event)" >
Cancel
</a>
</div>
</form>
You are binding the click event of the select box to a getAll() method, but the method that you have defined is called getAllDepartment().
You need to bind the event to an existing method name.
That being said, I'm unsure why you would want to reload the departments every time the dropdown is clicked. If the desired behaviour is to load the departments once when the component is initialized, you should add a constructor() method to the component, which will be called automatically.
constructor() {
this._employeeService.getAllDepartment()
.subscribe(data => this.departments = data.json());
}