I am using AngularJS 1.0, PHP, and mysql with localhost on my Mac.
I have a very simple mysql database: "cats". It has one table called "kittens". The table has two columns, "id" and "name". There are three kittens in the database with names Larry, Curly, and Moe.
Now, I have some simple PHP code which opens the dbase, gets the three records, and returns them in JSON like this:
[{"id":"1","name":"Larry"},{"id":"2","name":"Curly"},{"id":"3","name":"Moe"}]
I used the latest angular-seed and set up a project in Eclipse. In app.js I wrote this:
'use strict';
angular.module('Cats', ['ngResource','Cats.filters', 'Cats.services', 'Cats.directives'])
I put my PHP code in the services folder and named it "index.php". If I navigate to that services/index.php in my browser using localhost on the Mac, I get back the JSON above. So far everything is cool.
Now - ALL I WANT TO DO (!famous last words ;) is connect to the PHP service I have and display the contents of that Cats table on my main index page using my AngularJS project. Plain old text, no tables, just bind to the available resource and display it.
I have tried all manner of demos online (many are out of date). Can anyone show me one simple, current way to do this? For the life of me I have all kinds of AngularJS demos working but none of them does this simple task in a way I can understand.
Thank you in advance.
This should also work. It's significantly fewer lines of code, but note that any error handling has been removed:
function FetchCtrl($scope, $resource) {
var services = $resource('../services/index.php');
$scope.data = services.query();
}
FetchCtrl.$inject = ['$scope', '$resource'];
Normally I would have used the built in .get() method on the $resouce but your response is in the form of an Array, which .query() supports by default.
You can find the documentation on $resource here
OK. Solved it. First, copied and added a controller I found in one of the examples:
function FetchCtrl($scope, $http, $templateCache) {
$scope.method = 'GET';
$scope.url='../services/index.php';
$scope.fetch = function() {
$scope.code = null;
$scope.response = null;
$http({method: $scope.method, url: $scope.url, cache: $templateCache}).
success(function(data, status) {
$scope.status = status;
$scope.data = data;
}).
error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
};
$scope.updateModel = function(method, url) {
$scope.method = method;
$scope.url = url;
};
}
Then, I added a route controller to my module:
var module = angular.module('Cats', ['ngResource','Cats.filters', 'Cats.services', 'Cats.directives'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/main', {templateUrl: 'partials/partial1.html', controller: MyCtrl1});
Finally, on that partial1.htm page, I added this:
<div ng-init="fetch()">
{{data}}
</div>
Now when I go to that page in the browser, I can see the entire database dumped out in JSON. Ahhhh. Clearly there are many more sophisticated things to try, etc etc but I needed this simple result so I could be certain that I did in fact have a model arriving when summoned. Angular dies silently in many cases, so a visible result was quite helpful.
One gotcha: be careful that the php file is executable by more than yourself as user. I don't know the name of the user that AngularJS implements but I had to chmod 644 index.php so the app could use it.
I hope that helps someone.
Related
I have an application in an EC2 instance in AWS, using a classic load balancer, LAMPP server running Apache2 and some of my ajax calls to my php files, hosted in a folder in the same instance, don't work. But what is odd is that some calls work, and some doesn't. For exemple, I have a page of a supermarket that loads the data of the supermarket (name, address, phone) from the server (using a PHP file to access the MySQL database and returning a json) but doesn't load the products of the store. But if I use the search field, that makes a call to a different PHP file (but that is very similar to the one responsible for returning all the products with the difference that have a where clausule on the MySQL that specifies the product name) than it works perfectly.
Another exemple is the call to list all the cities of a state, when the user chooses a state in a select field. It doesn't work. Here is the code that I use to make the ajax call:
$("#state").change(function() {
var idState = $(this).val();
$("#cities").text("");
$.ajax({
type: "POST",
url: "https://myurl.com/server/consultations/find-cities.php",
data: {
idState: idState,
},
}).done(function(response) {
var obj = $.parseJSON(response);
var field = $("#modal-change-city").find("#cities");
var option = $("<option>").attr("selected", "true").attr("disabled", "true").text("City");
field.append(option);
obj.forEach(function(index) {
var cityResponse = index[0];
var select = $("#modal-change-city").find("#cities");
var city = $("<option>").attr("value", cityResponse).text(cityResponse);
select.append(city);
});
});
});
And this is the PHP file find-cities.php:
<?php require_once("../connection.php");
header("Access-Control-Allow-Origin: *");
$id_state = mysqli_real_escape_string($connection, $_POST['idState']);
$query = "SELECT name_city
from cities_tb
where id_state = $id_state";
$con = mysqli_query($connection, $query);
$response = array();
while ($data = $con->fetch_array()) {
array_push($response, $data);
}
echo json_encode($response);
?>
When I just run the php script, going to its URL, it gives me error 500, what is normal. I tested my application on 000webhost before using the AWS and this error also happens when I tried to access the URL directly, but the ajax call works on 000webhost.
When I put an id directly no the $id_state instead of the post data, I keep getting no response on the ajax, but when I go to the URL, I got a page all white, that doesn't show the json on the echo, but also shows no errors. If I change the echo json_encode($response) to a var_dump($response) I got a page with the array $response, with all the cities of that state. But still nothing on the html page that made the ajax request.
I never actually got an error, I just got no response and then the console shows Uncaught SyntaxError: Unexpected end of JSON input, due to the fact that the ajax code kept running, got no response, and so it wasn't able to parse the json file it expected to receive. When I try a console.log(response), it just shows the file and line in which I wrote the console.log code and nothing on it, as it got no answer.
And my guess is that there might be something wrong with the URL call. Or maybe because of the use of load balancers, or maybe the URL I'm calling should be different. I tried using localhost or the ip but, since it's a https page, it doesn't accept calls to http URLs. When I put the https before the localhost or IP, it gives me the error 500. I tried also to point to the directory of the PHP file on the URL, but it also didn't work.
And also, I need to make the calls to the full URL, because I need to make it work on a cordova application for Android and iOs too.
The directories are organized like this (some folders and files, that aren't important in this issue, were ommited):
/var/www/html
|-js
|-css
|-img
|-all the .html files
|-server
|-consultations
|-find-cities.php
|-other .php files (including the ones of the supermarket)
|-other folders
|-connection.php
And just for the record: I tested all of it on 000webhost before and all worked fine, even on the cordova application, and using the same directory organization and files.
The problem was in the MySQL database. The charset and collation weren't UTF-8 and so the requests that had special characters, such as ã, é or ç weren't working, while the requestas that didn't had special characters were working.
I've been working on a small app that uses Laravel 4 as the backend with AngularJS as a starting point (mainly to learn angularjs).
The idea is that I can add bookmarks via a form and have these show up in a searchable table. I've got it working where I can post to the database and save the records. The following does that job perfectly for my needs.
$scope.addLink = function () {
var link = {
title: $scope.newTitle,
address: $scope.newAddress,
type: $scope.newType
};
$scope.links.push(link);
$http.post('bookmarks', link);
};
However, I can't work out how to delete a row. Well, I can remove it in the table but not from the database using routes. I have got a ng-click function that removes the row from the table using splice but don't know how to send the details to the Laravel route to remove from the database. So far I have this:
$scope.remove = function ( idx ) {
var link = $scope.links[idx];
$scope.links.splice(idx, 1);
$http.delete('removemark/{link.id}');
};
Clearly the $http.delete is not set up correctly but not sure how to do it.
I have the route as this:
Route::delete('removemark /{id}', function($id)
{
Bookmark::destroy($id);
});
But this isn't working. Hopefully some one can point me in the next direction.
you need to build the url string yourself.
$scope.remove = function ( idx ) {
var link = $scope.links[idx];
$http.delete('removemark/' + link.id).success(function(response){
/* should validate response and then remove from array */
$scope.links.splice(idx, 1);
}).error(function(err){
/* do something with errors */
});
};
I would suggest you leave the item in the array until you have confirmed delete from server response and then remove it from array.
Use your browser console network tab to inspect requests. You likely would see a 404 status with what you had and would give you clues where things weren't quite right
I'm a newbie web developer diving into Backbone for the first time. One major question I've been grappling with (actually spending a lot of the last 2 days researching to no avail) is how exactly Backbone communicates with the server.
In my previous projects, I've been able to fetch data from my database using PHP and jQuery's $.getJSON method. I'm also able to do this when I use Backbone (ex. myCollection.url = "todos.php"). However, in every single tutorial, documentation, example code I've looked at, the url for the collection is always set as a directory and the urlRoot is directory/id (ex. myCollection.url = "/todos" and myModel.urlRoot = "/todos/5".
My question is how exactly is this managed? It seems a lot cleaner than the traditional way I was doing by adding GET parameters to my calls. At first I thought this was routing, but that seems to be only related to setting browser history and stuff. Some of the source code I've looked at uses SLIM PHP and Rails (neither of which I've used) but I have no clue how any of the parts fit together.
Backbone has its own api for communicating with server, such as fetch, save, destory.In fact, these methods do the same things with jQuery's $.ajax. For example, you use backbone's fetch in this way:
var UserModel = Backbone.Model.extend({
url : "rootURL/user",
});
var user = new UserModel;
user.fetch(
data:{
userId : 1, //the webservice will be: rootURL/user?userId=1 GET;
}
success:function(model, response, options){
//callback
},
error:function(model, response, options){
//callback
},
);
But you can also use the same way as in the jQuery to communicate with server in backbone's application.For example:
var UserView = Backbone.View.extend({
render: function(){
//do something
}
getUser: function(userId){
$.get("rootURL/user",
{ userId : userId},
success:function(data){
//callback
}
);
}
});
I'm trying to get an understanding of how Backbone.js, Slim PHP and Paris/Idiorm might work together and I'm having trouble completing the flow, starting with model attribute data, all the way to the database. PROBLEM: What exactly gets sent to my server when I do model.save() ?
Client-side: Backbone.js
var Donut = Backbone.Model.extend({
defaults: {
name: null,
sparkles: false,
creamFilled: false
},
url: function() {
return '/donut';
}
});
var bostonCream = new Donut({
name: 'Bawston Cream',
sparkles: true,
creamFilled: true
});
bostonCreme.save(); // <-- Problem: Not sure what & format this is sending
I think the above is my main problem. My understanding is that backbone will by default, know to send POST data since it's new. It sends it to /donut which is routed, but the question I have is WHAT does it send? And in what format? The outcome I want is to save those donut attributes to my DB. I can pass this server-side code a json like this using jQuery $.post()...
var myDonut = {"name":"Jelly Filled", "sparkles":false, "creamFilled":true};
$.post('http://localhost/donut', myDonut);
...and it happily takes it, saves it to my database. But with the current setup trying to send my backbone donut data, I get POST 500 Internal Server Error. Below I have some server-side code.
Server-side: Slim PHP w/ Paris
class Donut extends Model {}
$app->post('/donut', function() use ($app) { // Slim framework routes my POST...
$donuts = Model::factory('Donut')->create(); // Paris stuff...
$donuts->name = $app->request()->post('name'); // Slim request parameters...
$donuts->sparkles = $app->request()->post('sparkles');
$donuts->creamFilled = $app->request()->post('creamFilled');
$donuts->save(); // Paris... Save name, sparkles, and creamFilled to my DB
});
I have a feeling the answer is out there, but every example I've looked at seems to be missing one piece of the puzzle or another and I can't get that "A-hA!" moment. I thank you in advance and apologize if this is a really ignorant question. :-P
FOLLOWUP/EDIT: 1
Can you paste the error messages?
I get a POST http://localhost:8888/donut 500 (Internal Server Error) in the current state. I can get more information with the following code.
bostonCream.save({}, { // REPLACE bostonCream.save();
success: function(model, response) {
console.log('SUCCESS:');
console.log(response);
},
error: function(model, response) {
console.log('FAIL:');
console.log(response);
}
});
Now when I run backbone's save(), I still get the 500 Error but also XMLHttpRequest as my FAIL response. The only remarkable clue from the XMLHttpRequest is responseText = SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name' cannot be null.
So my guess is that either 1) I'm messing something up with the save() in that it isn't capturing my attributes correctly, 2) It is currently sending my attributes in a format that my server isn't recognizing with the standard $app->request()->post() Slim methods (Doesn't seem to do much when I try accessing directly with $_POST either), 3) My server isn't setup correctly to take the kind of data that is being sent.
Another thing I noticed although I don't know what to make of it is that when I add
echo $_POST;
It returns to me an empty array. Still gives me the FAIL. If I do THIS however...
echo json_encode($_POST);
It gives me a SUCCESS and the response is a [ ]. Nothing in there. Clearly my POST data is still wonky.
I came up with a solution to completing the problem: how to get data from client to server using the default backbone save() and .sync - passed over to the Slim php framework and going through Paris/Idiorm to my DB.
I am including my working updated code below:
Client-side: Backbone.js
var Donut = Backbone.Model.extend({
defaults: {
name: null,
sparkles: false,
creamFilled: false
},
url: function() {
return '/donut';
}
});
var bostonCream = new Donut({
name: 'Bawston Cream',
sparkles: true,
creamFilled: true
});
bostonCream.save();
/***** If you want to check out the response to save() ? ***
bostonCream.save({}, {
success: function(model, response) {
console.log('SUCCESS:');
console.log(response);
},
error: function(model, response) {
console.log('FAIL:');
console.log(response);
}
});
************************************************************/
Sever-side: Slim PHP w/ Paris/Idorm
class Donut extends Model {}
$app->post('/donut', function() use ($app) {
$donuts = Model::factory('Donut')->create();
/* EDIT: Works... but not the Slim way
$parameters = json_decode(file_get_contents('php://input'), true);
$donuts->name = $parameters['name'];
$donuts->sparkles = $parameters['sparkles'];
$donuts->creamFilled = $parameters['creamFilled']; */
/* SLIM: Using Slim Request Object */
$requestBody = $app->request()->getBody(); // <- getBody() of http request
$json_a = json_decode($requestBody, true);
$donuts->name = $json_a['name'];
$donuts->sparkles = $json_a['sparkles'];
$donuts->creamFilled = $json_a['creamFilled'];
$donuts->save();
// echo json_encode($parameters); // Prove you've captured POST data, send it back
}
Now my code is happily using the default settings of Backbone.js (no changes to sync) and sending proper model attribute information to my server which seems to be successfully accepting the data and saving it to my DB.
The key here seems to be this line...
/* $parameters = json_decode(file_get_contents('php://input'), true); */
// EDITED: getBody() method not documented in Develop Doc, only Stable # time of post
$requestBody = $app->request()->getBody();
If you want to know "what exactly is sent to the server", you should have a look at the Backbone.sync function in Backbone's code. It is very well documented, step-by-step. Then, the cleanest way to achieve what you need is to write you own sync function, inspired by Backbone's sync.
Also, a quick way to see what is sent to the server is to use your browser debug console (Network tab). You can compare here what is sent by Backbone vs. what is sent when you use $.post directly. Please post this information if you need more help !
backbone sends json data to your php backend server, which you should expose your RESTful api to respond to http verb like get, post, put, delete and etc.
your backend api is responsible for communicating with database.
I am not sure about SLIM PHP. it seems to handle the request. Can you paste the error messages?
I'm building a dynamic pie chart to show election results using the Google Visualization API and jQuery, and I had it (sort of) working on my local machine, and wanted to get some feedback, so uploaded it to an external server, now everything I try to load gives me a "No Data" error.
I've got two files, one which gets data from a database and converts it to JSON, and one which displays the visualization, depending on what areas are checked. You can see it here:
http://www2.lichfielddc.gov.uk/sandbox/pie.php?electionid=14
Any ideas where I'm going wrong?
Cheers
Think I may be on to something.
I downloaded it and tested it locally and as you said it works fine. However, in the datasource i was using (data.php) if I put a delay (sleep(1)) it stopped working. I think it was because you were drawing the chart out of the ajax success callback.
Try this:
function drawVisualization() {
$('.poll').click(function() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Party');
data.addColumn('number', 'Votes');
var polls = [];
$('.poll:checked').each(function(){
polls.push(this.value);
});
polls.join(",");
url = "http://www2.lichfielddc.gov.uk/sandbox/piedata.php?pollid=" + polls;
$.getJSON(url, function(d) {
data.addRows(d.length);
var items = [];
var num = 0;
$.each(d, function(i, o) {
console.log(o);
data.setValue(num, 0, o['party']);
data.setValue(num, 1, o['votes']);
num++;
});
new google.visualization.PieChart(document.getElementById('visualization')).
draw(data);
});
});
Stepping through the code: By the time you call the draw() method, the object 'data' has no data in it. This is likely because the modifications to the data object are not in the same scope.
I agree with Tribal that it's a timing issue, because the live version does work sometimes. There's a couple of other lurking bugs, that aren't behind the presenting issue, but ...
polls.join(",");
url = "http://www2.lichfielddc.gov.uk/sandbox/piedata.php?pollid=" + polls;
polls.join() returns a string, it doesn't do the join in-situ. And url isn't being declared as a local variable