Model Context Protocol (MCP) finally gives AI models a way to access the business data needed to make them really useful at work. CData MCP Servers have the depth and performance to make sure AI has access to all of the answers.
Try them now for free →Building Dynamic Web Pages with XML Data Using AngularJS
Create single-page applications with live connectivity to XML using the CData API Server.
AngularJS (Angular) is a structural framework for building dynamic web applications. Using Angular, the CData API Server, and the XML connector (or any of 300+ data source connectors), you can create single-page applications (SPAs) with live access to XML data. This article walks you through setting up the CData API Server and creating a simple SPA that dynamically builds and populates an HTML table with live XML data.
Setting Up the API Server
If you haven't already, download and install the CData API Server. After installation, run the application, download the XML connector from within the API Server, and configure your XML data connection. Then, configure API Server to generate OData feeds for any tables you want to access in your single-page application (SPA).
Enable CORS
AngularJS requires servers to have CORS (Cross-Origin Resource Sharing) enabled. To enable CORS, go to the Server tab under the Settings section in the API Server and adjust the following settings:
- Click the checkbox to Enable cross-origin resource sharing (CORS).
- Either click the checkbox to Allow all domains without '*' or specify the domain(s) that are allowed to connect in Access-Control-Allow-Origin.
- Set Access-Control-Allow-Methods to "GET, PUT, POST, OPTIONS".
- Set Access-Control-Allow-Headers to "Content-Type, Authorization".
- Click Save to save the changes made.
Connect to XML
To work with XML data using AngularJS, we start by creating and configuring a XML connection. Follow the steps below to configure the API Server to connect to XML data:
- First, navigate to the Connections page.
-
Click Add Connection and then search for and select the XML connection.
-
Enter the necessary authentication properties to connect to XML.
- After configuring the connection, click Save & Test to confirm a successful connection.
Configure API Server Users
Next, create a user to access your XML data through the API Server. You can add and configure users on the Users page. Follow the steps below to configure and create a user:
- On the Users page, click Add User to open the Add User dialog.
-
Next, set the Role, Username, and Privileges properties and then click Add User.
-
An Authtoken is then generated for the user. You can find the Authtoken and other information for each user on the Users page:
Creating API Endpoints for XML
Having created a user, you are ready to create API endpoints for the XML tables:
-
First, navigate to the API page and then click
Add Table
.
-
Select the connection you wish to access and click Next.
-
With the connection selected, create endpoints by selecting each table and then clicking Confirm.
Gather the OData URL
Having configured a connection to XML data, created a user, and added resources to the API Server, you now have an easily accessible REST API based on the OData protocol for those resources. From the API page in API Server, you can view and copy the API Endpoints for the API:

Just like with standard OData feeds, you can limit the fields returned by adding a $select parameter to the query. You can also include other standard OData URL parameters, such as $filter, $orderby, $skip, and $top. Refer to the help documentation for more details on supported OData queries.
Save the Authtoken
Select the user you created, then copy and save the authtoken for future use. You can also toggle on 'Token Expiration' and set the number of days based on your use case. Click Save to save the details.

Building a Single-Page Application
Now that we've completed the API Server setup, we're ready to build our SPA. For this simple demonstration, we'll include all our CSS, scripts, and Angular controllers in a single file, intentionally skipping the use of AngularJS services, factories, and custom directives.
CSS Definitions & Importing AngularJS Libraries
We start by defining some CSS rules to style our table and other components. We also import AngularJS from Google's CDN:
<title>AngularJS Application - CData API Server</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #f9f9f9;
}
h1 {
text-align: center;
color: #2c3e50;
margin-bottom: 30px;
}
.container {
display: flex;
gap: 40px;
justify-content: center;
flex-wrap: wrap;
}
.select-box {
display: flex;
flex-direction: column;
}
label {
font-weight: bold;
margin-bottom: 5px;
}
select, button {
padding: 8px;
border-radius: 5px;
border: 1px solid #ccc;
margin-bottom: 15px;
min-width: 220px;
}
button {
background-color: #3498db;
color: white;
border: none;
cursor: pointer;
}
button:disabled {
background-color: #bdc3c7;
cursor: not-allowed;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 30px;
}
th, td {
border: 1px solid #ccc;
padding: 10px;
text-align: left;
}
th {
background-color: #2980b9;
color: white;
}
tr:nth-child(even) {
background-color: #ecf0f1;
}
tr:nth-child(odd) {
background-color: #ffffff;
}
</style>
Creating & Referencing the Angular App and Controller Objects
To start building our AngularJS app, we first create the app and link it to a controller. We do this using the ng-app and ng-controller directives. These directives form the foundation of the app and tell AngularJS which part of the page it should manage and which controller handles the logic. In our case, we name the app DataApp and assign SimpleController to manage its behavior. We place these directives in the body tag so AngularJS manages everything inside it. This setup allows AngularJS to connect the data (model), the logic (controller), and the HTML (view) in a structured way. Here's how we declare it:
<body ng-app="DataApp" ng-controller="SimpleController">
<h1>AngularJS Application - CData API Server</h1>
With this setup, Angular initializes the app and connects it to the controller, allowing us to build the rest of the logic and interface.
Defining Our Controller
The controller is the brain of the AngularJS app as it contains all the logic that makes the application work. Inside the controller, we define a data object in the $scope that holds important information like the available tables, selected table, selected columns, and the table data we will fetch. The controller also makes HTTP requests to the CData API Server to retrieve this data. At the very beginning, the init() function is called to set up the application. It sends a GET request to http://localhost:8080/api.rsc with authorization headers, which fetches the list of tables available on the CData API Server. Once we have the list of tables, the first table is selected by default and its columns are immediately fetched.
The controller includes other functions too: one to fetch columns for the selected table getTableColumns(), and another to retrieve the data based on selected columns getTableData(). All of these use $http.get() with headers that include the username and authtoken for authentication. This ensures that only authorized users can access and interact with the server's resources.
<script>
var app = angular.module('DataApp', []);
app.controller('SimpleController', function($scope, $http) {
init(); // Initialization function
function init() {
$scope.data = {
availableTables: [],
availableColumns: [],
selectedTable: {},
selectedColumns: [],
tableData: []
};
const username = "your_username"; // Enter your username
const authtoken = "your_authtoken"; // Enter your authtoken & password
// Fetch tables
$http.get("http://localhost:8080/api.rsc", {
headers: { "Authorization": "Basic " + btoa(username + ":" + authtoken) }
}).then(function(response) {
$scope.data.availableTables = response.data.value;
$scope.data.selectedTable = $scope.data.availableTables[0];
$scope.getTableColumns(); // Load initial columns
});
}
Building the Web Page
Now that we have set up the logic, we can build the actual web page that users will interact with. This section uses HTML combined with AngularJS directives to dynamically bind data. The web page features dropdowns for selecting tables and columns, a button to fetch data, and a table to display the results. We populate the dropdowns using AngularJS's ng-options directive and bind the selected values using ng-model. The table appears conditionally only after the app fetches data. We place all these components inside a container div to keep the layout organized.
For example, when a user selects a table from the first dropdown, AngularJS automatically updates the selected table in the scope and fetches the corresponding columns. When the user clicks the "Get Data" button, AngularJS makes a request to the API server and displays the results in the table below. This setup ensures a seamless and responsive experience without reloading the page.
Select a Table
One of the core functionalities of the app allows users to choose from a list of tables available on the CData API Server. The app makes a GET request to the root API endpoint http://localhost:8080/api.rsc, which returns metadata about all accessible tables. The app then displays this list in a dropdown using ng-options, where each item represents a table. When users select a table from the dropdown, AngularJS stores the selected table in the model and automatically triggers a function to fetch the available columns for that table.
<select ng-options="table.name for table in data.availableTables track by table.url"
ng-model="data.selectedTable"
ng-change="getTableColumns()">
</select>
Select Columns
After users select a table, the app retrieves and displays the available columns in that table. It makes another API request, this time targeting a special $metadata endpoint. The call returns detailed information about the table's structure - such as the column names - which the app extracts and displays in a multi-select dropdown. Users can choose one or more columns (using Ctrl to select multiple columns) from this list, depending on the data they want to view.
$scope.getTableColumns = function () {
$scope.data.tableData = [];
$scope.data.selectedColumns = [];
const table = $scope.data.selectedTable.url;
const username = "your_username"; // enter the username
const authtoken = "your_authtoken"; // enter the authtoken/password
$http.get("http://localhost:8080/api.rsc/" + table + "/$metadata?@json", {
headers: { "Authorization": "Basic " + btoa(username + ":" + authtoken) }
}).then(function (response) {
const columns = response.data.items[0]["odata:cname"];
$scope.data.availableColumns = columns.map((col, index) => ({ id: index, name: col }));
});
};
Get Table Data
After the user selects a table and the desired columns, they click the "Get Data" button to fetch the actual records from the database. The app constructs a query using the $select parameter to retrieve only the selected columns. It then sends a GET request to a URL like: http://localhost:8080/api.rsc/TableName?$select=Column1,Column2.
The app efficiently uses the API to avoid pulling unnecessary data and ensures fast performance. It stores the returned data in data.tableData, which the HTML table uses for display. When the user doesn't select any columns, the app disables the button to prevent accidental or empty queries.
$scope.getTableData = function () {
const table = $scope.data.selectedTable.url;
const columnsArray = $scope.data.selectedColumns;
const columnString = columnsArray.map(col => col.name).join(',');
const username = "your_username";
const authtoken = "your_authtoken";
if (table !== "" && columnString !== "") {
$http.get(`http://localhost:8080/api.rsc/${table}?$select=${columnString}`, {
headers: { "Authorization": "Basic " + btoa(username + ":" + authtoken) }
}).then(function (response) {
$scope.data.tableData = response.data.value;
});
} else {
$scope.data.tableData = [];
}
}
Display the Table Data
In the final step, we use a simple HTML table structure to display the fetched data. We generate the headers from the selected columns and populate the rows with the returned data. AngularJS's ng-repeat directive enables this dynamic rendering. We loop through the selectedColumns array to create the column headers and iterate over the tableData array to fill the rows.
<table ng-if="data.tableData.length > 0">
<tr>
<th ng-repeat="column in data.selectedColumns | orderBy:'name'">{{column.name}}</th>
</tr>
<tr ng-repeat="row in data.tableData">
<td ng-repeat="column in data.selectedColumns">{{ row[column.name] }}</td>
</tr>
</table>
Complete App
Here's the complete HTML script to build the AngularJS application from scratch:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AngularJS Application - CData API Server</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #f9f9f9;
}
h1 {
text-align: center;
color: #2c3e50;
margin-bottom: 30px;
}
.container {
display: flex;
gap: 40px;
justify-content: center;
flex-wrap: wrap;
}
.select-box {
display: flex;
flex-direction: column;
}
label {
font-weight: bold;
margin-bottom: 5px;
}
select, button {
padding: 8px;
border-radius: 5px;
border: 1px solid #ccc;
margin-bottom: 15px;
min-width: 220px;
}
button {
background-color: #3498db;
color: white;
border: none;
cursor: pointer;
}
button:disabled {
background-color: #bdc3c7;
cursor: not-allowed;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 30px;
}
th, td {
border: 1px solid #ccc;
padding: 10px;
text-align: left;
}
th {
background-color: #2980b9;
color: white;
}
tr:nth-child(even) {
background-color: #ecf0f1;
}
tr:nth-child(odd) {
background-color: #ffffff;
}
</style>
</head>
<body ng-app="DataApp" ng-controller="SimpleController">
<h1>AngularJS Application - CData API Server</h1>
<div class="container">
<div class="select-box">
<label>Select a XML Table</label>
<select name="tableDropDown" id="tableDropDown"
ng-options="table.name for table in data.availableTables track by table.url"
ng-model="data.selectedTable"
ng-change="getTableColumns()">
</select>
<button name="getTableData" id="btnGetTableData"
ng-click="getTableData()"
ng-disabled="data.selectedColumns.length == 0">
Get {{data.selectedTable.name}} Data
</button>
</div>
<div class="select-box">
<label>Select Columns</label>
<select name="columnMultiple" id="columnMultiple"
ng-options="column.name for column in data.availableColumns | orderBy:'name' track by column.id"
ng-model="data.selectedColumns"
multiple>
</select>
</div>
</div>
<table ng-if="data.tableData.length > 0">
<tr>
<th ng-repeat="column in data.selectedColumns | orderBy:'name'">{{column.name}}</th>
</tr>
<tr ng-repeat="row in data.tableData">
<td ng-repeat="column in data.selectedColumns">{{ row[column.name] }}</td>
</tr>
</table>
<script>
var app = angular.module('DataApp', []);
app.controller('SimpleController', function($scope, $http) {
init(); // Call init on load
function init() {
// Define our app’s data structure
$scope.data = {
availableTables: [],
availableColumns: [],
selectedTable: {},
selectedColumns: [],
tableData: []
};
// Set your CData API Server credentials here
const username = "your_username"; // Replace with your CData API Server username
const authtoken = "your_authtoken"; // Replace with your actual authtoken
// Step 1: Fetch all available tables from the API server
$http.get("http://localhost:8080/api.rsc", {
headers: {
"Authorization": "Basic " + btoa(username + ":" + authtoken)
}
}).then(function(response) {
$scope.data.availableTables = response.data.value;
// Automatically select the first table (optional)
$scope.data.selectedTable = $scope.data.availableTables[0];
// Fetch columns for the selected table
$scope.getTableColumns();
});
}
// Step 2: Fetch column metadata for the selected table
$scope.getTableColumns = function () {
// Reset previous selections
$scope.data.tableData = [];
$scope.data.selectedColumns = [];
const table = $scope.data.selectedTable.url;
// Credentials again if needed (can reuse)
const username = "your_username";
const authtoken = "your_password";
if (table !== "") {
$http.get("http://localhost:8080/api.rsc/" + table + "/$metadata?@json", {
headers: {
"Authorization": "Basic " + btoa(username + ":" + authtoken)
}
}).then(function (response) {
const columns = response.data.items[0]["odata:cname"];
// Map each column to an object with id and name
$scope.data.availableColumns = columns.map((col, index) => ({
id: index,
name: col
}));
});
}
}
// Step 3: Fetch table data based on selected columns
$scope.getTableData = function () {
const table = $scope.data.selectedTable.url;
const columnsArray = $scope.data.selectedColumns;
// Convert selected column objects into comma-separated names
const columnString = columnsArray.map(col => col.name).join(',');
// Again, set username and authtoken if needed
const username = "your_username";
const authtoken = "your_authtoken";
if (table !== "" && columnString !== "") {
$http.get(`http://localhost:8080/api.rsc/${table}?$select=${columnString}`, {
headers: {
"Authorization": "Basic " + btoa(username + ":" + authtoken)
}
}).then(function (response) {
// Store the retrieved data to be displayed in the table
$scope.data.tableData = response.data.value;
});
} else {
// If nothing selected, clear data
$scope.data.tableData = [];
}
}
});
</script>
</body>
</html>
Run the AngularJS application using the given command:
http-server -p "port_number"

Free Trial & More Information
If you are interested in connecting to your XML data (or data from any of our other supported data sources) from Web applications built with Angular, download a free, 30-day trial of our API Server today! For more general information on our API Server and to see what other data sources we support, refer to our API Server page.