handling an angular $http request in symfony controller - angularjs

i want to send data from a twig view to a symfony controller using angular js $http method
this is my javascript
<script>
var app = angular.module('app',[]);
app.controller('ctrl',function($scope,$http) {
$scope.processForm = function() {
var val = $scope.libelle;
$http({
method: "POST",
url: 'http://localhost/symangular/web/app_dev.php/home',
headers: {
'Content-Type': 'application/json'
},
data:{libelle:val}
}).then(function (html) {
console.log(html);
});
}
});
</script>
and this is my controller
class DefaultController extends Controller
{
public function indexAction(Request $request)
{
$prod = new Product();
$form = $this->createForm(ProductType::class,$prod);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid() && $request- >isMethod('POST') && $request->isXmlHttpRequest()){
$em = $this->getDoctrine()->getManager();
$data = json_decode($request->getContent(), true);
dump($request->request->get('libelle'));
$request->request->replace($data);
$prod->setLibelle($request->request->get('libelle'));
$em->persist($prod);
$em->flush();
return new JsonResponse("good");
}
return $this->render('angulartestangularBundle:Default:index.html.twig',array('form'=>$form->createView()));
}
}
so when i execute i got an object in the console that i didn't understand it also i got nothing in database , did anyone have an idea about how to handle an
$http angular request in symfony controller
Object {data: "<script src="https://ajax.googleapis.com/ajax/libs…: 5 } ); })();/*]]>*/</script>↵</body>↵", status: 200, config: Object, statusText: "OK"}

In Symfony, a request considered as a XmlHttpRequest by reading request headers. The exactly code in Symfony is:
public function isXmlHttpRequest()
{
return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
}
So, when using angularjs or any javascript framework to make a XMLHttpRequest, you should add a header with X-Requested-With key and value = XMLHttpRequest. This header also required even if you're using fetch api. In your code abow, the request call should be:
$http({
method: "POST",
url: 'http://localhost/symangular/web/app_dev.php/home',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
data:{libelle:val}
})
If you don't want to add this header every time call $http function, you can add it as default config to $httpProvider:
angular
.module('yourModuleName', [])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
}])
;
For more details about $http configs and how to set default configs, look at Angularjs Documentation

Your approach is a little confused, and therefore very hard to debug.
its good practice (IMO) to separate your api endpoints from the controller action that renders the page. Coupling them together is going to seriously limit the extensibility of your application.
Its possible that you're failing one part of the complex if statement in your controller action; this is making it hard to debug.
Its also good practice when writing an api to provide the client with an idea of what went wrong on a call. (even if its only you accessing the api)
Looking at your code, it also looks like your getting mixed up in processing the request. Your'e validating the submission based upon it being a Product, and asking symfony to process the data on that basis, but then inside your submitted if statement, pulling out the data again..
You can still harness the symfony form validation by decoupling it and submitting this way.
/**
* If your using annotated routing do this, or make the equivelent in your yaml file.
* #Route("/api/product/create", name="api_product_create")
*/
public function productAction(Request $request)
{
$data = json_decode($request->getContent(), true);
if(!$data)
{
// youll want to handle this exception with a listener or somesuch so that it sends back a nice neat message to your client
throw new InvalidArgumentException('Invalid json');
}
$product = new Product();
// create your form as you did before.
$form = $this->createForm(ProductType::class,$product);
// this tells symfony to fill out the form, as if it was submitted conventionally.
$form->submit($data);
// now we can check for valid (you dont need to check submitted, as valid will do that anyway)
if($form->isValid())
{
// persist the new object
$em = $this->getDoctrine()->getManager();
$em->persist($prod);
$em->flush();
// create a new set of return data
$return = [
'status' => 'success',
'productId' => $product->getId(),
];
return new JsonResponse($return, 201);
}
// oops, something went wrong, find out and report it
$return = [
'status' => 'error',
'uri' => $request->getPathInfo(),
'errors' => (string) $form->getErrors(true, false),
];
return new JsonResponse($return, 400);
}
then, in your template, render the url to the endpoint properly.
url: {{ path('api_product_create') }},
this is just an example, and may not totally fit your usecase, but as you can see, once decoupled its much easier to find out what went wrong and where.
Useful resources:
https://knpuniversity.com/screencast/symfony-rest2/validation-errors-test (whilst payed to see the video, the transcript is free)
http://symfony.com/doc/current/form/direct_submit.html

thank you all for ansewring my question.. the informations were so helpfull .. otherwise the solution was to seprate the two actions in symfony controller and adding a request header to the $http method and it works very well .. thank u all

Related

how to have angularJS post data to MVC controller which redirects to a view

I am posting some data to an MVC action method using AngularJS. This action method will either show its backing view or redirect to another page. Currently all that is happening is the data is getting posted but the redirect is not happening via MVC. I am getting this done using angular's window.location method. I want to know if there is a better way or if I need to post differently using Angular.
On page A I have angular scripts posting data to page B like below:
serviceDataFactory.POST('http://localhost:1234/home/B', someData, pageConfig).then(function () {
//on success
window.location = 'http://localhost:1234/home/Index';
},
function() {
//on error
window.location = 'http://localhost:1234/home/B';
});
This is my service factory
app.factory('serviceFactory', function($http, $q) {
var service = {};
//POST
service.POST = function (url, postData, conf) {
var d = $q.defer();
$http({
method: 'POST',
url: url,
data: postData,
config: conf
}).success(function(data) {
d.resolve(data);
}).error(function(error) {
d.reject(error);
});
return d.promise;
}
return service;
}
);
On Page B I want to redirect to another page. This is my page B in MVC
[HttpPost]
public ActionResult B(string someData)
{
//recieve string someData and perform some logic based on it
.
.
.
if(boolCondition)
return RedirectToAction("Index", "Home");
else
return View();
}
Here once Angular posts to the action method B, it executes all the code all the way till the if(boolCondition) statement. Since I am unable to have that redirect affected via MVC, I do that in Angular itself using the success or error block that the promise returns to.
I want to know if there is a better way to do this or if I am doing something wrong here or if this is the only acceptable way. How do I get angular to hand-off to the MVC action method and let further redirects continue from there only?
You should not use the .success() / .error() pattern with $http, because this has been deprecated. Instead, use then() with two arguments, the first argument being the success function and the second being the error function.
The $http legacy promise methods success and error have been
deprecated. Use the standard then method instead. If
$httpProvider.useLegacyPromiseExtensions is set to false then these
methods will throw $http/legacy error.
You do not need to promisify the result of $http, because $http returns a promise. Just return $http from your service.
app.factory('serviceFactory', function($http, $q) {
var service = {};
//POST
service.POST = function (url, postData, conf) {
return $http({
method: 'POST',
url: url,
data: postData,
config: conf
});
}
return service;
});
Your Page A controller will work the same as before with this new simplified code. At the server, be sure to emit a 500 http status code in cases where you want to trigger the
function() {
//on error
window.location = 'http://localhost:1234/home/B';
}
to run. The 500 in the headers of the response will cause the AngularJS promise to run the second function in your controller.

How to send data from angularjs to cakephp

I have all my actions done in cakephp. I was using a platform in client side, and now I decided to migrate to Ionic/angularjs, but as I saw, they do not "talk" with each other. Angular uses Content-Type: application/json that cakePHP does not read with request->data or request->query.
I found some workarounds that I need to change all the actions in my server side. Is there another way to keep it without changes? I´d like to keep it working in the old client version too.
[Edited]
In algularjs I´m using $http service.
I'm not sure that I understand your problem.
If you send json data to your server with a post request, you can get data with a cakephp application easily:
jsonData = {data: [{key: 'a', value: 'b'}, {key: 'a', value: 'b'}]};
var req = {
method: 'POST',
url: 'http://example.com/Tests',
data: jsonData
}
$http(req);
Then in your controller you can get data:
class MyController extends AppController{
// ...
public function action(){
debug($this->request->data);
// you have your data from your angularjs app
// ['data' => [['key' => 'a', value => 'b'], ['key' => 'a', value => 'b']]];
}
I solved the problem changing the $http header to "application/x-www-form-urlencoded", I saw this post
body = 'cel=xxxx&pass=zczx';
var req = {
method: 'POST',
url: 'http://localhost/cakefacility/usuarios/checkUsuario.json',
data: body,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
$http(req);
For Sending Data from angular to cakephp project you need to create REST API at cakephp project for creating REST API you can follow my forked github repo.
After Creating API in cakephp project you just call API like:
For Call API by GET:
$http.get("<REST API URL>").then(function(response) {
......
......
});
For Call API by POST:
$http.post(<REST API URL>, <DATA TO SEND>).success(function(data, status) {
.......
.......
});

Sending Array of Objects to Web API from AngualrJS

During this process, I grab an array of "vehicles" from the Web API. I modify and do whatever to each vehicle. Then I want to send the list back, without going through and looping...
I've tried a lot of the ways that i've looked up.
I've got this in the WEB API for a breakpoint to see if I can even get the array there, but I havent been able to yet.
public IHttpActionResult UpdateVehicles(Vehicle[] vehiclesArry)
{
return Ok();
}
I'm confused if I need to do a $post, or if I could just "get" it to the correct method like I've been doing. The problem is I can't get the array to the WEB API method.
I've got my $resource setup like this.
return $resource(appSettings.serverPath + "/api/Violators/:id",null,
{
'update': { method: 'PUT' },
'delete': { method: 'DELETE' },
'post': { method: 'POST' }
});
I've tried using $post, but it says the object doesn't support it. I'm not sure what other ways I can try. I've tried using "dynamic" in the web API, that doesn't seem to work either.
You're missing the params object for $resource, so it doesn't know the id.
return $resource(appSettings.serverPath + "/api/Violators/:id", { id: '#id' });
You don't need to explicitly setup methods for get, post, delete. That's already done for you. If your API uses PUT for update, set that up like this:
return $resource(appSettings.serverPath + "/api/Violators/:id", { id: '#id' }, {
update: { method: 'PUT' }
});
Also, the property on your resource must be vehiclesArry exactly or web API won't know how to map it. I also want to echo #sowen. You will need to setup a view model that your endpoint receives.
My assumption is that you are having some script errors in your page or you are not using the $http methods properly.
One problem people usually run into is using the correct url to the web api endpoints in your angular controller. If you don't get it right, you might be getting 404 errors. Look for those in your browser console(network tab)
The below code should work fine without any issues
$http.get("../api/Values/")
.then(function (res) {
var vehicles = res.data;
console.log('data received', JSON.stringify(vehicles));
//Let's update the Name of each vehicle.
$.each(vehicles, function (indx, item) {
item.Name = item.Name + " Updated";
});
console.log('data modified', JSON.stringify(vehicles));
//Let's make a call to web api with modified data
$http.post("../api/Values/UpdateVehicles", vehicles)
.then(function (res2) {
console.log('response', JSON.stringify(res2.data));
});
});
Assuming you have angular js properly loaded in your page and the above code is part of your angular controller for the current page and you have the Web api controller with 2 action methods like below example.
public class ValuesController : ApiController
{
[HttpPost]
[Route("api/Values/UpdateVehicles")]
public IHttpActionResult UpdateVehicles(Vehicle[] vehiclesArry)
{
// just returning whatever came in for TESTING PURPOSE
return Ok(vehiclesArry);
}
public IEnumerable<Vehicle> Get()
{
return new List<Vehicle>
{
new Vehicle {Id = 1, Name = "Car"},
new Vehicle {Id = 2, Name = "Van"}
};
}
}
Also,FYI : I am using Attribute routing in my api controller for the UpdateVehicle endpoint.
Create a model object like
public class UpdateReq
{
public IEnumerable<Vehicle> Vehicles { get; set; }
}
From your angular, just pass a json with an array
{
[v1, v2, v3]
}

angularjs PATCH method 404 response

WORK AROUND IS AT THE BOTTOM
Original problem
There are question like this all over the web and none of them really have answer for me. I can't get an http PATCH operation to work using angular to save my life. I've implemented $http, with shortcut $http.patch and without using the config object method:PATCH. I've used $resource by adding a custom method. And I've implemented Restangular using their patch and I'm getting the same error. I have the correct Content-Type as suggested in other posts. I think it's safe to say at this point, it's something I'm missing. I'm getting the same "404" message via postman when trying to patch. I can PUT, GET, POST, and DELETE, but not PATCH.
In the following images you can see that the resource exists for GET. But when trying to patch I get 404. Browsing to that endpoint shows the record. Which is stored in Mongodb.
Here's some code snippets:
Resangular GET:
var corporiumRecord = Restangular.one('corporium-mgmnts', $scope.uuid);
corporiumRecord.get().then(function(res) {
console.log(res)
}, function(err) {
console.log('Restangular failed: ', err)
});
Restangular Patch:
var data = {
corporiumId: $scope.newBlock
};
var corporiumRecord = Restangular.one('corporium-mgmnts', $scope.uuid);
corporiumRecord.patch(data).then(function(res) {
console.log(res)
}, function(err) {
console.log('Restangular failed: ', err)
});
$http attempt using config object:
controller code:
httpCorporiumSrv.updateCorporiumId('/corporium-mgmnts/' + $scope.params.id, data)
.then(handleUpdateSuccess)
.catch(handleUpdateError);
service code, tried forcing the content-type header but got same result
with or without it:
function updateCorporiumId(url, data) {
return $http({
method: 'PATCH',
url: url,
data: angular.toJson(data),
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
//transformRequest: transformUpdateData
})
.then(handleUpdateSuccess)
.catch(handleUpdateErrors);
}
Using the .patch shortcut:
function updateCorporiumId(url, data) {
return $http.patch(url, data, {
transformRequest: transformUpdateData
})
.then(handleUpdateSuccess)
.catch(handleUpdateErrors);
}
Thing is I've tried this every which way I know how. I don't even know how to start debugging any more. I'm just getting 404 on a resource that does exist. Any suggestions on what might be happening to my request would be great.
Resolution:
For anyone having this issue, if you could post the fix or what's going on here to this point or PM me that would be awesome I'd like to know. I ended up just using PUT to fix this.
Quick Restangular solution:
Build the url template for findByOne like function using Restangular.one(url, _id) where '_id', is the id of the resource you're looking for. .get() goes out and finds that one resource by said id, which you can populate dynamically however you like. Once you have the one resource with GET copy it with Restangular.copy() which is different from angular.copy as it doesn't bind 'this' to the new object. Change what needs to be changed or added in the new object and then perform a .put() on it.
var corporiumRecord = Restangular.one('corporium-mgmnts', $scope.uuid);
corporiumRecord.get().then(function(res) {
var update = Restangular.copy(res);
// update date corporiumId
update.corporiumId = $scope.newBlock;
// submit new doc with altered value
update.put().then(function() {
console.log('updated')
});
console.log(update)
}, function(err) {
console.log('Restangular failed: ', err)
});
Also because mongo uses _id and Restangular uses id you have to add this to your module
angular.module('corporium-mgmnts').config(function(RestangularProvider) {
RestangularProvider.setMethodOverriders(['put', 'patch']);
// setRestangularFields is required for mongodb
RestangularProvider.setRestangularFields({
id: "_id"
});
});

AngularJS $resource save() causes HTTP Error 415 (Unsupported Media Type)

I am using Angular 1.2-RC2 (also tried 1.0.8 and 1.1.x) and the ngResource module. The backend is a Spring WebMVC application.
angular.module("dox", ['ngResource'])
.controller('SettingsController', function ($scope, Settings) {
$scope.settings = Settings.query();
$scope.save = function () {
Settings.save();
};
})
.factory('Settings', function ($resource) {
return $resource('/api/v1/settings/:settingId', {settingId: '#id'}, {
'save': {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}
});
});
Whenever the save() method on the Settings class gets called the frontend receives a HTTP 415 (Unsupported media type). The reason is that AngularJS send the POST request using the following content type:
Content type: text/plain;charset=UTF-8
but the backend expects
Content type: application/json;charset=UTF-8
According the API docs it should be possible to override the header but my settings are somehow ignored. I seems that this is a common problem and as a workaround there are many recommendation to use $http.post instead of $resource.
Can you give me any hint how to solve this content type problem using the $resource service?
Please find the backend controller code here.
First, you're overriding a built-in $save method, so you can just omit the save: part (see source code). If you do define additional HTTP methods that aren't built-in, you can update the $httpProvider like so (this is to add the patch method):
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.headers.patch = {
'Content-Type': 'application/json;charset=utf-8'
}
}]);
Kevin Stone pushed me to the right direction. Thank you!
Since query() returns an array and save() is meant to save only one item of that array at the same time I had to reimplement the save() stuff differently.
$scope.save = function () {
angular.forEach($scope.settings, function (value, key) {
value.$save();
});
};
Done! Again thank you for your hints and help

Resources