zend get with jQuery $.getJson - zend-framework

Does anyone know how to do this?
I want to point $.getJson() to a controller and have it return json through ajax based on request arguments. Unfortunately it appears Zend handles the get parameters different than jQuery encodes them.
How can I do this with Zend and jQuery? I saw something on stackoverflow about Post arguments but I am lost when it comes to GET.
When using jQuery I get a 404 error using this code:
Client side:
$.getJSON("/entry/get-member-course",
{
"id": 1,
"format": "json"
},
function(json) {
alert("WIN");
});
Server side:
public function init() {
$this->_helper->ajaxContext->addActionContext('get-member-course', 'json')->initContext();
}
public function getMemberCourseAction() {
$this->view->test = Array("test"=>"bleh");
}

Easiest way is to use context switching. In your controller, setup the AjaxContext helper for your action with a "json" context
class EntryController extends Zend_Controller_Action
{
public function init()
{
$this->_helper->ajaxContext->addActionContext('get-member-course', 'json')
->initContext();
}
public function getMemberCourseAction()
{
$id = $this->_getParam('id');
$this->view->test = array('test' => 'bleh');
}
}
The view for the calling script should contain a reference to the JSON URL. For example, say your JSON code is fired by clicking a link, create the link like this
<a id="get-json" href="<?php echo $this->url(array(
'action' => 'get-member-course',
'controller' => 'entry',
'id' => $someId
), null, true) ?>">Click me for JSON goodness</a>
Your client-side code would have something like this
$('#get-json').click(function() {
var url = this.href;
$.getJSON(url, {
"format": "json" // this is required to trigger the JSON context
}, function(data, textStatus, jqXHR) {
// handle response here
});
});
By default, when the JSON context is triggered, any view property is serialized as JSON and returned in the response. If your view properties cannot be simply converted, you need to disable automatic JSON serialization...
$this->_helper->ajaxContext->addActionContext('my-action', 'json')
->setAutoJsonSerialization(false)
->initContext();
and provide a JSON view script
// controllers/my/my-action.json.phtml
$simplifiedArray = array(
'prop' => $this->someViewProperty->getSomeValue()
);
echo Zend_Json::encode($simplifiedArray);

getJSON encodes a JSON string sequences such as {Person:{name: "Ken", age: "24"}}, do you have corresponding decoder on your server side?
JSON PHP
Zend PHP JSON

Related

Umbraco cannot find the route in backoffice

I've used Umbraco 7.3 in my project. I created a custom data type but when I want to call a Surfacecontroller in here is HelloSurfaceController or Hello2SurfaceController, I got an error in umbraco backoffice that said Request error: The URL returned a 404 (not found):
I studied some articles about routing but I couldn't solve my problem. I don't know that where I did wrong.
How can I solve this problem?
Reply.controller.js:
angular.module("umbraco")
.controller("Reply.controller", function ($scope, $http) {
$scope.SendReply = function () {
var sendTo = $("#Email").val();
var textMessage = $("#TextMessage").val();
$scope.xxx = "I'm here!";
var data = { SendTo: sendTo, TextMessage: textMessage };
// ~/Hello2Surface/ReplyMessage ---> Cannot find this URL
$http.post("~/App_Plugins/Reply/HelloSurface/ReplyMessage") // Can not find this URL
.then(function (response) {
alert("YES!");
//TODO:
});
}
});
SurfaceController
namespace Jahan.Nuts.Web.Mvc.UmbracoCms.App.App_Plugins.Reply
{
public class HelloSurfaceController : SurfaceController
{
[HttpPost][ChildActionOnly]
public ActionResult ReplyMessage()
{
//TODO: how should be write this method that be proper for getting data from angularjs?
return null;
}
}
}
package.manifest
{
propertyEditors: [
{
alias: "Send.Reply",
name: "Send Reply",
editor:{
view:"~/App_Plugins/Reply/Reply.html"
},
}
]
,
javascript:[
'~/App_Plugins/Reply/Reply.controller.js'
]
}
Reply.html
<div ng-controller="Reply.controller">
<div style="width: 100%;">
<input type="button" value="Send Reply" title="SendReply" name="Send Reply" ng-click="SendReply()" />
</div>
<div>
<input type="text" ng-model="xxx" name="message" />
</div>
Error in umbraco backoffice:
Take a closer look at the documentation - in particular the Plugin-based SurfaceControllers section:
https://our.umbraco.org/documentation/Reference/Routing/surface-controllers
try doing this (note the PluginController attribute):
namespace Jahan.Nuts.Web.Mvc.UmbracoCms.App.App_Plugins.Reply
{
[PluginController("Reply")]
public class HelloSurfaceController : SurfaceController
{
[HttpPost][ChildActionOnly]
public ActionResult ReplyMessage()
{
//TODO: how should be write this method that be proper for getting data from angularjs?
return null;
}
}
}
Other Notes:
You don't need to include "Surface" in the controller name anymore - simply calling it HelloController is enough.
Don't use a SurfaceController for Api calls if you're using it with AngularJS - Better to use an UmbracoApiController instead. Check out https://our.umbraco.org/documentation/Reference/Routing/WebApi/ for more information (including notes on where to expect the Api Endpoint to be)
You might also want to re-locate your controller so it's in a more conventional spot. There's no problem with putting it in the ~/Controllers directory even if it is a Plugin Controller.
Edit: Added "correct" way to do this:
As noted above, to implement an UmbracoApiController, you want a class looking like this - note you can use UmbracoApiController if you don't need to worry about authorization:
namespace Jahan.Nuts.Web.Mvc.UmbracoCms.App.App_Plugins.Reply
{
[PluginController("Reply")]
public class HelloApiController : UmbracoAuthorizedApiController
{
public void PostReplyMessage(string to, string message)
{
// TODO: process your message and then return something (if you want to).
}
}
}
Then in AngularJS set up a resource like this:
function replyResource($q, $http, umbDataFormatter, umbRequestHelper) {
var replyResource = {
sendMessage: function (sendTo, msg) {
return umbRequestHelper.resourcePromise(
$http.post("Backoffice/Reply/HelloApi/PostReplyMessage?" +
umbRequestHelper.dictionaryToQueryString(
[{ to: sendTo }, { message: msg }])),
'Failed to send message to ' + sendTo + ': ' + msg);
}
};
return replyResource;
}
angular.module('umbraco.resources').factory('replyResource', replyResource);
and finally your actual view controller can use this as follows:
angular.module("umbraco")
.controller("Reply.controller", function ($scope, $http, $injector) {
// Get a reference to our resource - this is why we need the $injector specified above
replyResource = $injector.get('replyResource');
$scope.SendReply = function () {
// You really shouldn't use jQuery here - learn to use AngularJS Bindings instead and bind your model properly.
var sendTo = $("#Email").val();
var textMessage = $("#TextMessage").val();
replyResource.sendMessage(sendTo, textMessage)
.then(function (response) {
// Success
}, function (err) {
// Failure
});
}
};
});
It's possible there's some errors in there; I did it mostly from memory - in particular, you may need to look into the best way to post data to the ApiController - it's not likely that it'll just accept the two parameters like that.
For a more complete example, consider reviewing the code of the Umbraco MemberListView plugin: https://github.com/robertjf/umbMemberListView
Also, you really should read up on the ASP.Net MVC fundamentals and the Umbraco Documentation for SurfaceControllers and APIControllers I've listed above already.
remove the "Surface" from the URL and include "backoffice":
angular.module("umbraco")
.controller("Reply.controller", function ($scope, $http) {
$scope.SendReply = function () {
var sendTo = $("#Email").val();
var textMessage = $("#TextMessage").val();
$scope.xxx = "I'm here!";
var data = { SendTo: sendTo, TextMessage: textMessage };
// ~/Hello2Surface/ReplyMessage ---> Cannot find this URL
$http.post("backoffice/Reply/Hello/ReplyMessage") // Can not find this URL
.then(function (response) {
alert("YES!");
//TODO:
});
}
});
Also, I'd recommend using UmbracoAuthorizedController not a surface controller as this is being used in the back end by logged in users it'll be wise to keep it secure.
So instead your controller should look something like this:
[PluginController("Reply")]
namespace Jahan.Nuts.Web.Mvc.UmbracoCms.App.App_Plugins.Reply
{
public class HelloApiController : UmbracoAuthorizedJsonController
{
public [Model-to-be-returned-to-angular] ReplyMessage()
{
//sql query etc to populate model
//return model
}
}
}

zend form dispatch a validator onblur an element

Is that possible?
I already wrote a simple validator that is triggered when the form is submitted.
Can I use the same validator, but it must be triggered right after the input field has been left.
is it possible?
pablo
It would be possible, here is one way to go about it.
Add a Javascript onblur event to the form element
The onblur call sends an ajax request containing that one field and its value to a ZF action
The action calls the validator on that element, or uses Zend_Form::isValidPartial to check the populated element
Return a JSON response indicating valid/invalid and optional error message
On ajax complete, read the JSON response and update HTML to reflect the result of validation
Hope that helps.
This is part of working example:
Backend:
class UserController extends Zend_Controller_Action
{
/* ... */
public function validateAction()
{
if ($this->_request->isXmlHttpRequest()) {
$values = $this->_request->getParam('values');
$form = new Form_User();
$isValid = true;
if (!$form->isValidPartial($values)) {
$isValid = $form->getMessages();
}
$this->_helper->json(array('errors' => $isValid));
}
}
/* ... */
}
Frontend, (just ajax call part) should be attached on event:
$.ajax({
type: "POST",
url: "/user/validate/",
data: {
'values': $('#my-form-id').serialize()
},
dataType: "json",
beforeSend:function(){
},
success: function(response){
var result = response.errors;
if (result == true) {
// given fields are valid
// do some extra stuff here
} else {
// invalid
// do some extra stuff here
}
}
}
});

Backbone.js Model different url for create and update?

lets say I have a Backbone Model and I create an instance of a model like this:
var User = Backbone.Model.extend({ ... });
var John = new User({ name : 'John', age : 33 });
I wonder if it is possible when I use John.save() to target /user/create when I use John.save() on second time (update/PUT) to target /user/update when I use John.fetch() to target /user/get and when I use John.remove() to target /user/remove
I know that I could define John.url each time before I trigger any method but I'm wondering if it could be happen automatically some how without overriding any Backbone method.
I know that I could use one url like /user/handle and handle the request based on request method (GET/POST/PUT/DELETE) but I'm just wondering if there is a way to have different url per action in Backbone.
Thanks!
Methods .fetch(), .save() and .destroy() on Backbone.Model are checking if the model has .sync() defined and if yes it will get called otherwise Backbone.sync() will get called (see the last lines of the linked source code).
So one of the solutions is to implement .sync() method.
Example:
var User = Backbone.Model.extend({
// ...
methodToURL: {
'read': '/user/get',
'create': '/user/create',
'update': '/user/update',
'delete': '/user/remove'
},
sync: function(method, model, options) {
options = options || {};
options.url = model.methodToURL[method.toLowerCase()];
return Backbone.sync.apply(this, arguments);
}
}
To abstract dzejkej's solution one level further, you might wrap the Backbone.sync function to query the model for method-specific URLs.
function setDefaultUrlOptionByMethod(syncFunc)
return function sync (method, model, options) {
options = options || {};
if (!options.url)
options.url = _.result(model, method + 'Url'); // Let Backbone.sync handle model.url fallback value
return syncFunc.call(this, method, model, options);
}
}
Then you could define the model with:
var User = Backbone.Model.extend({
sync: setDefaultUrlOptionByMethod(Backbone.sync),
readUrl: '/user/get',
createUrl: '/user/create',
updateUrl: '/user/update',
deleteUrl: '/user/delete'
});
Are you dealing with a REST implementation that isn't to spec or needs some kind of workaround?
Instead, consider using the emulateHTTP option found here:
http://documentcloud.github.com/backbone/#Sync
Otherwise, you'll probably just need to override the default Backbone.sync method and you'll be good to go if you want to get real crazy with that... but I don't suggest that. It'd be best to just use a true RESTful interface.
No you can't do this by default with backbone. What you could to is to add to the model that will change the model url on every event the model trigger. But then you have always the problem that bckbone will use POST add the first time the model was saved and PUT for every call afterward. So you need to override the save() method or Backbone.sync as well.
After all it seems not a good idea to do this cause it break the REST pattern Backbone is build on.
I got inspired by this solution, where you just create your own ajax call for the methods that are not for fetching the model. Here is a trimmed down version of it:
var Backbone = require("backbone");
var $ = require("jquery");
var _ = require("underscore");
function _request(url, method, data, callback) {
$.ajax({
url: url,
contentType: "application/json",
dataType: "json",
type: method,
data: JSON.stringify( data ),
success: function (response) {
if ( !response.error ) {
if ( callback && _.isFunction(callback.success) ) {
callback.success(response);
}
} else {
if ( callback && _.isFunction(callback.error) ) {
callback.error(response);
}
}
},
error: function(mod, response){
if ( callback && _.isFunction(callback.error) ) {
callback.error(response);
}
}
});
}
var User = Backbone.Model.extend({
initialize: function() {
_.bindAll(this, "login", "logout", "signup");
},
login: function (data, callback) {
_request("api/auth/login", "POST", data, callback);
},
logout: function (callback) {
if (this.isLoggedIn()) {
_request("api/auth/logout", "GET", null, callback);
}
},
signup: function (data, callback) {
_request(url, "POST", data, callback);
},
url: "api/auth/user"
});
module.exports = User;
And then you can use it like this:
var user = new User();
// user signup
user.signup(data, {
success: function (response) {
// signup success
}
});
// user login
user.login(data, {
success: function (response) {
// login success
}
});
// user logout
user.login({
success: function (response) {
// logout success
}
});
// fetch user details
user.fetch({
success: function () {
// logged in, go to home
window.location.hash = "";
},
error: function () {
// logged out, go to signin
window.location.hash = "signin";
}
});

JQuery AJAX get not working correctly?

I have a partial view with this jquery script:
$("#btnGetEmpInfo").click(function () {
var selectedItem = $('#EmployeeId').val();
var focusItem = $('#EmployeeId')
alert("Starting");
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
url: "<%= Url.Action("getEmpInfo", "NewEmployee")%>?sData=" + selectedItem,
data: "{}",
success: function(data) {
if (data.length > 0) {
alert("Yeah!");
} else {
alert("No data returned!");
}
}
});
alert("Back!");
});
Then in my controller I have:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult getEmpInfo(string sData)
{
return new JsonResult { Data = "test" };
}
I can breakpoint in the controller and it is hitting, but the only "Alerts" I get are the "Starting" and "Back". Why would the data not be returned or at least hit saying no data returned?
Thanks in advance for any and all help.
Geo...
You probably might want to improve this ajax call like this:
$.ajax({
type: 'GET',
url: '<%= Url.Action("getEmpInfo", "NewEmployee")%>',
data: { sData: selectedItem },
success: function(data) {
// Warning: your controller action doesn't return an array
// so don't expect a .length property here. See below
alert(data.Data);
}
});
and have your controller action accept GET requests:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult getEmpInfo(string sData)
{
return Json(new { Data = "test" }, JsonRequestBehavior.AllowGet);
}
OK, now that we have fixed the error let me elaborate. In your code you were using an application/json content type to format your request string. Unfortunately in ASP.NET MVC 2 there is nothing out of the box that is capable of making sense of JSON requests (unless you wrote a custom json value provider factory). Then using string concatenation to append the sData parameter to the URL without ever URL encoding it meaning that your code would break at the very moment the user enters some special character such as & in the EmployeeId textbox.
Try adding the 'beforeSend', 'error' and 'complete' callbacks to get more info in your javascript debugger. http://api.jquery.com/jQuery.ajax/
Are you using a javascript debugger? (firebug, ie9 dev-tools, chrome dev-tools are decent ones 3 that come to mind)

Sending parameters in AJAX call

This has been asked before by others, but I have not been able to use their answers.
I am trying to sending some data by doing the following:
function addForumPost() {
var title = jQuery('#forumTitle').val();
var message = htmlEncode(jQuery('#message').htmlarea("toHtmlString"));
var tagNames = addTags();
var dataPost = $.toJSON({ title: 'testingsadf', message: message, tagNames: tagNames });
jQuery.ajax({
type: "POST",
url: "/Create",
data: dataPost,
dataType: "json",
success: function (result) {
}
});
}
I have checked, and doubled checked that the input contains data, but I only receive the data from the message parameter in my controller. The other two values are null. As you can see in the the example above, I have even assigned some static text to the title parameter, but I still only receive data for the message parameter.
The controller looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string title, string message, List<string> tagNames)
{
....
}
Your error is very simple. If you have an action which returns JsonResult then it means not that you have to send also JSON encoded input parameters to the method. Just remove the usage of $.toJSON() and use the object as a value of data parameter of jQuery.ajax (see the answer of Darin for example).
If you call ASMX Web Service instead of ASP.NET MVC action you have to use contentType: "application/json; charset=utf-8" and convert the values of all web-method parameters to JSON but in a little other way (see How do I build a JSON object to send to an AJAX WebService?). But ASP.NET MVC hat no such requirement. MVC converts the input parameters which you send with respect of Parse method (int.Parse, DateTime.Parse and so on) and not deserialize from JSON. So if you search for code examples look exactly which technology are used on the backend: ASP.NET MVC, ASMX web serveces or WFC.
Try setting the traditional parameter starting from jQuery 1.4. This works for me:
var title = 'title';
var message = 'message';
var tagNames = ['tag1', 'tag2'];
jQuery.ajax({
type: 'POST',
url: '/Home/Create',
data: { title: title, message: message, tagNames: tagNames },
dataType: 'json',
traditional: true,
success: function (result) {
}
});
With this action:
[HttpPost]
public ActionResult Create(
string title,
string message,
IEnumerable<string> tagNames
)
{
return Json(new
{
Title = title,
Message = message,
TagNames = tagNames
});
}
You don't have to post json for the type of action you described.
You don't have to manually assemble the fields into a map. Use .serializeArray()
var postData = $('.myform').serializeArray()