I'm rather new to web development and I am trying to get a better understanding of servers and databases and what the limitations are of client-side development.
Right now, I'm learning AngularJS and I have been able to create simple CRUD applications such as a to-do list or online store. Currently, I have always been creating the data for my web applications through regular JavaScript Arrays/Objects.. but now I want to be able to permanently edit/change this data through my own CMS user interface.
Some research has led me to use JSON and the angular $http service to request JSON data from the server.
Now, I am trying to update this JSON data Asynchronously with angularJS and I'm not sure how to do this (see below for my attempt).
Simple To-Do List Application
<body ng-controller="ToDoCtrl">
<div class="container">
<div class="page-header">
<h1>
{{todo.user}}'s To Do List
<span class="label label-default" ng-hide="incompleteCount() == 0"
ng-class="warningLevel()">
{{ incompleteCount() }}
</span>
</h1>
</div>
<div class="panel">
<div class="input-group">
<input class="form-control" ng-model="actionText">
<span class="input-group-btn">
<button class="btn btn-success" ng-click="addItem(actionText, todo.items)">Add</button>
</span>
</div><!-- end input-group -->
<table class="table table-striped">
<thead>
<tr>
<th>Descriptions</th>
<th>Done</th>
<th>Remove</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in todo.items | checkedItems: showComplete | orderBy: 'action'">
<td>{{item.action}}</td>
<td><input type="checkbox" ng-model="item.done"></td>
<td><button ng-click="deleteItem(item, todo.items)" class="btn btn-danger">Delete</button></td>
</tr>
</tbody>
</table>
<div class="checkbox-inline">
<label><input type="checkbox" ng-model="showComplete">Show Complete</label>
</div>
<div class="input-group">
<button ng-click="save()" class="btn btn-primary">Save Changes</button>
<p>{{msg}}</p>
</div>
</div><!-- end panel -->
</div>
<!-- Vendor JS -->
<!-- Angular -->
<script src="vendors/angular.min.js"></script>
<!-- Modules -->
<script src="app.js"></script>
</body>
app.js
var model = {
user: "Alex"
};
angular.module('todoApp', [])
.run(function($http) {
$http.get("todo.json").success(function(data) {
model.items = data;
});
})
.controller('ToDoCtrl', ['$scope', '$http', function($scope, $http) {
$scope.todo = model;
$scope.incompleteCount = function() {
var count = 0;
angular.forEach($scope.todo.items, function(item) {
if (!item.done) {
count++
}
});
return count;
};
$scope.warningLevel = function() {
return $scope.incompleteCount() < 3 ? "label-success" : "label-warning";
};
$scope.addItem = function(actionText, sourceArray) {
sourceArray.push(
{
action: actionText,
done: false,
}
);
$scope.actionText = '';
};
$scope.deleteItem = function(item, sourceArray) {
for(var i = 0, j = sourceArray.length; i < j; i++) {
if(item.action == sourceArray[i].action) {
sourceArray.splice(i, 1);
return;
}
}
};
$scope.save = function() {
$http.post('C:\Users\Alex\Desktop\Development\"Web Design"\2015\todoApp\public\src\todo.json', $scope.todo.items).then(function(data) {
$scope.msg = 'Data saved '+ JSON.stringify($scope.todo.items);
});
};
}])
.filter("checkedItems", function() {
return function(items, showComplete) {
var resultArr = [];
angular.forEach(items, function(item) {
if(item.done == false || showComplete == true) {
resultArr.push(item);
}
});
return resultArr;
}
});
I used this Post for the $scope.save function, but I receive an error: "XMLHttpRequest cannot load. Cross origin requests are only supported for protocol schemes: http, data, chrome..."
$scope.save = function() {
$http.post('C:\Users\Alex\Desktop\Development\"Web Design"\2015\todoApp\public\src\todo.json', $scope.todo.items).then(function(data) {
$scope.msg = 'Data saved '+ JSON.stringify($scope.todo.items);
});
};
Basically, I just want to update my todo.json file with the current contents of my $scope.todo.items array. I think the simplest way would be to delete the current contents of the JSON data and replace with current contents of $scope.todo.items, but I don't know much about this stuff.
Thanks for any help.
Let's start with some concepts first:
1.- a JSON file is just a text file, it can be a product of a database query or it can be generated dynamically by a server, but at the end of the day is just a text file.
2.- the $http service deals with request to HTTP servers, like the Apache Web Server, or NodeJS Http Server, running your software with backend technology, there's a multitude of servers and some can run in your machine as well as remotely.
3.- GET and POST are HTTP methods, that must be made to a server running your backend. The most common one, the GET method is usually used to get data from a server, like text files, or JSON files.
4.- In a file server like the one Windows provides you for local development, the GET method can bring up files from your file system (like "todo.json"). This file server is really basic, it just accepts GET requests and that's all it should accept.
5.- In your backend software, you define an Endpoint, that should be an address where your backend is ready to receive a POST request, and you also need to define what does this POST request do.
It's a long step between going from your angular file to defining an endpoint in a server, you will go across different technologies, the Angular framework is not a backend technology, it's a frontend library.
If you want to get into these concepts, a TODO List project is a great first project, sites like http://www.todobackend.com/ can show you all sort of TODO projects in a myriad of different backends and frontends.
Related
So I am currently working on an application that uses WebAPI and AngularJS to search for some data from a SQL table and display it on a webpage for the user to select multiple individual rows of data. I am inserting the selected rows into a separate JSON array (availableclients) that I would like to insert into a separate SQL table. What is the best method for me to take my array of JSON data and insert it into a different SQL table. I will attach the code I am currently using to get the data.
Controller.js
var app = angular.module('myApp', []);
app.controller("myController", function ($scope, $http) {
function getCert(myid) {
$http.get('api/Cert/Get/', { params: { id : myid } })
.success(function (data) {
$scope.selectedclients = data;
})
}
$scope.searchClick = function() {
getCert($scope.myid);
}
$scope.moveItem = function (item, from, to) {
var idx = from.indexOf(item);
if (idx != -1) {
from.splice(idx, 1);
to.push(item);
}
};
$scope.availableclients = [];
});
HTML
<html data-ng-app="myApp">
<body data-ng-controller ="myController">
My_ID: <input type="text" ng-model="my_id" />
<input type="submit" value="Search" ng-click="searchClick()" />
<select size="10" multiple ng-model="selected" ng-options="i.unit_id for i in selectedclients" style="width: 400px"></select>
<div class="btn-group">
<button title="Remove From List" class="btn btn-default" ng-click="moveItem(available[0], availableclients,selectedclients)"><i class="glyphicon glyphicon-chevron-left"></i></button>
<button title="Add To List" class="btn btn-default" ng-click="moveItem(selected[0], selectedclients,availableclients)"><i class="glyphicon glyphicon-chevron-right"></i></button>
</div>
<select size="10" multiple ng-model="available" ng-options="i.unit_id for i in availableclients" style="width: 400px"></select>
</body>
</html>
The code I have is working fine I am just at a loss for how to take my availableclients JSON array and insert it into my SQL table. This is probably really easy to do but all my searches are coming up blank on what I am looking for exactly. Any help is appreciated. Thanks!
EDIT 1: On recommendation of a comment I am adding the Controller I used for the Web API get. Again thanks for any advice!
public class CertController : ApiController
{
CertEntities objapi = new CertEntities();
[HttpGet]
public IEnumerable<AngularCoCSelect_Result> Get(int id)
{
return objapi.AngularCoCSelect(id).AsEnumerable();
}
}
It is quite wide question... also you are showing the client code and nothing from the backend which eventually will store the data (suspecting as you put asp.net tag also)
So based on that You can use Entity Framework to store your data into a database. You may find a lot of articles on the internet about implementing this approach.
This solution as guide line
need back-end api that accept array of object (json) for your clients ASP.NET or PHP then
You need to add function in Angular within your controller for submit client data to the server using $http.put or $http.post
$scope.submitClientData = function(){
$http.post('webapi/clients' , { clients:$scope.availableclients })
}
You may call function from submit button
<button ng-click='submitClientData()'>Save</button>
I am a bit confused on when to use what in angularjs. I know the basic concept of controller, service/factory and directive but I'm not sure what to use in my case.
Scenario: A form that allows a user to post a link. The form itself requests some information about the link from an external service and presents it immediately to the user. Posting is possible via the API of a NodeJS app (not that that matters). The form should be reusable so I want the code to be DRY. I don't like the use of ng-include since directives seem to be the way to go.
So far I have a factory to deal with requesting information (linkservice) and a factory to deal with creating posts (posts). I then use a directive with it's own controller to display the form and handle user actions. But I'm not sure if I should move the content of the directives' controller into a normal controller or even a service, since directives shouldn't deal with requesting data (as I understand). Or maybe this is already the right way.
The Directive
// The form to publish a new post
myModule.directive('postForm', [
'linkservice',
'posts',
'$state',
function(linkservice, posts, $state){
return {
templateUrl : '/js/app/views/partials/post-form.html',
controller: function ($scope) {
$scope.analyzeURL = function() {
$scope.filtered_url = $scope.link.url.match(/(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,#?^=%&:/~+#-]*[\w#?^=%&/~+#-])?/gmi);
if($scope.filtered_url !== null) {
linkservice.extractURL($scope.filtered_url).then(function(res) {
var website_info = res.data;
$scope.link = {
title: website_info.title,
description: website_info.description,
medium: website_info.provider_name,
medium_thumbnail_url: website_info.favicon_url,
url: $scope.filtered_url[0]
}
// Image
if(website_info.images.length > 0 && website_info.images[0].width >= 500) {
$scope.link.thumbnail_url = website_info.images[0].url;
} else { $scope.link.thumbnail_url = null; }
// Keywords
$scope.link.keywords = [];
if(website_info.keywords.length >= 2) {
$scope.link.keywords[0] = website_info.keywords[0].name;
$scope.link.keywords[1] = website_info.keywords[1].name;
}
$scope.show_preview = true;
});
}
},
// addPost
$scope.addPost = function(){
if(!$scope.post || $scope.post.text === '' || !$scope.link || $scope.link.url === '') { return; }
posts.create({
post: $scope.post,
link: $scope.link
}).success(function() {
delete $scope.post;
delete $scope.link;
});
}
}
}
}]);
The template
<form ng-submit="addPost()" style="margin-top:30px;">
<h3>Add a new Post</h3>
<div class="form-group">
<input type="text"
class="form-control"
placeholder="URL"
ng-model="link.url" ng-change="analyzeURL()"></input>
</div>
<div class="form-group">
<textarea type="text"
class="form-control"
placeholder="Description / TLDR"
ng-model="post.text" ></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Post</button>
</div>
<div class="form-group">
<input type="hidden" ng-model="link.title"></input>
<input type="hidden" ng-model="link.description"></input>
<input type="hidden" ng-model="link.thumbnail_url"></input>
<input type="hidden" ng-model="link.medium"></input>
<input type="hidden" ng-model="link.medium_thumbnail_url"></input>
<input type="hidden" ng-model="link.keywords"></input>
</div>
<div class="lp-container" ng-show="show_preview">
<span class="lp-provider"><img src="{{link.medium_thumbnail_url}}" class="lp-favicon"> {{link.medium}}</span>
<h2 class="lp-title">{{link.title}}</h2>
<div class="lp-description">{{link.description}}</div>
<img class="lp-thumbnail" ng-show="link.thumbnail_url" src="{{link.thumbnail_url}}">
<div class="lp-keywords">
<span ng-repeat="kw in link.keywords" class="lp-keyword">{{kw}}</span>
</div>
</div>
</form>
The best way to do it, is to keep in mind that Angular is a MVVM-like framework.
Your directives define the view, how to print data, events, etc.
Your services are singletons so they are the best place to store data and to put all data management (web services requests, etc). As they will be instanciated only once, your data won't be duplicated.
Your controllers are instanciated each time you link them to a directive (ng-controller etc.). So you should avoid to store data here. Controllers should be used as link between services and directives. They could contains low-level data check etc, then call the services.
In your example you can simplify you code by moving your controller in another place to avoid to mix it all. Ex: Here your directive directly depends to linkservice when it's only the controller which needs it.
I am using Angular and TingoDB (Mongo) inside Node Webkit for a single page application. However I have a strange problem that I have been unable to resolve.
When I use an object literal (option 2) the data displays correctly in the html page. However changing the code to return data from the database (option 1) the results do not appear on the html page. I have converted both styles of data into the a JSON string to prove consistency and then using the angular.fromJSON to return an object. Both methods return the same JSON string in console.log and before anyone asks I have either Option 1 or Option 2 commented out so both are not running concurrently.
I have copied the JSON string based on the data passed from TingoDB into the console.log and re-entered it into the code below to ensure that no differences between the 2 versions of the data existed without changing any other code, but the problem still persists.
Can anyone shed light on why this occurs and how to fix it?
var app = angular.module('myApp', []);
var Engine = require('tingodb')(),
assert = require('assert');
var db = new Engine.Db('./db', {});
var collection = db.collection("clean.db");
app.controller('tingoDataCtrl', ['$scope', function($scope) {
function getData(callback) {
//Option 1
collection.find().toArray(function(err, docs){
callback (JSON.stringify(docs));
});
//Option 2
var docs = [
{name:"tingo1", description:"56",_id:2},
{name:"tingo2", description:"33",_id:3},
{name:"tingo3", description:"22",_id:4},
{name:"tingo4", description:"76",_id:5},
{name:"tingo5", description:"99",_id:6}
];
callback (JSON.stringify(docs));
}
function info(b) {
// I'm the callback
console.log(b);
$scope.items = angular.fromJson(b)
}
getData(info);
}]);
And the Html
<body ng-app="myApp" id="main">
<div class="page page-data ng-scope">
<section class="panel panel-default" ng-controller="tingoDataCtrl">
<div class="panel-heading"><span class="glyphicon glyphicon-th"></span> Tingo Data</div>
<table class="table">
<thead>
<th class="col-md-4">
Name
</th>
<th class="col-md-8">
Description
</th>
<th class="col-md-8">
ID
</th>
<th></th>
<tr>
</tr>
</thead>
<tbody>
<!-- <tr class="reveal-animation" ng-repeat="item in items | filter:query"> -->
<tr ng-repeat="item in items | filter:query">
<td>{{item.name}}</td>
<td>{{item.description}}</td>
<td>{{item._id}}</td>
</tr>
</tbody>
</table>
</section>
</div>
<script src="js/tingo_problem.js"></script>
</body>
TingoDB is an asynchronous API which will work in the background without stop your app. This means that a syncronous code have no time to wait for an answer and in return it gives undefined.
In your case, you have done a asynchronous call, and it returns correctly the answer to the memory, but too late, the DOM have been updated with undefined already even if your javascript has the data (try console.log to see that it was there).
Angular has a way to be forced to update again the DOM with the new elements of the controller. it is called $apply. And the best way to use it to avoid unexpected behaviours is:
function info(b) {
// I'm the callback
console.log(b);
$scope.items = angular.fromJson(b);
if (!$scope.$$phase) {
$scope.$apply(); //forces update the view
}
}//$scope is NECESARY to be defined in the controler, avoid using it with "ControlerAs"
I have a Play framework project using AngularJS for its views. The controller responsible for querying data makes 2 requests, each returning a JSON block. The first one works correctly and it's displayed properly. The second one's data is pretty much destroyed by Angular (example below).
The JSONs are created correctly prior to being rendered, as it's shown through the application's log.
This is the correct JSON (taken from the Play Framework routed method's log):
{"id":5,"name":"auditoria","url":null,"version":1,"methods":[]}
This is how AngularJS prints it. It tokenizes :
[{},{"0":"a","1":"u","2":"d","3":"i","4":"t","5":"o","6":"r","7":"i","8":"a"},{},{},{"length":0}]
And here's the controller:
app.controller("ViewCtrl", [ "$scope", "$resource", "$routeParams", "apiUrl",
function($scope, $resource, $routeParams, apiUrl) {
var ServiceList = $resource(apiUrl + "/services");
$scope.services = ServiceList.query(); //JSON is displayed properly
if ($routeParams.id) {
jsonServicoQuery = apiUrl + "/services/" + $routeParams.id
var Service = $resource(jsonServicoQuery);
$scope.currentService = Service.query(); //JSON is butchered
}
} ]);
Here's the HTML:
<div class="row">
<div class="col-md-3">
<div class="bs-sidebar hidden-print" role="complementary">
<ul class="nav bs-sidenav">
<li><i class="fa fa-plus"></i></li>
<li ng-repeat="s in services| orderBy:'name'">{{s.nome}}
<ul>
<li ng-repeat="m in s.methods| orderBy:'name'">{{m.name}}
[{{m.id}}]</li>
</ul></li>
</ul>
</div>
</div>
<div class="col-md-9" role="main">
<div class="bs-docs-section">
<div class="page-header">
<!-- displaying the whole currentService JSON for debugging purposes -->
{{currentService}}
</div>
</div>
</div>
</div>
Anybody has any clues about what am I doing wrong?
Update: Service.query() executes the method routed by Play Framework.
Route configuration:
GET /api/services/:id controllers.Services.show(id: String)
And controllers.Services.show(id: String) implementation:
public static Result show(String id) {
Service s = Service.findById(id);
//JSON displayed here is correct, log below
Logger.info("At Services show, json " + Json.toJson(s));
return ok(Json.toJson(s));
}
Log:
[info] application - At Services show, json {"id":5,"name":"auditoria","url":null,"version":1,"methods":[]}
I managed to find the problem, I should have used $scope.currentService = Service.get(); instead of $scope.currentService = Service.query();
Right now I am working with AngularJS on a web interface which should have similar behavior like Dev HTTP Client. I can't find a way how to add headers in the way like DHC does.
I'm trying to make it somehow like this, but it isn't working since array is initialized empty:
<div ng-repeat="header in headersCollection.headers">
<input ng-model="header.name" type="text"/> :
<input ng-model="header.value" type="text"/>
</div>
<button type="button" ng-click="addNewHeader()">Add</button>
Headers should be stored inside this object and be available for creating, editing and removing through web interface. Just like in DHC.
$rootScope.headersCollection = {
headers : []
}
Any idea / link / answer are highly appreciated and answered immidiately.
Thank you.
Just make an "empty" header object in the headers collection. See http://jsfiddle.net/e8MEx/
Of course you will want to throw in some validation to make sure they are values before adding another one and potentially add the ability to remove an item:
JavaScript:
var mod = angular.module("myApp", []);
mod.run(["$rootScope", function($rootScope) {
//start the array with one empty value for header
$rootScope.headersCollection = {
headers : [{name: "", value: ""}]
}
}]);
mod.controller("MainController", ["$scope", "$rootScope", function ($scope, $rootScope) {
$scope.headersCollection = $rootScope.headersCollection
$scope.addNewHeader = function () {
//push a new empty value onto the array.
$scope.headersCollection.headers.push({name: "", value: ""});
}
}]);
HTML:
<div ng-app="myApp" ng-controller="MainController">
<div ng-repeat="header in headersCollection.headers">
<input ng-model="header.name" type="text"/> :
<input ng-model="header.value" type="text"/>
</div>
<button type="button" ng-click="addNewHeader()">Add</button>
<p>{{headersCollection.headers}}</p>
</div>