AngularJS + Rails: get a resource but stay on current page - ruby-on-rails

I have a page with a button that will change the state of the resource on the server. Everything is wired up and working fine. But I had to resort to a server response that sent the browser "back" because I don't want to display a new template for the action url.
This seems like a typical thing to do: ask the server to do something, then get the current state of the resource and redisplay it in the current template without refetching the template.
The markup on the page is like this:
<a ng-href="/api/preference/{{video._id}}/add_to_watchlist" data-method="get">
<i rel="tooltip" data-original-title="Add to watchlist" class="icon-3x action-icon
icon-plus-sign" ng-class="{iconSelected : video.user_watchlist == 1}">
</i>
</a>
This highlights the icon depending on the value of $scope.video.watchlist. When clicked it fires a custom action on the server to add the video being viewed to the current user's watchlist. The url created is /api/preference/{{video._id}}/add_to_watchlist, this fires the server controller, which does the right thing but instead of going to a template for /api/preference/{{video._id}}/add_to_watchlist the server responds by telling the client to go "back" in Rails this is redirect_to :back.
Clearly this is wrong. It works but refetches the entire page markup and data.
The AngularJS controller that loads the original data is here:
GuideControllers.controller('VideoDetailCtrl', ['$scope', 'Video',
function($scope, Video) {
var pattern = new RegExp( ".*/([0-9,a-f]*)", "gi" );
//ugh, there must be a better way to get the id!
$scope.video = Video.get({ id: pattern.exec( document.URL )[1] });
}
]);
Here is the resource that gets the json
var VideoServices = angular.module('VideoServices', ['ngResource']);
VideoServices.factory('Video', ['$resource',
function($resource){
return $resource("/api/videos/:id", {id: "#id"}, {
update: {method: "PUT"},
query: {
isArray: true,
method: "GET",
headers: {
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
},
get: {
isArray: false,
method: "GET",
headers: {
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
}
});
}
]);
Somehow I need to
tell the server to add_to_watchlist without changing the browser URL
trigger a refetch of the video json without reloading the template.

It seems like you're relying on routing to handle a model update on the server, instead of doing it through angular's $resource service. Here's a quick and dirty:
Instead of routing, call a function when the user clicks:
<a ng-click="addToWatchlist(video._id)">
<i rel="tooltip" data-original-title="Add to watchlist" class="icon-3x action-icon
icon-plus-sign" ng-class="{iconSelected : video.user_watchlist == 1}">
</i>
</a>
Add the click handler function to your controller (I'm using $http here, but you would be better off adding a custom action to your Video resource). On the success callback you can reload the video. Or, better yet, you can return the video from the add_to_watchlist action.
Check out http://docs.angularjs.org/tutorial/step_07 which explains the $routeParams (to answer your comment about getting the video id).
GuideControllers.controller('VideoDetailCtrl', ['$scope', '$http', '$routeParams', 'Video',
function($scope, $http, $routeParams, Video) {
$scope.video = Video.get({ id: $routeParams.videoId });
$scope.addToWatchlist = function(videoId) {
$http.get('/api/preference/'+videoId+'/add_to_watchlist').success(function() {
$scope.video = Video.get({ id: $routeParams.videoId });
};
};
}
]);

Related

Rails 6: AJAX data not reaching controller

I'm trying to make a pretty standard ajax call, this is my code:
// app/javascripts/packs/contacts.js
jQuery(() => {
$(".upload-contacts-button").on("click", (e) => {
e.preventDefault();
//...
$.ajax({
url: "/pre-parse-contacts",
type: "post",
data: { my_param: random_param },
success() {},
});
});
});
This is my route:
post 'pre-parse-contacts', to: 'contacts#pre_parse_contacts', as: 'pre_parse_contacts'
For some reason the data I send in the ajax request never reaches the controller, when I try to puts params in the controller action I get this result:
------- DEBUG --------
{"controller"=>"contacts", "action"=>"pre_parse_contacts"}
I'm sure the ajax call is made, even the js.erb view tries to render but I get errors due to I need the data I send in the ajax call. Why is this happening?
Sorry I found the answer to my issue, it seems I was trying to send an array of strings in the params of the ajax request, this is why it was never reaching the controller.
joining the array made the trick:
$.ajax({
url: "/pre-parse-contacts",
type: "post",
data: { my_param: random_param.join() },
success() {},
});

ASP.NET MVC Ajax form submitted cross domain not sending Cookies

I have a site that has some forms that use ASP.NET MVC Ajax. An example of BeginForm method:
#using (Ajax.BeginForm("HandleSignin", "Profile", null, new AjaxOptions() {
HttpMethod = "POST",
Url = Url.Action("HandleSignin", "Profile", null, Request.Url.Scheme),
OnBegin = "SetWithCredentialsTrue(xhr)",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "signin-form-container" },
new { id = "sign-in-form", #class = "text-left-desktop group" }))
{
#Html.AntiForgeryToken()
#Html.TextBoxFor(x => Model.Email, new { placeholder = "Email" })
#Html.PasswordFor(x => Model.Password, new { placeholder = "Password" })
<input type="submit" value="SignIn" class="button small-button">
}
Note that because of the Request.Url.Scheme param in the of the Url.Action method, the URL is being set to a different domain than the domain that the browser is getting this from. This is done because the main site is hosted statically using a CDN while the form is loaded from another domain using AJAX. This works, except that the cookies are not sent in the AJAX request. I tried to have the cookies sent by setting xhr.withCredentials = true by using the OnBegin event and this JavaScript:
<script type="text/javascript">
function SetWithCredentialsTrue(xhr) {
console.log("SetWithCredentialsTrue(xhr)", xhr);
xhr.withCredentials = true;
}
</script>
While I can see that SetWithCredentialsTrue() method gets called, it does not seem to work in that the HTTP Request generated when the form is submitted does not have the Cookie header.
All of the server-side handlers are setting the Access-Control-Allow-Credentials response header to true and the Access-Control-Allow-Origin to the main (static) site domain.
UPDATE: With some more console logging, I have verified that the xhr parameter passed to my OnBegin event handler (SetWithCredentialsTrue) is NOT an XMLHttpRequest object and hence setting withCredentials on it does not have an affect. So the question is how can I access the XMLHttpRequest object?
I finally figured this out. The XMLHttpRequest object is not exposed via the ASP.NET MVC library. I was able to alter jquery.unobtrusive-ajax.js, the JS library used by the ASP.NET MVC helper so that it sets withCredentials to true:
$(document).on("submit", "form[data-ajax=true]", function (evt) {
var clickInfo = $(this).data(data_click) || [],
clickTarget = $(this).data(data_target),
isCancel = clickTarget && clickTarget.hasClass("cancel");
evt.preventDefault();
if (!isCancel && !validate(this)) {
return;
}
asyncRequest(this, {
url: this.action,
type: this.method || "GET",
data: clickInfo.concat($(this).serializeArray()),
xhrFields: {
withCredentials: true
}
});
});
Note: xhrFields is the part that I added.
as per my understanding what you trying to do is, you want to send data from one domain to another domain using AJAX post right?
If it is true then, i want to tell you that browser doesn't allow you to do that because of security issue.
If you still want to do that then you have two option's by using CORS and JSONP.
http://csharp-video-tutorials.blogspot.in/2016/09/calling-aspnet-web-api-service-in-cross.html
http://csharp-video-tutorials.blogspot.in/2016/09/cross-origin-resource-sharing-aspnet.html

page refresh unsetting Ember controller property

If I do a manual page refresh or set a redirect like this
}).then(function() {
document.location = "/";
}, function() {
in a function in my Ember app, an Ember controller property is becoming unset. I'm not sure if it matters which property is becoming unset, but in case it does, here's the code. In my application template, I check for whether a user is authenticated
{{#if isAuthenticated}}
blah blah
{{else}}
blah blah
{{/if}}
The property is set in the AuthController
App.AuthController = Ember.ObjectController.extend({
currentUser: null,
isAuthenticated: Em.computed.notEmpty("currentUser.email"),
login: function(route) {
var me;
me = this;
return $.ajax({
url: "/users/sign_in.json",
type: "POST",
data: {
"user[email]": route.currentModel.email,
"user[password]": route.currentModel.password
},
success: function(data) {
me.set('currentUser', data.user);
So, after I do a page refresh, my currentUser is (according to Ember) no longer showing as authenticated. However, on the (Ruby on Rails) server side the user is still authenticated. I put this code in the layout to check and after Ember thinks that I'm signed out (with a page refresh), my server code is still telling me I'm signed in (which in fact I am).
<% if current_user %>
there is a current user
<% end %>
Can you explain why this might be happening?
Your best is to set up is probably to make isAuthenticated a computed property that does an ajax GET to /users/current if currentUser is null. /users/current should return {} if the user is not authenticated, and the current user data if they are.
App.AuthController = Ember.ObjectController.extend({
currentUser: null,
isAuthenticated: function() {
var currentUser = this.get('currentUser');
if(Ember.isEmpty(currentUser)) {
$.ajax({
type: "GET",
url: "/users/current.json",
async: true
}).done( function( data ) {
me.set('currentUser', data.user);
});
return false;
} else {
return true;
}
}.property('currentUser'),
});
Why are you doing a manual page refresh? This way you are starting the browser completely from scratch and therefore the Ember App initializes again and therefore the property currentUser is not set on your Controller.
Instead of doing this:
}).then(function() {
document.location = "/";
}, function() {
I assume that you want to go to the start page of your app (since you want to go to "/"). Instead you should use the Ember feature of transitioning between routes. I do not know, where you execute the code above, but you can do this either from one of your controllers or in a route.
In a controller you could use transitionToRoute:
// index is a Route which is implicitly generated by Ember for you
this.transitionToRoute('index');
In a router you could use transitionTo:
this.transitionTo('index');

global ajaxError event

i have a global ajaxError event in my base.master, something like this
$(document).ajaxError(function(event, request, settings,thrownError) {
$("#results").append( "<li>some error msg.</li>" );
});
but i dont want to use "#results". i want it to be dynamic. i want the function to always display the error msg in the update-target element. how can i find the UpdateTarget Id that was used from my ajax call in the error event? thanks.
using (Ajax.BeginForm("action", null,
new AjaxOptions {
UpdateTargetId = "results", <--find this element in my error event
LoadingElementId = "loading",
I am not familiar with ASP, but the ajaxError receives all settings that are passed to the ajax method in the aptly named settings variable.
So if you have an AJAX call like this:
$.ajax({
url: '/some/url.asp',
data: myData,
resultsEle: $('#myResults')
});
You can access resultsEle in the ajaxError like this:
$(document).ajaxError(function(event, request, settings, thrownError) {
settings.resultsEle.append( "<li>some error msg.</li>" );
});
Again, I am not familiar with the way you are calling the AJAX method in your ASP code, but my guess would be you can get to the element this way:
$(document).ajaxError(function(event, request, settings, thrownError) {
$('#'+settings.UpdateTargetId).append( "<li>some error msg.</li>" );
});

jQuery: why does $.post does a GET instead of a POST

I'm trying to do an ajax call and populate the "Data" div with the results of the ajax call,
but i get an error saying: "Server Error in '/' Application. The resource cannot be found. Description: HTTP 404. Requested URL: /Home/GetStuff"
When I look with Firebug the request was a GET to /Home/GetStuff and the answer was 404 Not found. Why I doesn't do a POST as I required in the ajax call? How can I do a POST?
I tried using $.post and got the same behavior, though I haven't checked the jquery code, I assume $.post is a wrapper around $.ajax.
Also i tried Ajax.ActionLink and it works fine, though I would like to use jQuery and not the Microsoft's ajax js libraries.
The code follows:
Home/TestStuff.aspx
function aClick() {
$.ajax({
type: "POST",
url: $("#MeFwd").href,
data: ({ accesscode: 102, fname: "JOHN", page: 0 }),
dataType: "html",
success: renderData
});
};
function renderData(data) {
$("#Data").html(data.get_data());
}
<div id="Data">
</div>
<div id="link">
Click Me!
</div>
HomeController.cs
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult GetStuff(int accessCode, string fName, int? page)
{
return "<table><tr><td>Hello World!</td></tr></table>";
}
Change your onclick="aClick" to onclick="aClick(); return false;". Clicking the link is doing a get to the url instead of running your JS.
$('#MeFwd').click(function() {
$.ajax({
type: "POST",
url: $("#MeFwd").href,
data: ({ accesscode: 102, fname: "JOHN", page: 0 }),
dataType: "html",
success: function(data){
$("#Data").html(data);
});
});
};
I don't think you can call a function on your return ... but I could be wrong.
Or look into load:
http://docs.jquery.com/Ajax/load#urldatacallback

Resources