If you GET /user/save you'll get back HTML and `<script>` to build the form.
If you POST /user/save you're expected to pass the entire form data PLUS an "operation" parameter which is used by the backend to decide what should be done and returned.
For example if user clicks [add new user] button, the "operation" parameter has value of "btnSubmit.click".
Why pass operation parameter? Because business forms can have more than just a [submit] button.
For example, there might be a datagrid filter value being changed (operation: "txtFilter.change"), or perhaps a dropdown search to select a city name from a large list (operation: "textCitySearch.change"), it can be a postal code to address lookup (operation: "txtPostalCode.change"), etc.
On the backend, the pseudocode looks somewhat like this but it's cleaner/safer because of encapsulation, validation, error handling, data sanitization, model binding and csrf/xss protection:
function user_save($operation) {
$form = new Form('/user/save');
$form->add($textName = new component(...));
$form->add($textCitySearch = new component(...));
$form->add($btnSubmit = new component(...));
if (method == "GET") return $form->getHtml();
try {
if ($operation == "btnSubmit.click") {
$newUser = UserService.createNewUser($_POST);
return '<script>' . makeJavaScriptSuccessDialog('New user created!') . '</script>';
}
if ($operation == "textCitySearch.change") {
$foundCities = UserService.searchCities($_POST);
return '<script>' . $textCitySearch->getJsToReplaceResultsWith($foundCities) . '</script>';
}
} catch ($exception){
// Services above throw ValidationException() for incorrect input, $form takes that and generates friendly HTML for users in a centralized way
if ($exception is ValidationException) {
return '<script>' . $form->getValidationErrorJs($exception) . '</script>';
}
// code below is actually done by a middleware elsewhere that catches unhandled exceptions,
// but i put it here for brevity in this example.
logSystemException($exception);
return '<script>' . makeJavaScriptErrorDialog('Ops, something went wrong with us. We will fix it!') . '</script>';
}
So the HTML generation and form processing for user creation is handled by a single HTTP endpoint and the code is very straight-forward. The locality of behaviour is off the charts and I don't need 10 template fragments for each form because everything is component based.