I want to access an API route to a FOSRESTBundle Controller from my JS. I'm using the FOSJSRoutingBundle, but the route is not visible (I'm getting the 'the route xxx does not exist' error.
This is an action from my controller:
namespace ApiBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations\Get;
class AjaxController extends FOSRestController{
/**
* #Get("/someaction", name="someaction")
*/
public function someAction()
{
$response = 'some response';
$view = $this->view($response,200);
return $this->handleView($view);
}
}
This route is accessible via curl requests and by hardcoding it in the JS file, but I don't want to hardcode it. So i tried this in the JS file (after installing the JSRoutingBundle of course):
function prepareChart() {
$url = Routing.generate('someaction');
$.get( $url, function( data ) {
<do something>
}, "json" );
}
Here is the routing.yml entry:
ajax:
resource: "#ApiBundle/Controller/AjaxController.php"
prefix: /ajax/
type: rest
options:
expose: true
I've solved the problem - the FOSRestBundle does not support the 'name' attribute - the path to my controller was autogenerated and I had to retrieve its name from list of all routes by executing php app/console debug:router in the console.
Related
I inherited a mess. And I need a quick fix for some code that needs to be completely rewritten - but not enough time to do a complete rewrite yet.
Orig developer created an index.js file that has all kinds of global functions used all through an AngularJS/Ionic project. Quite often I am finding AngularJS functions in specific controllers actually passing $scope/$q out to the standard JS functions in the index.js file - its a mess.
One of these global functions is window.FirebasePlugin.onNotificationOpen - which is watching for any inbound pushNotification/messages. The original developer was simply using an "alert" for inbound messages with a data payload. We are now trying to redirect those messages to open up in proper popover or modal windows that belong in the controller in two specific pages.
So the question is, in the global watch JS function, how can I direct
the 'data/payload' to a controller to process in a proper $scope?
I have modified the .state to accept parameters - and then all the subsequent code is in place to pass the $stateParams into specific controllers:
.state('tab.clubs', {
cache: true,
url: '/clubs',
views: {
'tab-clubs': {
templateUrl: 'templates/tab-clubs.html',
controller: 'ClubCtrl',
params: {
'pushAction' : 0,
'pushCode' : 'default'
}
}
}
})
The problem I am having is trying to figure out how to pass URL data from standard JS into the AngularJS .state
The global JS function:
window.FirebasePlugin.onNotificationOpen(function(payload) {
// if there is a payload it will be in payload object
if (payload.action == 1) {
window.location("/clubs/", payload) ; // process message in ClubCtrl
} else if (payload.action == 2) {
window.location("/map/", payload) ; // process message in MapCtrl
}
}, function(error) {
console.error(error);
}) ;
But this method fails.
If your not going to use angulars router to navigate to the page you will need to declare the params in the URL somehow. You can use path params by doing something like /clubs/:pushAction/:pushCode or url params with something like /clubs?pushAction&pushCode
Example:
.state('tab.clubs', {
cache: true,
url: '/clubs/:pushAction/:pushCode',
views: {
'tab-clubs': {
templateUrl: 'templates/tab-clubs.html',
controller: 'ClubCtrl',
params: {
'pushAction' : 0,
'pushCode' : 'default'
}
}
}
})
Then navigate it with
location.href = `/clubs/${payload.action}/${payload.code}`
Additionally if you have alot of unknown params you could also pass in the whole payload as base64 encoded json. I wouldnt recommend this but it is... a solution
.state('tab.clubs', {
cache: true,
url: '/clubs?state',
views: {
'tab-clubs': {
templateUrl: 'templates/tab-clubs.html',
controller: 'ClubCtrl',
params: {
'state' : 0,
}
}
}
})
window.location(`/clubs?state=${btoa(JSON.stringify(payload))}`
Then in your controller reverse that operation
class ClubCtrl {
...
// parses the state out of the state params and gives you the full payload
parseState($stateParams) {
return JSON.parse(atob($stateParams.state));
}
...
}
It would give you all the payload, but its pretty gross
I would like to use AJAX in my Symfony3.0.3 project.
The communication works, but I can't get variable from JS to the handler. In the direction handler to JS, it works fine.
I'm trying to get the variable from the request with "$request->query->get('id'))" but I only get "null".
In an other way I'm trying to use the variable from the URL but I get this error:
"An exception has been thrown during the rendering of a template ("Some mandatory parameters are missing ("id") to generate a URL for route "admin_ajax".") in CommonBundle:Default:index.html.twig at line 421."
I don't mind using a solution or an other (I'll use the best one depending of your advices), but I still would like the solution for both error.
JS
function selectClient(idClient)//idClient = 1
{
alert(idClient);
$.post('{{path('admin_ajax')}}',{idClient: id},
function(response)
{
if(response.code == 100 && response.success)
{
alert(response.id);//Show null if using $request->query->get('id')) in handler but should be 1
}}, "json");
}
routing:
admin_ajax:
defaults: { _controller: CommonBundle:Default:getClient }
path: /ajax/{id}
handler:
public function getClientAction($id)
{
$request = $this->container->get('request_stack')->getCurrentRequest();
$isAjax = $request->isXMLHttpRequest();
if ($isAjax)
{
$response = array("code" => 100, "success" => true, "id" => $request->query->get('id'));
return new Response(json_encode($response));
}
$response = array("code" => 0, "success" => false);
return new Response(json_encode($response));
}
EDIT:
Thank for Rim, and Rvanlaak answer, I used the FOSJsRoutingBundle.
JS
function selectClient(idClient)
{
$.get(Routing.generate('ajax_getclient', { id:idClient }),
function(response)
{
if(response.code == 100 && response.success)
{
alert(response.id);
}
else
}, "json");
}
routing:
ajax_getclient:
defaults: { _controller: CommonBundle:Default:getClient }
path: /ajax/{id}
options:
expose: true
Note that the option "expose: true" was necessary to works.
Thats because the twig is executing before javascript so he is not reconizing the client id param
i had the same problem and resolve it using FOSJSRoutingBundle see this post :
Ajax url parametetr using Twig path
I want to give a parameter to an url in Ember.js and extract this parameter in the route index.js.
http://localhost:4200/7698
And then in index.js I do this:
model( params ) {
console.log( params.edition_id );
return "something";
}
Router.js file:
Router.map( function( ) {
this.route( '/index' );
this.route( '/', {
path: '/:edition_id'
} );
} );
This gives me the following error:
Error while processing route: / Ember Data Request GET undefined
returned a 404 Payload (text/html; charset=utf-8) Cannot GET /7698
Error: Ember Data Request GET undefined returned a 404 Payload
(text/html; charset=utf-8) Cannot GET /7698
However I don't want ember-data interfering with this, I simply need the parameter in my app logic, without any models or such.
you should just use
Router.map( function( ) {
this.route('index', { path: '/:edition_id' });
} );
by the way be aware if you remove # from first of url, you would get 404 error on reload your page
Currently I am trying to get a response from my Symfony2 FOSRest Controller using Javascript Fetch API. In my controller I am serving a response with the following code:
return View::create()
->setStatusCode(200)
->setData(array('success'=>true));
And in my JS I am getting it with this:
fetch(url, 'get').then(
function (response) {
if (response.status !== 200) {
console.log('Problem, status: ' + response.status);
return;
}
response.json().then(function (data) {
console.log('OK');
if (typeof callback == 'function')
return callback(data);
});
}
).catch(function (err) {
Console.log(err);
});
As a result my controller is giving me a pure json which I can see (in Chrome Dev Tools under Response section) if I type my url directly into browser, but when js code is executed I see the following error:
Uncaught (in promise) SyntaxError: Unexpected token <
which is related to my initial file which begins from !doctype html. In my debugger if I log response.body I see that it is ReadableByteStream, but not my json. I think that the problem is somewhere in Symfony because I can read my json if I send it from regular .php file but not from RESTFul controller. Any help would be appreciated.
UPD:
when I changed response.json() to response.text() I finally got the data)) Is there any reason for that? And in addition in my url I have query string parameters (like ?par1=1&par2=2) which I want to pass to my REST Controller and which I get when I again type my url into browser directly with the following method:
$data = $request->query->get('data');
But when I pass it with JS fetch (in url parameter) no data is transferred to controller. Any idea what to do in this case? Thank You
UPD2
My symfony config.yml
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener: true
view:
formats:
json: true
xml: false
html: false
rss: false
templating_formats:
json: false
xml: false
html: false
rss: false
view_response_listener: 'force'
sensio_framework_extra:
view: { annotations: false }
router: { annotations: true }
Try to add this in your config :
fos_rest:
# ...
view:
# ...
formats:
json: true
If it doesn't work, add the following :
fos_rest:
# ...
format_listener:
rules:
- { path: '^/yourApiPrefix', priorities: ['json'], fallback_format: json, prefer_extension: false }
Environment : scala 2.10, play 2.1.1, eclipse 4.2
Use case : the user click on a link figuring an object (game) in the database. An ajax request is sent through Javascript Route to a controller, which load the game data, convert it to json and send it back to view. The ajax success callback print the game title into a div.
Problem: i dont get a json, but the a html page (the page from which the ajax request s sent).
I suspect the problem is in the router : i put a print("route") in the javascript route action and a print("load game") in load game action. The "route" is displayed in console, but not the "load game". It may also come from my loadGame(id) route, but i dont see how i should set it.
Here is my code.
Routes:
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / controllers.Application.index
# Javascript routes
GET /javascriptRoutes controllers.Application.javascriptRoutes
# Library
GET /library/:id controllers.UserController.library(id: Long)
GET /library/:id controllers.GameController.loadGame(id: Long)
View:
<div class="span2">
<ul class="nav nav-pills nav-stacked">
#userGames.map { game =>
<li>#game.title</li>
}
</ul>
</div>
...
<script>
var successFn = function(data) {
$('#gameInfo').html('');
$("#gameInfo").append('<h4>'+data.title+'</h4>')
}
var errorFn = function(err) {
console.debug("Error of ajax Call");
console.debug(err);
}
ajax1 = {
dataType: 'text',
contentType:'application/json',
success: successFn,
error: errorFn
}
var displayGameInfo = function(id) {
javascriptRoutes.controllers.GameController.loadGame(id)
.ajax(ajax1);
}
</script>
ApplicationController with javascript route:
...
object Application extends Controller {
def javascriptRoutes = Action { implicit request =>
import routes.javascript._
println("-=== route ===-")
Ok(
Routes.javascriptRouter("javascriptRoutes")(routes.javascript.GameController.loadGame)
).as("text/javascript")
}
}
GameController with loadGame(id) method:
object GameController extends Controller {
...
// Library
def loadGame(id: Long) = Action(parse.json) { implicit request =>
println("-=== load game ===-")
val mess = Json.toJson(Game.find(id))
Ok(mess)
}
}
Game model:
case class Game(id: Long, title: String, description: String, userId: Long)
object Game {
val game = {
get[Long]("id") ~
get[String]("title") ~
get[String]("description") ~
get[Long]("userId") map {
case id~title~description~userId => Game(id, title, description, userId)
}
}
...
def find(id: Long): Game = DB.withConnection { implicit c =>
SQL("select * from game where id = {id}")
.on('id -> id).as(game *).head
}
implicit object GameFormat extends Format[Game] {
def reads(json: JsValue) = JsSuccess(Game(
(json \ "id").as[Long],
(json \ "title").as[String],
(json \ "description").as[String],
(json \ "uid").as[Long]
))
def writes(game: Game) = JsObject(Seq(
"id" -> JsNumber(game.id),
"title" -> JsString(game.title),
"description" -> JsString(game.description),
"userId" -> JsNumber(game.userId))
)
}
}
In your routes file, you have to routes matching the same URL :
# Library
GET /library/:id controllers.UserController.library(id: Long)
GET /library/:id controllers.GameController.loadGame(id: Long)
When your browser request the /library/123 url, Play will try the routes in the order of declaration and will match the first one, calling the controllers.UserController.library() Action. This is probably why you get a full HMTL page.
Try to define a different URL for the second route (the one returning JSON) and Play will be able to match the correct Action.
Ex :
GET /library/:id controllers.UserController.library(id: Long)
GET /gameData/:id controllers.GameController.loadGame(id: Long)