These instructions will take you through the process of creating your first forms-angular application, which will consist of an input form for a simple model, a listing showing data already on file, search, menus and a simple report with several features including outputting to PDF.
If you want a sneak preview of what the input form will look like when you have finished take a look at one of the examples - there is a simple input form here and a more feature-filled one here.
(if you need to - you may already have these applications / packages installed)
Please note: the "just a few minutes" in the title starts from after these are installed!
npm install -g express
npm install -g bower
You have the option at this point (if you are really lazy and you aren't worried about
stuff happening in scripts you have never seen) you can do
wget https://raw.github.com/mchapman/forms-angular/dev/app/partials/get-started/get-started.sh && bash get-started.sh
.
If you want to understand a bit more about how the example app works you are better off working through
the instructions below.
Several other packages / components are installed by npm / bower, and some of them need to be understood before you can make best use of forms-angular (though you can do simple forms without). forms-angular is based on what has become known as the MEAN stack - made up of Mongo, Express and Node (all installed above) and Angular JS. All of these come with extensive documentation on their websites.
In addition to the MEAN stack, some grasp of Mongoose is a good idea for all but the simplest of forms, and knowledge of Twitter Bootstrap is recommended.mkdir myapp
cd myapp
express
npm install
npm install forms-angular --save
npm install mongoose --save
cd public
bower install forms-angular
cd ..
var mongoose = require('mongoose');
var formsAngular = require('forms-angular');
mongoose.connect('mongodb://localhost/mydb');
var Schema = mongoose.Schema;
var ApplicantSchema = new Schema({
surname: {type:String, required:true, index:true},
forename: {type:String, index:true}
});
var Applicant = mongoose.model('Applicant', ApplicantSchema);
var DataFormHandler = new (formsAngular)(app);
DataFormHandler.addResource('applicant', Applicant); // Create and add more schemas to taste
and comment out any lines that start with app.get(
:
// app.get('/', routes.index);
// app.get('/users', user.list);
Create the following files:
public/index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My App</title>
<script type="text/javascript" src="bower_components/jquery/jquery.js"></script>
<script type="text/javascript" src="bower_components/jquery-ui/ui/jquery-ui.js"></script>
<link rel="stylesheet" href="bower_components/jquery-ui/themes/smoothness/jquery-ui.css">
<script type="text/javascript" src="bower_components/angular/angular.js"></script>
<script type="text/javascript" src="bower_components/angular-route/angular-route.js"></script>
<script type="text/javascript" src="bower_components/angular-sanitize/angular-sanitize.js"></script>
<script type="text/javascript" src="bower_components/underscore/underscore.js"></script>
<script type="text/javascript" src="bower_components/angular-ui-bootstrap-bower/ui-bootstrap-tpls.js"></script> <!-- is now in fngTabs.js -->
<script type="text/javascript" src="bower_components/angular-ui-date/src/date.js"></script>
<script type="text/javascript" src="bower_components/angular-ui-select2/src/select2.js"></script>
<script type="text/javascript" src="bower_components/ngInfiniteScroll/ng-infinite-scroll.js"></script>
<script type="text/javascript" src="bower_components/jspdf/dist/jspdf.source.js"></script>
<script type="text/javascript" src="bower_components/bootstrap/js/bootstrap-transition.js"></script>
<script type="text/javascript" src="bower_components/bootstrap/js/bootstrap-collapse.js"></script>
<link rel="stylesheet" href="bower_components/ng-grid/ng-grid.css">
<script type="text/javascript" src="bower_components/ng-grid/build/ng-grid.js"></script>
<link rel="stylesheet" href="bower_components/select2/select2.css">
<script type="text/javascript" src="bower_components/angular-elastic/elastic.js"></script>
<script type="text/javascript" src="bower_components/select2/select2.js"></script>
<!--[if lt IE 9]>
<script src="bower_components/html5shiv-dist/html5shiv.js"></script>
<![endif]-->
<!--forms-angular stuff-->
<link rel="stylesheet" href="bower_components/forms-angular/forms-angular.css">
<script type="text/javascript" src="bower_components/forms-angular/forms-angular.js"></script>
<script src="get-started/myapp.js"></script>
</head>
<!--The NavCtrl controller is responsible for parsing the URL and loading the menu for the model and form-->
<body ng-app="myApp" ng-controller="NavCtrl">
<div class="navbar row">
<div class="navbar-inner">
<ul class="nav">
<li><a href="get-started/">Home</a></li>
<!--if The NavCtrl has loaded any menu items we display them here-->
<li ng-show="items.length > 0" class="dropdown" >
<a class="dropdown-toggle">
{{contextMenu}}
</a>
<ul class="dropdown-menu">
<li ng-repeat="choice in items">
<a class="dropdown-option" href="{{choice.url}}" ng-click="doClick($index)">{{choice.text}}</a>
</li>
</ul>
</li>
</ul>
<div class="span9">
<!--The SearchCtrl handles AJAX searches of the database (which can be customised)-->
<div class="global-search" ng-controller="SearchCtrl">
<form class="navbar-search pull-right">
<div id="search-cg" class="control-group" ng-class="errorClass">
<input type="text" ng-model="searchTarget" class="search-query" placeholder="Search">
</div>
</form>
<div class="search-results" ng-show="results.length >= 1">
<div ng-repeat="result in results">
<a class="search-result" ng-href="#/{{result.resource}}/{{result.id}}/edit">
{{result.resourceText}} {{result.text}}
</a>
</div>
<div ng-show="moreCount > 0">(plus more - continue typing to narrow down search...)</div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div ng-view></div>
</div>
</body>
</html>
public/myapp.js
var myApp = angular.module('myApp', ['formsAngular']);
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/index', {templateUrl: 'partials/index.html'} ).
when('/:model/:id/edit', {templateUrl: 'partials/base-edit.html'}).
when('/:model/new', {templateUrl: 'partials/base-edit.html'}).
when('/:model', {templateUrl: 'partials/base-list.html'}).
otherwise({redirectTo: '/index'});
}]);
public/partials/index.html
<h1>Applicants</h1>
<ul>
<li>Create a new applicant at <code><a href="http://0.0.0.0:3000/#/applicant/new">http://0.0.0.0:3000/#/applicant/new</a></code></li>
<li>List applicants at <code><a href="http://0.0.0.0:3000/#/applicant">http://0.0.0.0:3000/#/applicant</a></code></li>
<li>Edit existing applicants by clicking on the links in the <a href="http://0.0.0.0:3000/#/applicant">list</a></li>
<li>Show applicants in a <a href="http://0.0.0.0:3000/#/analyse/applicant">grid report</a></li>
</ul>
public/partials/base-edit.html
<form name="myForm" class="form-horizontal compact" ng-controller="BaseCtrl">
<!--This is the header section-->
<div class="page-header edit-header row-fluid">
<!--The left hand side contains important fields from the data-->
<div class="header-lhs span9">
<h4>{{modelNameDisplay}} :
<span ng-repeat="field in listSchema">{{getListData(record, field.name)}} </span>
</h4>
</div>
<!--The right hand side contains buttons to Save, Cancel, Delete and create New-->
<div class="header-rhs span2">
<div form-buttons></div>
</div>
</div>
<div class="container-fluid page-body edit-body">
<!--This section only appears when there is an error message to display-->
<div ng-show="errorMessage" class="row-fluid">
<div class="span6 offset3 alert alert-error">
<button type="button" class="close" ng-click="dismissError()">×</button>
<h4>{{alertTitle}}</h4>
<div ng-bind-html="errorMessage"></div>
</div>
</div>
<!--This is the section that displays the input fields-->
<div ng-if="!panes.length">
<form-input schema="formSchema" formStyle="horizontal"></form-input>
</div>
<div ng-if="panes.length">
<form-input schema="panes" formStyle="horizontal"></form-input>
</div>
</div>
</form>
public/partials/base-list.html
<div ng-controller="BaseCtrl">
<div class="container-fluid page-header list-header">
<div class="row-fluid">
<div class="header-lhs span9">
<h1>{{modelNameDisplay}}</h1>
</div>
<div class="header-rhs span2">
<button class="btn pull-right" ng-click="new()"><i class="icon-plus"></i> New</button>
</div>
</div>
</div>
<div class="container-fluid page-body list-body">
<div class="row-fluid" infinite-scroll="scrollTheList()">
<a ng-repeat="record in recordList" ng-href="/#/{{modelName}}/{{formPlusSlash}}{{getId(record)}}/edit">
<div class="list-item">
<div class="span{{12/listSchema.length}}" ng-repeat="field in listSchema">{{getListData(record, field.name)}} </div>
</div>
</a>
</div>
</div>
</div>
public/partials/base-analysis.html
<div ng-controller="AnalysisCtrl">
<div class="container-fluid page-header report-header">
<div class="row-fluid">
<div class="header-lhs span7">
<h1>{{ reportSchema.title }}</h1>
</div>
<form name="paramForm" ng-show="paramSchema" class="span4 header-rhs form form-horizontal compact">
<form-input schema="paramSchema"></form-input>
</form>
</div>
</div>
<div class="container-fluid page-body report-body">
<div ng-show="errorMessage" class="row-fluid">
<div class="span6 offset3 alert alert-error">
<button type="button" class="close" ng-click="dismissError()">×</button>
<h4>{{alertTitle}}</h4>
<div ng-bind-html="errorMessage"></div>
</div>
</div>
<div class="row-fluid">
<div class="gridStyle" ng-grid="gridOptions"></div>
</div>
</div>
</div>
node app.js
Visit http://0.0.0.0:3000
in your browser.