Posting Input value as param to web service API in Angular - javascript

I have started learning Angular and I'm stuck on something that should be trivial. I'm from a Rails background and created a very simple Rails web-service that manages a MailingList. Currently 2x API endpoints exist in my web-service:
/api/v1/mailing_lists [GET]
/api/v1/mailing_lists [POST]
The POST endpoint requires a valid email as a PARAM in the API call. And this is where I'm stuck. How do I pass this param to my API in Angular? I've tried using solutions from around the web but I'm just not getting it right. Here's what I have thus far:
In my services.js:
angular.module('webFrontendApp.services', []).factory('MailingList', function($resource) {
var data = $resource(
'http://localhost:3000/api/v1/mailing_lists.json',
{},
{
// 'get': { method:'GET', cache: false},
'query': { method:'GET', cache: false, isArray:true },
'save': {method: 'POST', cache: false }
});
return data;
});
In app.js
....
.when('/list/new', {
templateUrl: 'views/list-new.html',
controller: 'MailingListCtrl'
})
....
Controller as mailinglist.js
angular.module('webFrontendApp.controllers', [])
.controller('MailingListCtrl', function($scope, MailingList) {
// Get all posts
$scope.mailing_lists = MailingList.query();
// Our form data for creating a new post with ng-model
$scope.memberData = {};
$scope.newMember = function() {
var member = new MailingList($scope.memberData);
member.$save();
};
});
The form looks like this:
<form role="form" ng-submit="newMember()">
<div class="row">
<div class="input-group">
<input type="text" ng-model="memberData.email" placeholder="New mailing list member" class="form-control">
<span class="input-group-btn">
<input type="submit" class="btn btn-primary" value="Add">
</span>
</div>
</div>
</form>
When I submit the form, nothing seems to happen, but I am receiving a 405 error response from my web service. After looking into my Web Service logs, I can see that a POST request was received but no params were passed and thus it was rejected.
Any help will be greatly appreciated!
PS. I have made sure to Enable CORS on my Rails app.

Pass params inside $save method, pass post_data inside MailingList, so basically this should work
$scope.newMember = function() {
var member = new MailingList();
member.$save({email: $scope.memberData.email});
};

Related

vue + nuxt.js - How to read POST request parameters received from an external request

I have a an external form which submits a post request to my nuxt app. I am struggling currently to find out how I can access these POST request parameters in my nuxt page?
I found so far the "asyncData" method, but when I try to access the submitted parameter through the "params" object it is always "undefined". What do I wrong here?
"asyncData" nuxt reference
example code in my nuxt page, assuming "email" is the request parameter submitted from outside
export default {
asyncData({ params }) {
console.log('asyncData called...' + params.email);
return {email: params.email};
},
external html form
<body>
<form action="https://..." target="_blank" method="post">
<input name="email" class="input" type="text" placeholder="Email" maxlength="255"></input>
<input name="submit" class="btn" type="submit" value="Ok"></input>
</form>
</bod>
I found a way, as described in "asyncData" nuxt reference you can pass the request and response object to the "asyncData({req,res})" call.
Here an example - assuming 'email' is one of the post parameter. querystring is a module of node.js and allows you to parse the request body into an array.
Small Remark - this seems only to work for nuxt page level components but not for lower level components
<script>
export default {
asyncData({ req, res }) {
if (process.server) {
const qs = require('querystring');
var body = '';
var temp = '';
while(temp = req.read()) {
body += temp;
}
var post = qs.parse(body);
return {data: post};
}
},
data() {
return {
data: '',
}
},
mounted() {
console.log(this.data['email']);
},
</script>
Nuxt.js cannot handle such things by itself.
https://nuxtjs.org/api/configuration-servermiddleware
You should implement your own middleware for such cases.
And asyncData has nothing to do with handling inbound POST data.

Laravel 4 can't get data from Angular Ajax

I am trying to develop my application in Laravel 4 and Angular JS, my application allows user to retrieve their Information through the system via Text Change.
Angular is used to pass data input from the user to Laravel which in turn retrieves the Information from the Database.
However Laravel is unable to retrieve the data passed from Angular.
View
<div data-ng-controller="ReservationController">
<input id='ERI' type='text' data-ng-model="scanRID" data-ng-change="queryRes()" name='exampleInput' maxlength='3' />
</div>
Angular Factory
app.factory('exampleFactory', function($http) {
var factory = {};
factory.getExample = function(scanRID) {
return $http({
method: 'GET',
url: LARAVEL_CONTROLLER + 'Example',
data: $.param(scanRID)
});
};
return factory;
});
Angular Controller
app.controller('exampleController', function($scope, $http, exampleFactory) {
$scope.queryRes = function() {
if($scope.scanRID.length == 3) {
exampleFactory.getExample($scope.scanRID)
.success(function (data) {
// Do Something Here
})
.error(function (data) {
console.log(data);
});
}
};
});
Laravel 4 Routes
Route::get('Example', 'ExampleController#show');
Laravel 4 ExampleController
class ExampleController extends \BaseController {
public function show()
{
$id = Input::get('scanRID'); // This here might be wrong. It's always empty!
$data = ExampleModel::find($id); // Able to query and retrieve.
return Response::JSON($data); // Returns empty.
}
}
Laravel 4 ExampleModel
class ExampleModel extends Eloquent {
// The id of this table is what I want, but I can't retrieve it.
protected $fillable = ['id', 'ExampleData1', 'ExampleData2'];
protected $table = 'exampleTable';
}
I have searched everywhere for a solution, it seems that everyone is able to successfully make the Ajax call. I think there is something that I am missing out that I am unaware about.
I have also tried setting CSRF Token, but however, I do not think that is an issue. So my last resort is to turn to the experts and hope someone is able to help me.
On a side note, I am fairly new to Laravel and Angular, so if you do post a solution, please explain to me the issue as I would like to learn more about Angular and Laravel.
Thank you for reviewing my issue.
You are not passing the value of scanRID by scanRID parameter instead pass only the value without parameter. So you are try to get the value from scanRID using Input::get('scanRID'); but without having scanRID parameter. that should be the case ur not getting the value :)
return $http({
method: 'GET',
url: LARAVEL_CONTROLLER + 'Example',
data: $.param({scanRID:scanRID}) //Change Here
});
OR
return $http({
method: "GET",
url: LARAVEL_CONTROLLER + 'Example',
params: {scanRID:scanRID} //Change Here
);
change like this

AngularJS giving 404 error on first run

I have written a very basic AngularJS script to parse JSON response. When I run this, I get a 404 status (error), while it works fine after that. I have been able to replicate this behavior on Google chrome and Opera while it works fine on Firefox.
What could be the problem?
<body ng-app="myApp" ng-controller="FormCtrl">
<form name="saveTemplateData" action="#">
First name:<br/>
<input type="text" ng-model="form.firstname"><br/><br/>
<input type="text" ng-model="form.firstname1">
<input type="submit" id="submit" value="Submit" ng-click="submitForm()" />
</form>
<ul>
<li ng-repeat="friend in friends">
{{friend.name}}, {{friend.house_no}}
</li>
</ul>
<script src="angular.min.js"></script>
<script src = "step4.js"></script>
</body>
</html>
The controller is as follows:
var app = angular.module('myApp', []);
app.controller('FormCtrl', function ($scope, $http) {
$scope.formData = {
firstname: "default",
firstname1: "default"
};
$scope.save = function () {
formData = $scope.form;
};
$scope.submitForm = function () {
console.log("posting data....");
$scope.formData = $scope.form;
$http({
method: 'GET',
url: 'http://localhost/testjson.php',
params: {
firstname: $scope.formData.firstname,
firstname1: $scope.formData.firstname1
}
}).success(function (data) {
//var pretty;
$scope.friends = data.response.docs;
//angular.toJson(data, [pretty]);
var str = JSON.stringify(data, undefined, 2);
//document.write(str);
alert("success");
}).error(function (data, status, headers, config) {
alert(status);
});
};
});
The PHP file just returns a dummy JSON file
<?php
header('Content-Type: application/json');
header("Access-Control-Allow-Origin: *");
$state = $_GET['firstname'];
$state1 = $_GET['firstname1'];
$arr = '{
"response": {
"numFound": 1,
"start": 0,
"docs": [
{
"name": "'.$state.'",
"house_no": "76"
}
]
}
}';
echo $arr;
?>
EDIT:
Some further Problem Diagnosis:
The problem is with both local and global URLs (working URLs with cross domain access allowed)
When I enter a value in textbox and submit, a 404 error is received and the page is refreshed. But if I re-submit the form again with the same information the results are shown. This probably means that the results are fetched but somehow a 404 error is shown in the first execution of get request. Since, above is the entire code I have, I am not able to determine where the problem lies.
There are no errors in the console. In the network tab, following details are there
Method:GET
Status: Cancelled
Initiater: angular.js:8081
Time:Pending
Are you serving the html/js from the filesystem?
i.e. file:///angular-test.html
If so you might be hitting a cross origin problem. See this other question about angular and 404s for a possible answer.
You are running this locally, correct? You need to add a port to your localhost, in the HTTP call in the controller:
(protocol)://localhost:XXXX/
MAMP/LAMP is usually port 8888 (your PHPs stack)
Cheers and <3 angular
Most of the tutorials on angularJS seem to leave the action field empty, but rather it turned out that action field is not required at all.
The issue is with the line
<form name="saveTemplateData" action="#">
It should rather be
<form name="saveTemplateData">
Additional help received from AngularJS Github issue.

View not updating bindings in SPA with Durandal/Knockout

I am building a SPA app with the default Durandal setup. I have multiple views returning data with ajax calls however, it is not working perfectly. I created my shell page with a search box so I can search through a list of employees shown here.
Shell.js
define(['require', 'durandal/plugins/router', 'durandal/app', 'config'],
function (require, router, app, config) {
var shell = {
router: router,
searchData: searchData,
employees: ko.observable(),
search: search,
activate: activate,
};
var searchData = ko.observable('');
function search(searchData) {
var url = '#/employeeSearch/' + searchData.searchData;
router.navigateTo(url);
}
return shell;
function activate() {
router.map(config.routes);
return router.activate(config.startModule);
}
});
shell.html
<div class="input-group">
<input type="text" class="form-control" placeholder="Search Employees" data-bind="value: searchData" />
<span class="input-group-btn">
<button type="submit" class="btn btn-default" data-bind="click: search">Search</button>
</span>
</div>
The user puts in a search and when they click the search button the view below navigates to the employeeSearch page. This does work and return the data and view I need it to here.
define(['require', 'durandal/plugins/router', 'durandal/app', 'config', 'services/logger'],
function(require, router, app, config, logger) {
var goBack = function() {
router.navigateBack();
};
function details(employee) {
var url = '#/employee/' + employee.Id + '/profile';
router.navigateTo(url);
}
var vm = {
goBack: goBack,
employees: ko.observable(),
details: details,
};
return {
activate: function (route) {
var self = this;
return self.getEmployees(route.q);
},
getEmployees: function (query) {
return $.ajax(app.url('/employees?q=' + query),
{
type: "GET",
contentType: 'application/json',
dataType: 'json',
}).then(querySucceeded).promise;
function querySucceeded(result) {
self.employees = result;
logger.log(query + ' Search Activated!', null, 'employeeSearch', true);
}
},
};
});
So then, if I try to search for another name, the url will navigate, the logger will show the value I searched for, however the view itself will not show the new results. Exploring in the Chrome debugger, I view the employees object to contain the new result set, but the view has still not updated. If I refresh the page however, the view does properly show up I have viewed multiple questions on here with similar issues about keeping your data calls in the activate method, make sure the data returns a promise before completing the activate method, and putting DOM manipulation in the viewAttached.
Javascript is not rendering in my SPA
How to use observables in Durandal?
HotTowel: Viewmodel lose mapping when navigating between pages
After putting those practices in my code, I am still having problems getting the view to update correctly.
Are there any other Durandal/Knockout properties I need to be aware of? How can I get the bindings to update every time I navigate to a view with (router.navigateTo(url);). I have set my shell properties (cacheViews: true) to true and false but nothing seems to change.
This is one of many problems I have been having with building SPA apps with Durandal. Trying not to give up yet.
I cant test this quick but a think you handle the observable wrong.
I suspect the "result" var is an array of employees. In this case you might handle this with an observableArray (http://knockoutjs.com/examples/collections.html)
And you cant set the value directly like self.employees
You must call the observable function to set the value like
function querySucceeded(result) {
self.employees(result)
logger.log(query + ' Search Activated!', null, 'employeeSearch', true);
}
In your samples you have not shown/mentioned where knockout is being loaded. If you are using Durandal 2.0 then add the following line to the top of your main.js file above your existing define statement
define('knockout', ko);

Can't delete objects with angular.js

I have defined myself an api for use with Angular.js:
angular.module('api', ['ngResource'])
.factory('Server', function ($resource) {
return $resource('http://localhost\\:3000/api/servers/:name');
})
.factory('ActiveServer', function ($resource) {
return $resource('http://localhost\\:3000/api/servers/active/:name', {},
{ start: {method: 'POST'}, stop: {method: 'DELETE'} });
});
The idea, is that I have a set of defined servers, available through the /api/servers/ uri. A particular server can be started by adding it to the /api/servers/active/ uri, and stopped by deleting it.
In my controller, I have the following code:
$scope.start = function() {
ActiveServer.start(this.server);
};
$scope.stop = function() {
ActiveServer.stop(this.server);
};
which again is triggered by buttons
<div class="well span4" ng-repeat="server in servers">
<h1>{{server.definition.name}}</h1>
<span class="label label-info">{{server.status}}</span>
<button type="button" class="btn btn-primary"
ng-click="start()">Start</button>
<button type="button" class="btn btn-primary"
ng-click="stop()">Stop</button>
</div>
Starting the server works alright, but I have trouble stopping it. The code above ends up with the following request:
Request URL:http://localhost:3000/api/servers/active?$$hashKey=005&_events=[object+Object]&definition=[object+Object]&status=ready
Request Method:DELETE
The definition-part of my server object, containing the name that identifies the server to stop, isnĀ“t serialized right.
How can I fix this?
If your API (ActiveServer) only needs to receive the server's name to work, then pass only the server's name during the service call. Like this:
$scope.start = function() {
ActiveServer.start({name: this.server.definition.name});
};
$scope.stop = function() {
ActiveServer.stop({name: this.server.definition.name});
};
Passing the entire this.server object in any service call will result on the entire object being parametrized into the HTTP request.
extended explanation:
When you use something like api/servers/:name on your $resource URL, you are basically saying that the :name part will be replaced by the value of a property with the same name (in this case, 'name') received on the parameters (i.e. {name: 'someName'}).
From the angularJS $resource documentation:
Each key value in the parameter object is first bound to url template
if present and then any excess keys are appended to the url search
query after the ?. Given a template /path/:verb and parameter
{verb:'greet', salutation:'Hello'} results in URL
/path/greet?salutation=Hello. If the parameter value is prefixed with
# then the value of that parameter is extracted from the data object
(useful for non-GET operations).
So, if you call service.get({name: 'abc'}) then URL of the request will be
api/server/abc
If you call service.get({name: 'abc', id: '123'}) then URL will be api/server/abc?id=123
In your case, the this.server Object looks something like this:
{
$$hashKey: 005,
_events: {},
definition: {},
status: ready
}
Since AngularJS does not do in-depth parametrization, _events and definitions are shown as _events=[object+Object]&definition=[object+Object] on the URL.

Categories

Resources