Symfony2: manually submit a form without class via AJAX - javascript

We have an old website where I have implemented a form that is sent by AngularJS to a PHP script and after processing an email message get sent. If the form is not valid the PHP script returns a JSON with the validation errors. Since we already use Symfony for some other applications (REST APIs), I thought it would be nice to reimplement my plain PHP script in Symfony.
For the sake of simplicity I put only a small but relevant fragment of my code. This is what I have:
HTML (ng-app is bound on body tag, not shown here):
<form name="infoscreenForm" class="form-horizontal" enctype="multipart/form-data" ng-controller="FormController">
<div class="form-group">
<div class="col-lg-1 control-label">*</div>
<div class="col-lg-11 input-group">
<input type="text" class="form-control" id="contact_person"
name="contact_person" ng-model="formData.contactPerson"
placeholder="Kontaktperson">
</div>
<span class="text-warning" ng-show="errors.contactPerson">
{{ errors.contactPerson }}
</span>
</div>
<div class="form-group">
<div class="col-lg-1 control-label">*</div>
<div class="col-lg-11 input-group">
<span class="input-group-addon">#</span>
<input type="email" class="form-control" id="email" name="email"
ng-model="formData.email" placeholder="E-Mail">
</div>
<span class="text-warning" ng-show="errors.email">
{{ errors.email }}
</span>
</div>
<div class="form-group">
<div class="col-lg-1 control-label"> </div>
<div class="col-lg-11 input-group">
<input type="file" class="form-control" id="file" name="file"
file-model="formData.file"
accept="application/pdf,image/jpeg,image/png">
</div>
<span class="text-warning" ng-show="errors.file">
{{ errors.file }}
</span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default" id="submit"
name="submit" ng-click="submitForm()">
Formular absenden
</button>
</div>
</form>
JS:
var app = angular.module('InfoscreenApp', []);
app.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function () {
scope.$apply(function () {
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
app.factory('multipartForm', ['$http', function ($http) {
return {
post : function (uploadUrl, data) {
var fd = new FormData();
for (var key in data) {
fd.append(key, data[key]);
}
return $http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers : { 'Content-Type': undefined }
});
}
};
}]);
app.controller('FormController', ['$scope', 'multipartForm', function ($scope, multipartForm) {
$scope.formData = {};
$scope.submitForm = function () {
var uploadUrl = 'http://localhost:8000/infoscreen';
multipartForm.post(uploadUrl, $scope.formData)
.then(function (data) {
console.log(data);
if (data.success) {
$scope.message = data.data.message;
console.log(data.data.message);
} else {
$scope.errors = data.data.errors;
}
});
};
}]);
With the plain PHP script everything works fine. Here is what I tried to do in Symfony:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Email;
class DefaultController extends Controller
{
/**
* #Route("/infoscreen", name="infoscreen")
*/
public function infoscreenAction(Request $request)
{
$defaultData = array('message' => 'infoscreenForm');
$form = $this->createFormBuilder($defaultData)
->add('contactPerson', TextType::class, array(
'constraints' => array(
new NotBlank(),
)
))
->add('email', EmailType::class, array(
'constraints' => array(
new NotBlank(),
new Email(),
)
))
->add('file', FileType::class)
->add('submit', SubmitType::class)
->getForm();
;
$form->submit($request->request->get($form->getName()));
$data = $form->getData();
if ($form->isValid()) {
echo 'Alles ok';
// send an email
}
$errors = array();
$validation = $this->get('validator')->validate($form);
foreach ($validation as $error) {
$errors[$error->getPropertyPath()] = $error->getMessage();
}
$response = new Response();
$response->setContent(json_encode(array(
'form_data' => $data,
'errors' => $errors,
)));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
CSRF is disabled in config.yml. The form is not bound to an entity class. After submitting the form I get the following object in the console:
{
data: Object,
status: 200,
config: Object,
statusText: "OK"
}
The important part is in data: Object:
{
form_data: {
contactPerson: null,
email: null,
message: "infoscreenForm",
file: null
},
errors : {
children[contactPerson].data = "This value should not be blank",
children[email].data = "This value should not be blank"
}
}
This happens when I submit the form with some values entered in the fields. It seems that the submitted data is not bound to the form in the controller. I'm probably missing something, but I stuck here and have no idea how to proceed. I tried with $form->bind($request), $form->handleRequest($request) and few other things, but it didn't work. Even if I bind the fields individually, I still don't get their values in the form.
Can somebody please help me.
Thanks in advance.

Try
$this->get('form.factory')->createNamedBuilder(null, 'form', $defaultData)
instead of
$this->createFormBuilder($defaultData)

Related

After Filling all Fields laravel sending 'required' error

I am trying to submit a form using axios post request in laravel. In this form i have 3 fields, name,age and a file called image.
here is the form
<form action="{{route('forms.store')}}" method="post" enctype="multipart/form-data">
#{{name}}
#csrf
<span v-if="errors.name">#{{errors.name[0]}}</span>
<label for="name">Name:</label>
<input type="text" name="name" id="name" v-model="name">
<span v-if="errors.age">#{{errors.age[0]}}</span>
<label for="age">Age:</label>
<input type="text" name="age" id="age" v-model="age">
<label for="image">Image:</label>
<span v-if="errors.image">#{{errors.image[0]}}</span>
<input type="file" name="image" id="image" #change="imageChanged">
<button #click.prevent="submitForm">Submit</button>
</form>
Here is my vueJs code:
const app = new Vue({
el: '#app',
data:{
name:'',
age:'',
image:'',
errors:{}
},
methods:{
imageChanged(e){
app.image = e.target.files[0]
console.log(e.target.files[0]);
},
submitForm(){
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
const fd = new FormData(this.$data);
fd.append('image',this.image);
axios.post('{{route('forms.store')}}',this.fd,config).then((response)=>{
console.log(response.data);
}).catch((error)=>{
//console.log(error.response.data);
this.errors = error.response.data.errors;
})
}
}
});
And here is my controller
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
'age' => 'required',
]);
if ($request->hasFile('image')) {
$image = $request->file('image');
return $ext = $image->extension();
} else {
return "NOT OK";
}
}
So here I am validating name and age. But my problem is when I fill the form and submit the form,
It sends back errors that name and age field is required.
where am I doing wrong and how to receive this data in the controller.
Thank you in advance.
I believe your code is in error in this part. changes:
axios.post('{{route('forms.store')}}',this.fd,config)
to:
axios.post('{{route('forms.store')}}',fd,config)

how to post data to server using angularJS, web api

i am new to angularJS and trying to figure out, how to post data to my server, problem is that my post to server is successful and it create new record but all is empty(null).
Thanks for any advice
JS:
$scope.addChannel = function () {
var channels = [];
//var newChannel = {
// Name: $scope.Name
//};
//clearing error message
$scope.errorMessage = "";
$http.post("api/Channel/channels", newChannel)
.success(function (newChannel) {
//on success
$scope.channels.push({ "Name": $scope.Name });
console.log("data added");
// newChannel.push(response.data);
newChannel = {};
}, function () {
//on failure
$scope.errorMessage = "Failed to save data";
})
}
HTML:
<div ng-controller="ChannelController">
<div class="col-md-4 col-lg-4 col-sm-4">
<form novalidate name="newUser"ng-submit="addChannel()">
<div class="form-group">
<label for="name">Channel</label>
<input class="form-control" type="text" id="Name" name="Name" ng-model="newChannel.Name" />
</div>
<div class="form-group">
<input type="submit" value="Add" class="btn btn-success" />
</div>
</form>
<a ng-href="/Dashboard#/channels">Return to dashboard</a>
</div>
<div class="has-error" ng-show="errorMessage">
{{errorMessage}}
</div>
Channel Controller
[HttpPost("channels")]
public async System.Threading.Tasks.Task Create(Channel channel)
{
await _channelRepository.CreateChannel(channel);
}
Repository
public async System.Threading.Tasks.Task CreateChannel(Channel channel)
{
_context.Channel.Add(channel);
await _context.SaveChangesAsync();
}
Check if you name properties correctly in object that you send to server, property names are case sensitive. Looks like you have in js
var customer = {name: 'foo', lastName: 'bar'};
and on server you try to deserialize your JSON to this entity
class Customer {
public Name {get;set;} //note prop names are capitalized
public LastName {get;set;}
}

AngularJS how to put data and id when user send form

I would like to send my api date and newsId together with data from form.
This is my form:
<div class="well">
<h4>Leave a Comment:</h4>
<form role="form" ng-submit="createComm(newComment)" novalidate>
<div class="form-group">
<input type="text" class="form-control" placeholder="Autor" ng-model="newComment.author">
</div>
<div class="form-group">
<textarea class="form-control" ng-model="newComment.comment" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
Controller:
.controller('CommentController',
function($scope, $routeParams, NewsModel){
var newsId = $routeParams.id;
path = 'getCommetnsByNewsId/'+newsId;
var comm = this;
var data = new Date().toLocaleString();
//comm.newComm.data = data; //this way?
$scope.createComm = function(comment){
NewsModel.create(comment).then(function (result){
initCreateComm();
})
}
function initCreateComm(){
comm.newComm = { comment: '', author: '', data: '', id: ''};
}
})
and service
service.createComm = function(comm){
return $http.post(getUrl(),comm);
}
How can I add to this code to send data and newsId? I do not want to keep data and id as html input.
You could send it after form submitting on create comment request with angular.extend:
$scope.createComm = function(comment){
NewsModel.create(angular.extend({}, {data: data, newsId: newsId}, comment)).then(function (result){
initCreateComm();
})
}

SilverStripe submit HTML form through Ajax

I want to pass data from a simple HTML form to a controller through Ajax, then process the data and return a response back.
At the moment I have the following:
HomePage.ss
<form method="POST" class="form-horizontal submit-form" onsubmit="return checkform(this);">
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="name">Name</label>
<div class="col-md-8">
<input id="name" name="name" type="text" placeholder="insert full Name" class="form-control input-md" required="" />
</div>
</div>
<!-- Button -->
<div class="form-group">
<label class="col-md-4 control-label" for="send-btn"></label>
<div class="col-md-8">
<button id="send-btn" name="send-btn" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
JavaScript
$('form.submit-form').submit(function() {
$.ajax({
type: 'POST',
url: 'processForm',
data: $(this).serialize(),
success: function(data) {
alert('data received');
}
});
});
HomePage.php
class HomePage_Controller extends Page_Controller {
public function events() {
$events = CalendarEvent::get();
return $events;
}
public function processForm() {
if (Director::is_ajax()) {
echo 'ajax received';
} else {
//return $this->httpError(404);
return 'not ajax';
}
}
}
In developer tools I can see that I got the xhr processForm with a 404 not found error.
How do I get this Ajax form working correctly with the SilverStripe controller?
Spider,
I've done something similar to below. This is a quick and dirty demo and hasn't been tested, but it may get you going in the right path. If you're unfamiliar with how forms work within SilverStripe there is a lesson for front end forms in SilverStripe. I've found the lessons useful personally and provide the code for the lesson as well: http://www.silverstripe.org/learn/lessons/introduction-to-frontend-forms?ref=hub
Page.php
<?php
class Page extends SiteTree
{
}
class Page_Controller extends Content_Controller
{
private static $allowed_actions = array(
'MyForm',
);
public function MyForm()
{
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.min.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-validate/jquery.validate.min.js');
Requirements::javascript('/path/to/your/validation/script.js');
$fields = FieldList::create(
TextField::create('name')
->setTitle('Name')
);
$actions = FieldList::create(
FormAction::create('doSubmit')
->setTitle('Submit')
);
$requiredFields = RequiredFields::create(
'name'
);
$form = Form::create($this, 'MyForm', $fields, $actions, $requiredFields);
return $form;
}
public function doSubmit($data, $form)
{
//process $data or create your new object and simpley $form->saveInto($yourObject); then $yourObject->write()
//then deal with ajax stuff
if ($this->request->isAjax()) {
return $this->customise(array(
'YourTemplateVar' => 'Your Value'
))->renderWith('YourIncludeFile');
} else {
//this would be if it wasn't an ajax request, generally a redirect to success/failure page
}
}
}
YourValidationScript.js
(function ($) {
$(function () {
$('#MyForm_Form').validate({
submitHandler: function (form) {
$.ajax({
type: $(form).attr('method'),
url: $(form).attr('action') + "?isAjax=1",
data: $(form).serialize()
})
.done(function (response) {
$('.content').html(response);
})
.fail(function (xhr) {
alert('Error: ' + xhr.responseText);
});
},
rules: {
name: "required"
}
});
})
})(jQuery);
You need to understand how HTTP request routing is handled in SilverStripe.
When you send request POST /processForm, it is treated as page and managed by ModelAsController. That is why you get 404 error - there is no SiteTree record with URLSegment = processForm.
Solution 1
Use Form object. It creates all routing configuration automatically during runtime. Read more
https://docs.silverstripe.org/en/3.3/tutorials/forms/
https://docs.silverstripe.org/en/3.3/developer_guides/forms/
Solution 2
Use this approach, when you really want to go down to the simple one method request handler. Register custom controller and routing.
You specify your route in mysite/_config/routing.yml
---
Name: siteroutes
---
Director:
rules:
processCustomForm: CustomFormController
Handle your request
class CustomFormController extends Controller
{
public function handleRequest( SS_HTTPRequest $request, DataModel $model ) {
if (!$request->isPost()) {
// handle invalid request
}
$name = $request->postVar('name')
// process your form
}
}

Edit MEANJS list in the list page

I am using MEAN JS, i am trying to edit the list items on the list page, but it shows the error as below. i have initiated the data using ng-init="find()" for the list and ng-init="findOne()" for individual data.
Error: [$resource:badcfg] Error in resource configuration for action `get`. Expected response to contain an object but got an array
HTML
Below i the form inside the controller where it initiates the find() and findOne().
<div ng-controller="OrdersController" ng-init="find()">
<div>
<div class="order-filter">
<div ng-repeat="order in orders">
<form ng-init="findOne()" name="orderForm" class="form-horizontal" ng-submit="update(orderForm.$valid)" novalidate>
<input type="text" class="" ng-model="order.title">
<input type="text" class="" ng-model="order.content">
<div class="form-group">
<input type="submit" value="Update" class="btn btn-default">
</div>
</form>
</div>
</div>
</div>
</div>
Controller
$scope.update = function (isValid) {
$scope.error = null;
if (!isValid) {
$scope.$broadcast('show-errors-check-validity', 'orderForm');
return false;
}
var order = $scope.order;
order.$update(function () {
$location.path('orders/' + order._id);
}, function (errorResponse) {
$scope.error = errorResponse.data.message;
});
};
$scope.find = function () {
Orders.query(function loadedOrders(orders) {
orders.forEach(appendFood);
$scope.orders = orders;
});
};
$scope.findOne = function () {
$scope.order = Orders.get({
orderId: $stateParams.orderId
});
};
You need to check your Orders Service which probably is using $resource to provide your API requests (Orders.query)
It should look something like this:
function OrdersService($resource) {
return $resource('api/orders/:orderId', {
orderId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}
The style may be different depending on which version of mean you're using. By default, the $resource query will expect an array of results, but if for some reason you've set "isArray" to false then it will expect an object.
https://docs.angularjs.org/api/ngResource/service/$resource

Categories

Resources