├── .gitignore
├── add_angular
├── app
│ ├── contacts
│ │ ├── contacts.css
│ │ ├── contacts.html
│ │ ├── contact-add-edit.html
│ │ └── contacts.js
│ ├── login
│ │ ├── login.html
│ │ ├── register.html
│ │ ├── login.js
│ │ └── login.css
│ ├── app.css
│ ├── groups
│ │ ├── groups.html
│ │ ├── group-add-edit.html
│ │ └── groups.js
│ ├── contact-info
│ │ ├── contact-info.html
│ │ ├── contact-info-add-edit.html
│ │ └── contact-info.js
│ ├── 404.html
│ └── app.js
├── .DS_Store
└── index.html
├── add_angular.zip
├── add_angular.dfpkg
├── description.json
├── schema.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
--------------------------------------------------------------------------------
/add_angular/app/contacts/contacts.css:
--------------------------------------------------------------------------------
1 | md-tab-content {
2 | display: initial;
3 | }
--------------------------------------------------------------------------------
/add_angular.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/angular-sdk/HEAD/add_angular.zip
--------------------------------------------------------------------------------
/add_angular.dfpkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/angular-sdk/HEAD/add_angular.dfpkg
--------------------------------------------------------------------------------
/add_angular/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dreamfactorysoftware/angular-sdk/HEAD/add_angular/.DS_Store
--------------------------------------------------------------------------------
/description.json:
--------------------------------------------------------------------------------
1 | {
2 | "name":"Address Book for AngularJS",
3 | "description":"An address book app for AngularJS showing user registration, user login, and CRUD.",
4 | "type": 1,
5 | "path": "/add_angular/index.html",
6 | "is_active": true
7 | }
--------------------------------------------------------------------------------
/add_angular/app/login/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Dreamfactory - Addressbook 2.0
4 |
24 |
25 |
--------------------------------------------------------------------------------
/add_angular/app/app.css:
--------------------------------------------------------------------------------
1 | @import "login/login.css";
2 |
3 |
4 |
5 | .btn-material-add {
6 | position: fixed;
7 | right: 35px;
8 | bottom: 35px;
9 | }
10 |
11 | .full-height {
12 | height: 100%;
13 | }
14 |
15 | md-tab-content {
16 | display: initial;
17 | }
18 |
19 | .btn-aligned-left {
20 | text-align: left;
21 | min-width: 40px;
22 | }
23 |
24 | md-sidenav,
25 | md-sidenav.md-locked-open,
26 | md-sidenav.md-closed.md-locked-open-add-active {
27 | min-width: 200px !important;
28 | width: 75vw !important;
29 | max-width: 400px !important;
30 | }
31 |
32 | md-dialog form {
33 | padding: 10px;
34 | }
35 |
36 | .contact-divider {
37 | height: 30px;
38 | min-height: 30px;
39 | }
40 |
41 | .sticky-bottom {
42 | position: absolute;
43 | bottom: 0px;
44 | }
--------------------------------------------------------------------------------
/add_angular/app/login/register.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Register
4 |
31 |
32 |
--------------------------------------------------------------------------------
/add_angular/app/groups/groups.html:
--------------------------------------------------------------------------------
1 | Groups
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | | ID |
12 | Name |
13 |
14 |
15 |
16 |
17 |
18 | | {{ item.id }} |
19 | {{ item.name }} |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
{{ item.name }}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/add_angular/app/contact-info/contact-info.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | | Type |
6 | Phone |
7 | Email |
8 | Address |
9 | City |
10 | State |
11 | Country |
12 |
13 |
14 |
15 |
16 |
17 | | {{ item.info_type }} |
18 | {{ item.phone }} |
19 | {{ item.email }} |
20 | {{ item.address }} |
21 | {{ item.city }} |
22 | {{ item.state }} |
23 | {{ item.country }} |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
{{ item.info_type + ' | ' + item.phone }}
33 |
34 |
{{ item.address || '' }}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/add_angular/app/contacts/contacts.html:
--------------------------------------------------------------------------------
1 | Contacts
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | | {{ col }} |
13 |
14 |
15 |
16 |
17 |
18 | |
19 | {{ item[col] }}
20 | |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
{{ item.first_name + ' ' + item.last_name }}
39 |
40 |
{{ item.notes || '' }}
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
54 | Previous
55 |
56 |
60 | Next
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/add_angular/app/contacts/contact-add-edit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/add_angular/app/login/login.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | angular.module('login', [
5 | 'ngRoute',
6 | 'ngResource',
7 | 'ngCookies'
8 | ])
9 |
10 |
11 | .run([
12 | '$rootScope',
13 |
14 | function ($rootScope) {
15 | try {
16 | $rootScope.user = JSON.parse(window.localStorage.user)
17 | } catch (e) {}
18 | }
19 | ])
20 |
21 | .config([
22 |
23 | '$routeProvider',
24 |
25 | function ($routeProvider) {
26 | $routeProvider
27 | .when('/login', {
28 | title : 'Login',
29 | controller : 'LoginCtrl',
30 | templateUrl : 'app/login/login.html'
31 | })
32 |
33 | .when('/register', {
34 | title : 'Register',
35 | controller : 'RegisterController',
36 | templateUrl : 'app/login/register.html'
37 | });
38 | }
39 | ])
40 |
41 | .service('LoginHelper', [
42 | '$http', '$q', '$cookies', '$rootScope',
43 |
44 | function ($http, $q, $cookies, $rootScope) {
45 | this.initiate = function (options) {
46 | var deferred = $q.defer();
47 |
48 | $http.post('/api/v2/user/session/', options).then(function (result) {
49 | $http.defaults.headers.common['X-DreamFactory-Session-Token'] = result.data.session_token;
50 | $cookies.session_token = result.data.session_token;
51 |
52 | $rootScope.user = result.data;
53 |
54 | try {
55 | window.localStorage.user = JSON.stringify(result.data);
56 | } catch (e) { }
57 |
58 | deferred.resolve();
59 | }, deferred.reject);
60 |
61 | return deferred.promise;
62 | };
63 |
64 | this.register = function (options) {
65 | var deferred = $q.defer();
66 |
67 | $http.post('/api/v2/user/register?login=true', options).then(function (result) {
68 | console.log(result);
69 | deferred.resolve();
70 | }, deferred.reject);
71 |
72 | return deferred.promise;
73 | };
74 | }
75 | ])
76 |
77 | .controller('LoginCtrl', [
78 | '$scope', 'LoginHelper', '$location', '$rootScope',
79 |
80 | function ($scope, LoginHelper, $location, $rootScope) {
81 | $rootScope.isLoggedIn = false;
82 | $scope.submit = function () {
83 | LoginHelper.initiate({
84 | email: $scope.username,
85 | password: $scope.password
86 | }).then(function () {
87 | $rootScope.isLoggedIn = true;
88 | $location.path('/contacts');
89 | });
90 | };
91 |
92 | $scope.register = function () {
93 | $location.path('/register');
94 | };
95 | }
96 | ])
97 |
98 |
99 | .controller('RegisterController', [
100 | '$scope', 'LoginHelper', '$location', '$rootScope',
101 |
102 | function ($scope, LoginHelper, $location, $rootScope) {
103 | $rootScope.isLoggedIn = false;
104 | $scope.register = function () {
105 | LoginHelper.register({
106 | email: $scope.username,
107 | password: $scope.password,
108 | first_name: $scope.firstName || 'Address',
109 | last_name: $scope.lastName || 'Book'
110 | }).then(function () {
111 | $location.path('/login');
112 | });
113 | };
114 | }
115 | ])
--------------------------------------------------------------------------------
/add_angular/app/contact-info/contact-info-add-edit.html:
--------------------------------------------------------------------------------
1 |
2 |
79 |
--------------------------------------------------------------------------------
/add_angular/app/groups/group-add-edit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Add new group
5 | Edit group
6 |
7 |
8 |
71 |
--------------------------------------------------------------------------------
/add_angular/app/contact-info/contact-info.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 |
5 | angular.module('contact-info', [
6 | 'ngResource',
7 | 'ngRoute',
8 | 'ngMaterial',
9 | 'ngAnimate',
10 | 'contacts',
11 | 'groups'
12 | ])
13 |
14 | .config([
15 | '$routeProvider',
16 |
17 | function ($routeProvider) {
18 |
19 | // Routes
20 | $routeProvider
21 | .when('/contact-info/:id', {
22 | title : 'Contact Info',
23 | templateUrl : 'app/contact-info/contact-info.html',
24 | controller : 'ContactInfoCtrl'
25 | });
26 |
27 | }
28 | ])
29 |
30 | .factory('ContactInfo', [
31 | '$resource',
32 |
33 | function ($resource) {
34 | return $resource('/api/v2/db/_table/contact_info/:id', { id: '@id' }, {
35 | query: {
36 | method: 'GET',
37 | isArray: false
38 | },
39 | create: {
40 | method: 'POST'
41 | },
42 | update: {
43 | method: 'PUT'
44 | },
45 | remove: {
46 | method: 'DELETE'
47 | }
48 | });
49 | }
50 | ])
51 |
52 |
53 | .controller('ContactInfoCtrl', [
54 | '$scope', '$location', '$route', '$mdDialog', '$mdToast', 'ContactInfo',
55 |
56 | function ($scope, $location, $route, $mdDialog, $mdToast, ContactInfo) {
57 | $scope.contactName = $route.current.params.name;
58 | $scope.paginate = { page: 0, limit: 15 }
59 |
60 | $scope.loadData = function (page) {
61 |
62 | ContactInfo.query({
63 | include_count: true,
64 | filter: 'contact_id=' + $route.current.params.id
65 | }).$promise.then(function (result) {
66 | $scope.contactInfo = result.resource;
67 | });
68 | };
69 |
70 |
71 | $scope.addEditContactInfo = function (ev, item) {
72 | console.log('Contact info')
73 | $mdDialog.show({
74 | controller: 'ContactInfoUpdateCtrl',
75 | templateUrl: 'app/contact-info/contact-info-add-edit.html',
76 | parent: angular.element(document.body),
77 | targetEvent: ev,
78 | locals: {
79 | contactInfo: item || { contact_id: $route.current.params.id }
80 | }
81 | }).then(function () {
82 | $scope.loadData();
83 | });
84 | };
85 |
86 | $scope.loadData(0);
87 | }
88 | ])
89 |
90 |
91 | .controller('ContactInfoUpdateCtrl', [
92 | '$scope', '$mdDialog', 'ContactInfo', 'contactInfo', '$mdToast',
93 |
94 | function ($mdScope, $mdDialog, ContactInfo, contactInfo, $mdToast) {
95 | $mdScope.contactInfo = angular.copy(contactInfo);
96 | $mdScope.info_types = [ 'home', 'work', 'mobile' ];
97 | if ($mdScope.contactInfo.id === '') {
98 | delete $mdScope.contactInfo;
99 | }
100 |
101 | $mdScope.submit = function () {
102 | if (!$mdScope.contactInfo.id) {
103 | ContactInfo.create($mdScope.contactInfo).$promise.then(function () {
104 | $mdToast.show($mdToast.simple().content('Contact saved!'));
105 | $mdDialog.hide($mdScope.contactInfo);
106 | });
107 | } else {
108 | ContactInfo.update({ id:
109 | contactInfo.id
110 | }, $mdScope.contactInfo).$promise.then(function () {
111 | $mdToast.show($mdToast.simple().content('Contact saved!'));
112 | $mdDialog.hide($mdScope.contactInfo);
113 | });
114 | }
115 |
116 | };
117 |
118 | $mdScope.cancel = function () {
119 | $mdDialog.cancel();
120 | };
121 | }
122 | ]);
--------------------------------------------------------------------------------
/add_angular/app/login/login.css:
--------------------------------------------------------------------------------
1 | .signin-card {
2 | -webkit-animation: cardEnter 0.75s ease-in-out 0.5s;
3 | animation: cardEnter 0.75s ease-in-out 0.5s;
4 | -webkit-animation-fill-mode: both;
5 | animation-fill-mode: both;
6 | opacity: 0;
7 | }
8 |
9 | /* -- login paper styles ------------------------------ */
10 | .signin-card {
11 | max-width: 350px;
12 | border-radius: 2px;
13 | margin: 20px auto;
14 | padding: 20px;
15 | background-color: #eceff1; // Blue Grey 50
16 | box-shadow: 0 10px 20px rgba(0, 0, 0, .19),
17 | 0 6px 6px rgba(0, 0, 0, .23); // shadow depth 3
18 | }
19 |
20 | .signin-card {
21 | .logo-image, h1, p {
22 | text-align: center;
23 | }
24 | }
25 |
26 | /* -- font styles ------------------------------------- */
27 | .display1 {
28 | font-size: 28px;
29 | font-weight: 100;
30 | line-height: 1.2;
31 | color: #757575;
32 | text-transform: inherit;
33 | letter-spacing: inherit;
34 | }
35 |
36 | .subhead {
37 | font-size: 16px;
38 | font-weight: 300;
39 | line-height: 1.1;
40 | color: #212121;
41 | text-transform: inherit;
42 | letter-spacing: inherit;
43 | }
44 |
45 | /* card animation from Animate.css -------------------- */
46 | @-webkit-keyframes cardEnter {
47 | 0%, 20%, 40%, 60%, 80%, 100% {
48 | -webkit-transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
49 | transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
50 | }
51 | 0% {
52 | opacity: 0;
53 | -webkit-transform: scale3d(0.3, 0.3, 0.3);
54 | -ms-transform: scale3d(0.3, 0.3, 0.3);
55 | transform: scale3d(0.3, 0.3, 0.3);
56 | }
57 | 20% {
58 | -webkit-transform: scale3d(1.1, 1.1, 1.1);
59 | -ms-transform: scale3d(1.1, 1.1, 1.1);
60 | transform: scale3d(1.1, 1.1, 1.1);
61 | }
62 | 40% {
63 | -webkit-transform: scale3d(0.9, 0.9, 0.9);
64 | -ms-transform: scale3d(0.9, 0.9, 0.9);
65 | transform: scale3d(0.9, 0.9, 0.9);
66 | }
67 | 60% {
68 | opacity: 1;
69 | -webkit-transform: scale3d(1.03, 1.03, 1.03);
70 | -ms-transform: scale3d(1.03, 1.03, 1.03);
71 | transform: scale3d(1.03, 1.03, 1.03);
72 | }
73 | 80% {
74 | -webkit-transform: scale3d(0.97, 0.97, 0.97);
75 | -ms-transform: scale3d(0.97, 0.97, 0.97);
76 | transform: scale3d(0.97, 0.97, 0.97);
77 | }
78 | 100% {
79 | opacity: 1;
80 | -webkit-transform: scale3d(1, 1, 1);
81 | -ms-transform: scale3d(1, 1, 1);
82 | transform: scale3d(1, 1, 1);
83 | }
84 | }
85 | @keyframes cardEnter {
86 | 0%, 20%, 40%, 60%, 80%, 100% {
87 | -webkit-transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
88 | transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
89 | }
90 | 0% {
91 | opacity: 0;
92 | -webkit-transform: scale3d(0.3, 0.3, 0.3);
93 | -ms-transform: scale3d(0.3, 0.3, 0.3);
94 | transform: scale3d(0.3, 0.3, 0.3);
95 | }
96 | 20% {
97 | -webkit-transform: scale3d(1.1, 1.1, 1.1);
98 | -ms-transform: scale3d(1.1, 1.1, 1.1);
99 | transform: scale3d(1.1, 1.1, 1.1);
100 | }
101 | 40% {
102 | -webkit-transform: scale3d(0.9, 0.9, 0.9);
103 | -ms-transform: scale3d(0.9, 0.9, 0.9);
104 | transform: scale3d(0.9, 0.9, 0.9);
105 | }
106 | 60% {
107 | opacity: 1;
108 | -webkit-transform: scale3d(1.03, 1.03, 1.03);
109 | -ms-transform: scale3d(1.03, 1.03, 1.03);
110 | transform: scale3d(1.03, 1.03, 1.03);
111 | }
112 | 80% {
113 | -webkit-transform: scale3d(0.97, 0.97, 0.97);
114 | -ms-transform: scale3d(0.97, 0.97, 0.97);
115 | transform: scale3d(0.97, 0.97, 0.97);
116 | }
117 | 100% {
118 | opacity: 1;
119 | -webkit-transform: scale3d(1, 1, 1);
120 | -ms-transform: scale3d(1, 1, 1);
121 | transform: scale3d(1, 1, 1);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/add_angular/app/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Page Not Found :(
6 |
136 |
137 |
138 |
139 |
Not found :(
140 |
Sorry, but the page you were trying to view does not exist.
141 |
It looks like this was the result of either:
142 |
143 | - a mistyped address
144 | - an out-of-date link
145 |
146 |
149 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/add_angular/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Address book
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | {{ $root.user.first_name + ' ' + $root.user.last_name }}
49 |
50 | {{ $root.user.email }}
51 |
52 |
53 |
54 |
55 |
56 |
57 | Contacts
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Groups
67 |
68 |
69 |
70 |
71 | Angular Addressbook v1.0.7
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/add_angular/app/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | angular.module('addressbook', [
5 | 'ngRoute',
6 | 'ngMaterial',
7 | 'ngCookies',
8 |
9 | 'login',
10 | 'contacts',
11 | 'groups'
12 |
13 | ])
14 |
15 | // set to empty to launch the app from the admin console apps tab
16 | .constant('INSTANCE_URL', '')
17 |
18 | // set to your app's API key which can be found on the admin console apps tab
19 | .constant('APP_API_KEY', '0c7b9f992741c6119c1614557615490080e8187199db589d33d0d35127bcb74a')
20 |
21 | .run([
22 | '$cookies', 'APP_API_KEY', '$http', '$rootScope', '$window',
23 |
24 | function ($cookies, APP_API_KEY, $http, $rootScope, $window) {
25 | $http.defaults.headers.common['X-Dreamfactory-API-Key'] = APP_API_KEY;
26 | $http.defaults.headers.common['X-DreamFactory-Session-Token'] = $cookies.session_token;
27 | if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
28 | $rootScope.isMobile = true;
29 | }
30 |
31 | angular.element($window).bind('scroll', function() {
32 | var windowHeight = 'innerHeight' in window ? window.innerHeight : document.documentElement.offsetHeight;
33 | var body = document.body, html = document.documentElement;
34 | var docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
35 | var windowBottom = windowHeight + window.pageYOffset;
36 | if (windowBottom >= docHeight) {
37 | $rootScope.$broadcast('SCROLL_END');
38 | }
39 | });
40 | }
41 | ])
42 |
43 | // Config - configure applicaiton routes and settings
44 | .config([
45 | '$routeProvider', '$httpProvider', 'APP_API_KEY', '$mdThemingProvider',
46 |
47 | function ($routeProvider, $httpProvider, APP_API_KEY, $mdThemingProvider) {
48 | $routeProvider
49 | .otherwise({
50 | redirectTo:'/contacts'
51 | });
52 |
53 | $httpProvider.interceptors.push('httpInterceptor');
54 |
55 | // Configure the theme of the whole app
56 | $mdThemingProvider.theme('default')
57 | .primaryPalette('blue-grey')
58 | .accentPalette('blue');
59 | }
60 | ])
61 |
62 |
63 | // Authentication interceptor. Executes a function everytime before sending any request.
64 | .factory('httpInterceptor', [
65 | '$location', '$q', '$injector', 'INSTANCE_URL',
66 |
67 | function ($location, $q, $injector, INSTANCE_URL) {
68 |
69 | return {
70 |
71 | request: function (config) {
72 |
73 | var ignoreWrapping = false;
74 |
75 | if (config.url.indexOf ('/user/register') > -1 || config.url.indexOf ('/user/session') > -1) {
76 | ignoreWrapping = true;
77 | }
78 |
79 | // Append instance url before every api call
80 | if (config.url.indexOf('/api/v2') > -1) {
81 | config.url = INSTANCE_URL + config.url;
82 |
83 | if ((config.method == 'POST' || config.method == 'PUT' || config.method == 'PATCH') && !ignoreWrapping) {
84 | var data;
85 | if (Array.isArray(config.data)) {
86 | config.data = { resource: config.data };
87 | } else {
88 | config.data = { resource: [config.data] };
89 | }
90 | }
91 | };
92 |
93 | // delete x-dreamfactory-session-token header if login
94 | if (config.method.toLowerCase() === 'post' && config.url.indexOf('/api/v2/user/session') > -1) {
95 | delete config.headers['X-DreamFactory-Session-Token'];
96 | }
97 |
98 | return config;
99 | },
100 |
101 | responseError: function (result) {
102 |
103 | // If status is 401 or 403 with token blacklist error then redirect to login
104 | if (result.status === 401 || (result.status === 403 && result.data.error.message.indexOf('token') > -1)) {
105 | $location.path('/login');
106 | }
107 |
108 | var $mdToast = $injector.get('$mdToast');
109 | $mdToast.show($mdToast.simple().content('Error: ' + result.data.error.message));
110 |
111 | return $q.reject(result);
112 | }
113 | }
114 | }
115 | ])
116 |
117 | // Header controller
118 | .controller('HeaderCtrl', [
119 | '$scope', '$mdSidenav', '$mdUtil', '$http', '$location', '$rootScope',
120 |
121 | function ($scope, $mdSidenav, $mdUtil, $http, $location, $rootScope) {
122 | $rootScope.isLoggedIn = true;
123 |
124 | $scope.toggleSidebar = $mdUtil.debounce(function () {
125 | $mdSidenav('left-sidebar').toggle();
126 | }, 200);
127 |
128 | $scope.logout = function () {
129 | $http({
130 | method: 'DELETE',
131 | url: '/api/v2/user/session'
132 | }).success(function () {
133 | $location.path('/login');
134 | delete $http.defaults.headers.common['X-DreamFactory-Session-Token'];
135 | });
136 | };
137 | }
138 |
139 | ])
140 |
141 |
142 | // Sidebar controller
143 | .controller('SidebarCtrl', [
144 | '$scope', '$mdSidenav', '$mdUtil',
145 |
146 | function ($scope, $mdSidenav, $mdUtil) {
147 | $scope.toggleSidebar = $mdUtil.debounce(function () {
148 | $mdSidenav('left-sidebar').toggle();
149 | }, 200);
150 | }
151 | ]);
152 |
--------------------------------------------------------------------------------
/add_angular/app/groups/groups.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('groups', [
4 | 'ngRoute',
5 | 'ngMaterial',
6 | 'ngResource'
7 | ])
8 |
9 |
10 |
11 | .config([
12 | '$routeProvider',
13 |
14 | function ($routeProvider) {
15 | $routeProvider
16 | .when('/groups', {
17 | title : 'Groups',
18 | templateUrl : 'app/groups/groups.html',
19 | controller : 'GroupsCtrl'
20 | })
21 | .when('/edit-group/:id', {
22 | title : 'Edit group',
23 | templateUrl : 'app/groups/group-add-edit.html',
24 | controller : 'GroupAddEditCtrl',
25 | resolve : {
26 | group: [ '$route', 'Groups',
27 |
28 | function ($route, Groups) {
29 | return Groups.query({
30 | id: $route.current.params.id
31 | }).$promise;
32 | }
33 | ]
34 | }
35 | });
36 | }
37 | ])
38 |
39 |
40 | .factory('Groups', [
41 | '$resource',
42 |
43 | function ($resource) {
44 | return $resource('/api/v2/db/_table/contact_group/:id', { id: '@id' }, {
45 | query: {
46 | method: 'GET',
47 | isArray: false
48 | },
49 | create: {
50 | method: 'POST'
51 | },
52 | update: {
53 | method: 'PUT'
54 | },
55 | remove: {
56 | method: 'DELETE'
57 | }
58 | });
59 | }
60 | ])
61 |
62 |
63 |
64 | .controller('GroupsCtrl', [
65 | '$scope', 'Groups', '$location', '$mdDialog', '$route',
66 |
67 | function ($scope, Groups, $location, $mdDialog, $route) {
68 |
69 |
70 | $scope.loadData = function (filter) {
71 |
72 | Groups.query({
73 | include_count: true,
74 | filter: filter
75 | }).$promise.then(function (result) {
76 | $scope.groups = result.resource
77 | });
78 | };
79 |
80 | $scope.search = function (event) {
81 | if (event.keyCode === 13) {
82 | $scope.loadData('name like %' + event.target.value + '%');
83 | }
84 | };
85 |
86 | $scope.addEdit = function (ev, item) {
87 | if (item) {
88 | $location.path('/edit-group/' + item.id);
89 | } else {
90 | $mdDialog.show({
91 | controller: 'GroupAddEditCtrl',
92 | template: [
93 | '',
94 | '',
95 | ''
96 | ].join(''),
97 | parent: angular.element(document.body),
98 | targetEvent: ev,
99 | locals: {
100 | group: { }
101 | }
102 | }).then(function () {
103 | $route.reload();
104 | });
105 | }
106 | };
107 |
108 | $scope.loadData();
109 | }
110 | ])
111 |
112 |
113 | .controller('GroupAddEditCtrl', [
114 | '$scope', '$mdDialog', 'Groups', 'group', 'ContactRelationships', 'Contacts', '$filter', '$mdToast', '$location',
115 |
116 | function ($scope, $mdDialog, Groups, group, ContactRelationships, Contacts, $filter, $mdToast, $location) {
117 | $scope.group = angular.copy(group);
118 |
119 | $scope.allContacts = Contacts.query();
120 |
121 | $scope.loadContacts = function () {
122 | ContactRelationships.query({
123 | filter: 'contact_group_id=' + group.id
124 | }).$promise.then(function (result) {
125 | if (result.resource) {
126 | $scope.contacts = result.resource.map(function (item) {
127 | return {
128 | relationshipId: item.id,
129 | data: Contacts.get({ id: item.contact_id })
130 | };
131 | });
132 | }
133 | });
134 | };
135 |
136 | if (group.id) {
137 | $scope.loadContacts();
138 | }
139 |
140 | $scope.showContact = function (contact) {
141 | $location.path('/edit-contact/' + contact.id);
142 | };
143 |
144 | $scope.removeGroup = function (group) {
145 | Groups.remove({
146 | filter: 'id=' + group.id
147 | }).$promise.then(function () {
148 | $scope.cancel();
149 | $mdToast.show($mdToast.simple().content('Group removed!'));
150 | });
151 | };
152 |
153 | $scope.removeContact = function (contact) {
154 | ContactRelationships.remove({
155 | filter: 'id=' + contact.relationshipId
156 | }).$promise.then(function () {
157 | $scope.loadContacts();
158 | $mdToast.show($mdToast.simple().content('Contact removed from group!'));
159 | });
160 | };
161 |
162 | $scope.addContact = function (contact) {
163 | var existing = $scope.contacts.filter(function (item) { return item.data.id === contact.id }) [0];
164 | if (existing) return;
165 |
166 | ContactRelationships.create({
167 | contact_id: contact.id,
168 | contact_group_id: group.id
169 | }).$promise.then(function () {
170 | $scope.loadContacts();
171 | $mdToast.show($mdToast.simple().content('Contact added to group!'));
172 | });
173 |
174 | $scope.selectedContact = null;
175 | $scope.searchText = '';
176 | };
177 |
178 | $scope.searchContacts = function (searchText) {
179 | var items = $filter('filter')($scope.allContacts.resource, searchText);
180 | return items.filter(function (item) {
181 | return !$scope.contacts.some(function (contact) {
182 | return contact.data.id === item.id;
183 | });
184 | });
185 | };
186 |
187 | $scope.submit = function () {
188 | if (!$scope.group.id) {
189 | Groups.create($scope.group).$promise.then(function () {
190 | $scope.cancel();
191 | $mdToast.show($mdToast.simple().content('Group created!'));
192 | });
193 | } else {
194 | Groups.update({ id:
195 | $scope.group.id
196 | }, $scope.group).$promise.then(function () {
197 | $scope.cancel();
198 | $mdToast.show($mdToast.simple().content('Group saved!'));
199 | });
200 | }
201 |
202 | };
203 |
204 | $scope.cancel = function () {
205 | if ($scope.group.id)
206 | $location.path('/groups');
207 | else
208 | $mdDialog.cancel();
209 | };
210 | }
211 | ]);
--------------------------------------------------------------------------------
/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "service": [
3 | {
4 | "name": "db",
5 | "table": [
6 | {
7 | "description": "The main table for tracking contacts.",
8 | "name": "contact",
9 | "field": [
10 | {
11 | "name": "id",
12 | "label": "Contact Id",
13 | "type": "id"
14 | },
15 | {
16 | "name": "first_name",
17 | "type": "string",
18 | "size": 40,
19 | "allow_null": false,
20 | "is_index": true,
21 | "validation": {
22 | "not_empty": {
23 | "on_fail": "First name value must not be empty."
24 | }
25 | }
26 | },
27 | {
28 | "name": "last_name",
29 | "type": "string",
30 | "size": 40,
31 | "allow_null": false,
32 | "is_index": true,
33 | "validation": {
34 | "not_empty": {
35 | "on_fail": "Last name value must not be empty."
36 | }
37 | }
38 | },
39 | {
40 | "name": "image_url",
41 | "label": "image_url",
42 | "type": "text",
43 | "validation": {
44 | "url": {
45 | "on_fail": "Not a valid URL value."
46 | }
47 | },
48 | "allow_null": true
49 | },
50 | {
51 | "name": "twitter",
52 | "label": "Twitter Handle",
53 | "type": "string",
54 | "size": 18,
55 | "allow_null": true
56 | },
57 | {
58 | "name": "skype",
59 | "label": "Skype Account",
60 | "type": "string",
61 | "size": 255,
62 | "allow_null": true
63 | },
64 | {
65 | "name": "notes",
66 | "label": "notes",
67 | "type": "text",
68 | "allow_null": true
69 | }
70 | ]
71 | },
72 | {
73 | "description": "The contact details sub-table, owned by contact table row.",
74 | "name": "contact_info",
75 | "field": [
76 | {
77 | "name": "id",
78 | "label": "Info Id",
79 | "type": "id"
80 | },
81 | {
82 | "name": "ordinal",
83 | "type": "integer",
84 | "allow_null": true
85 | },
86 | {
87 | "name": "contact_id",
88 | "type": "reference",
89 | "allow_null": false,
90 | "ref_table": "contact",
91 | "ref_fields": "id",
92 | "ref_on_delete": "CASCADE"
93 | },
94 | {
95 | "name": "info_type",
96 | "type": "string",
97 | "size": 32,
98 | "allow_null": false,
99 | "validation": {
100 | "not_empty": {
101 | "on_fail": "Information type can not be empty."
102 | },
103 | "picklist": {
104 | "on_fail": "Not a valid information type."
105 | }
106 | },
107 | "picklist": [
108 | "work",
109 | "home",
110 | "mobile",
111 | "other"
112 | ]
113 | },
114 | {
115 | "name": "phone",
116 | "label": "Phone Number",
117 | "type": "string",
118 | "size": 32
119 | },
120 | {
121 | "name": "email",
122 | "label": "Email Address",
123 | "type": "string",
124 | "size": 255,
125 | "validation": {
126 | "email": {
127 | "on_fail": "Not a valid email address."
128 | }
129 | }
130 | },
131 | {
132 | "name": "address",
133 | "label": "Street Address",
134 | "type": "string"
135 | },
136 | {
137 | "name": "city",
138 | "label": "city",
139 | "type": "string",
140 | "size": 64
141 | },
142 | {
143 | "name": "state",
144 | "label": "state",
145 | "type": "string",
146 | "size": 64
147 | },
148 | {
149 | "name": "zip",
150 | "label": "zip",
151 | "type": "string",
152 | "size": 16
153 | },
154 | {
155 | "name": "country",
156 | "label": "country",
157 | "type": "string",
158 | "size": 64
159 | }
160 | ]
161 | },
162 | {
163 | "description": "The main table for tracking groups of contact.",
164 | "name": "contact_group",
165 | "field": [
166 | {
167 | "name": "id",
168 | "type": "id"
169 | },
170 | {
171 | "name": "name",
172 | "type": "string",
173 | "size": 128,
174 | "allow_null": false,
175 | "validation": {
176 | "not_empty": {
177 | "on_fail": "Group name value must not be empty."
178 | }
179 | }
180 | }
181 | ]
182 | },
183 | {
184 | "description": "The join table for tracking contacts in groups.",
185 | "name": "contact_group_relationship",
186 | "field": [
187 | {
188 | "name": "id",
189 | "type": "id"
190 | },
191 | {
192 | "name": "contact_id",
193 | "type": "reference",
194 | "allow_null": false,
195 | "ref_table": "contact",
196 | "ref_fields": "id",
197 | "ref_on_delete": "CASCADE"
198 | },
199 | {
200 | "name": "contact_group_id",
201 | "type": "reference",
202 | "allow_null": false,
203 | "ref_table": "contact_group",
204 | "ref_fields": "id",
205 | "ref_on_delete": "CASCADE"
206 | }
207 | ]
208 | }
209 | ]
210 | }
211 | ]
212 | }
213 |
--------------------------------------------------------------------------------
/add_angular/app/contacts/contacts.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | angular.module('contacts', [
5 | 'ngResource',
6 | 'ngRoute',
7 | 'ngMaterial',
8 | 'ngAnimate',
9 | 'groups',
10 | 'contact-info'
11 | ])
12 |
13 |
14 | .config([
15 | '$routeProvider',
16 |
17 | function ($routeProvider) {
18 |
19 | // Routes
20 | $routeProvider
21 | .when('/contacts', {
22 | title : 'Contacts',
23 | templateUrl : 'app/contacts/contacts.html',
24 | controller : 'ContactsCtrl'
25 | })
26 | .when('/add-contact', {
27 | title : 'Add contact',
28 | templateUrl : 'app/contacts/contact-add-edit.html',
29 | controller : 'ContactsItemCtrl',
30 | resolve : {
31 | contact: function () {
32 | return { };
33 | },
34 | groups: function (Groups) {
35 | return Groups.query().$promise;
36 | }
37 | }
38 | })
39 | .when('/edit-contact/:id', {
40 | title : 'Edit contact',
41 | templateUrl : 'app/contacts/contact-add-edit.html',
42 | controller : 'ContactsItemCtrl',
43 | resolve : {
44 | contact: function ($route) {
45 | return { id: $route.current.params.id };
46 | },
47 | groups: function (Groups) {
48 | return Groups.query().$promise;
49 | }
50 | }
51 | });
52 |
53 | }
54 | ])
55 |
56 | // Contacts factory to fetch contacts data from dreamfactory services.
57 |
58 | .factory('Contacts', [
59 | '$resource',
60 |
61 | function ($resource) {
62 | return $resource('/api/v2/db/_table/contact/:id', { id: '@id' }, {
63 | query: {
64 | method: 'GET',
65 | isArray: false
66 | },
67 | create: {
68 | method: 'POST'
69 | },
70 | update: {
71 | method: 'PUT'
72 | },
73 | remove: {
74 | method: 'DELETE'
75 | }
76 | });
77 | }
78 | ])
79 |
80 |
81 | // Contact group relationship factory to fetch all the records with relationship
82 | // between contact and groups.
83 |
84 | .factory('ContactRelationships', [
85 | '$resource',
86 |
87 | function ($resource) {
88 | return $resource('/api/v2/db/_table/contact_group_relationship', { }, {
89 | query: {
90 | method: 'GET',
91 | isArray: false
92 | },
93 | create: {
94 | method: 'POST'
95 | },
96 | update: {
97 | method: 'PUT'
98 | },
99 | remove: {
100 | method: 'DELETE'
101 | }
102 | });
103 | }
104 | ])
105 |
106 | .controller('ContactsCtrl', [
107 | '$scope', 'Contacts', '$location', '$route', '$mdToast', 'ContactInfo', '$q', '$filter',
108 |
109 | function ($scope, Contacts, $location, $route, $mdToast, ContactInfo, $q, $filter) {
110 |
111 | $scope.colLabels = [ 'ID', 'First Name', 'Last Name', 'Image URL', 'Twitter', 'Skype', 'Notes' ];
112 | $scope.colFields = [ 'id', 'first_name', 'last_name', 'image_url', 'twitter', 'skype', 'notes' ];
113 | $scope.altImage = 'http://uxrepo.com/static/icon-sets/ionicons/png32/128/000000/ios7-contact-128-000000.png';
114 | $scope.mobileActive = null;
115 | $scope.paginate = { page: 0, limit: 15 }
116 | $scope.contacts = [];
117 |
118 | $scope.loadData = function (page, options) {
119 | $scope.paginate.page = page;
120 | options = angular.extend({
121 | include_count: true,
122 | offset: $scope.paginate.page * $scope.paginate.limit,
123 | limit: $scope.paginate.limit,
124 | order: 'last_name ASC'
125 | }, options || {});
126 |
127 | Contacts.query(options).$promise.then(function (result) {
128 | if (!$scope.$root.isMobile || page === 0) {
129 | $scope.contacts = result.resource;
130 | } else {
131 | $scope.contacts.push.apply($scope.contacts, result.resource);
132 | }
133 |
134 | $scope.paginate.meta = result.meta;
135 | });
136 | };
137 |
138 | $scope.search = function (event) {
139 | if (event.keyCode === 13) {
140 | $scope.loadData(0, {
141 | filter: 'first_name like %' + event.target.value + '%'
142 | });
143 | }
144 | };
145 |
146 | $scope.addContact = function () {
147 | $location.path('/add-contact')
148 | };
149 |
150 | $scope.editContact = function (contact) {
151 | $location.path('/edit-contact/' + contact.id);
152 | };
153 |
154 | if ($scope.$root.isMobile) {
155 | $scope.$on('SCROLL_END', function () {
156 | $scope.loadData($scope.paginate.page+1);
157 | });
158 | }
159 |
160 | $scope.loadData(0);
161 | }
162 | ])
163 |
164 | .controller('ContactsItemCtrl', [
165 | '$scope', 'Contacts', 'ContactInfo', 'contact', 'groups', '$mdToast', '$mdDialog', '$location', '$route', 'Groups', 'ContactRelationships', '$q',
166 |
167 | function ($scope, Contacts, ContactInfo, contact, groups, $mdToast, $mdDialog, $location, $route, Groups, ContactRelationships, $q) {
168 | $scope.contact = contact;
169 | $scope.groups = groups.resource;
170 | $scope.selectedGroups = {};
171 |
172 | $scope.loadData = function () {
173 | ContactInfo.query({
174 | include_count: true,
175 | filter: 'contact_id=' + $route.current.params.id
176 | }).$promise.then(function (result) {
177 | $scope.contactInfo = result.resource;
178 | });
179 | };
180 |
181 | if (contact.id) {
182 | Contacts.get({ id: contact.id }).$promise.then(function (response) {
183 | $scope.contact = response;
184 | ContactRelationships.query({
185 | filter: 'contact_id=' + contact.id
186 | }).$promise.then(function (result) {
187 | result.resource.forEach(function (item) {
188 | $scope.selectedGroups[item.contact_group_id] = true;
189 | });
190 | });
191 | });
192 |
193 | // load contact info
194 | $scope.loadData();
195 | }
196 |
197 | $scope.addEditContactInfo = function (ev, item) {
198 | $mdDialog.show({
199 | controller: 'ContactInfoUpdateCtrl',
200 | templateUrl: 'app/contact-info/contact-info-add-edit.html',
201 | parent: angular.element(document.body),
202 | targetEvent: ev,
203 | locals: {
204 | contactInfo: item || { contact_id: $route.current.params.id }
205 | }
206 | }).then(function () {
207 | $scope.loadData();
208 | });
209 | };
210 |
211 | $scope.remove = function () {
212 | var promises = [
213 | ContactRelationships.remove({
214 | filter: 'contact_id=' + contact.id
215 | }).$promise,
216 | ContactInfo.remove({
217 | filter: 'contact_id=' + contact.id
218 | }).$promise
219 | ];
220 |
221 | $q.all(promises).then(function () {
222 | Contacts.remove({
223 | id: contact.id
224 | }).$promise.then(function () {
225 | $location.path('/contacts');
226 | });
227 | });
228 | };
229 |
230 | $scope.cancel = function () {
231 | $location.path('/contacts');
232 | };
233 |
234 |
235 | $scope.save = function () {
236 | if (!contact.id) {
237 | // Create contact
238 |
239 | Contacts.create($scope.contact).$promise.then(function () {
240 | $mdToast.show($mdToast.simple().content('Contact saved!'));
241 | $location.path('/contacts');
242 | });
243 | } else {
244 |
245 | ContactRelationships.remove({
246 | filter: 'contact_id=' + contact.id
247 | }).$promise.then(function () {
248 |
249 | var contactGroupRelationships = Object.keys($scope.selectedGroups).filter(function (key) {
250 | return $scope.selectedGroups[key];
251 | }).map(function (key) {
252 | return { contact_id: contact.id, contact_group_id: key }
253 | });
254 |
255 | var promises = [
256 | Contacts.update({ id: $scope.contact.id }, $scope.contact).$promise,
257 | ContactRelationships.create(contactGroupRelationships).$promise
258 | ];
259 |
260 | $q.all(promises).then(function () {
261 | $mdToast.show($mdToast.simple().content('Contact saved!'));
262 | $location.path('/contacts');
263 | });
264 | });
265 | }
266 | };
267 | }
268 | ]);
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Address Book for AngularJS
2 | ==========================
3 |
4 | This repo contains a sample address book application for AngularJS that demonstrates how to use the DreamFactory REST API. It includes new user registration, user login, and CRUD for related tables.
5 |
6 | #Getting a DreamFactory instance
7 |
8 | To download and install DreamFactory, follow the instructions [here](http://wiki.dreamfactory.com/DreamFactory/Installation). Alternatively, you can create a [free hosted developer account](http://www.dreamfactory.com) at www.dreamfactory.com if you don't want to install DreamFactory locally.
9 |
10 | #Configuring your DreamFactory instance to run the app
11 |
12 | - Enable [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) for development purposes.
13 | - In the admin console, navigate to the Config tab and click on CORS in the left sidebar.
14 | - Click Add.
15 | - Set Origin, Paths, and Headers to *.
16 | - Set Max Age to 0.
17 | - Allow all HTTP verbs and check the Enabled box.
18 | - Click update when you are done.
19 | - More info on setting up CORS is available [here](http://wiki.dreamfactory.com/DreamFactory/Tutorials/Enabling_CORS_Access).
20 |
21 | - Create a default role for new users and enable open registration
22 | - In the admin console, click the Roles tab then click Create in the left sidebar.
23 | - Enter a name for the role and check the Active box.
24 | - Go to the Access tab.
25 | - Add a new entry under Service Access (you can make it more restrictive later).
26 | - set Service = All
27 | - set Component = *
28 | - check all HTTP verbs under Access
29 | - set Requester = API
30 | - Click Create Role.
31 | - Click the Services tab, then edit the user service. Go to Config and enable Allow Open Registration.
32 | - Set the Open Reg Role Id to the name of the role you just created.
33 | - Make sure Open Reg Email Service Id is blank, so that new users can register without email confirmation.
34 | - Save changes.
35 |
36 | - Make sure you have a SQL database service named 'db'. Most DreamFactory instances have a default 'db' service for SQLite. You can add one by going to the Services tab in the admin console and creating a new SQLite service. Make sure you set the name to 'db'.
37 |
38 | - Import the package file for the app.
39 | - From the Apps tab in the admin console, click Import and click 'Address Book for AngularJS' in the list of sample apps. The Address Book package contains the application description, source code, schemas, and sample data.
40 | - Leave storage service and folder blank. It will use the default local file service named 'files'.
41 | - Click the Import button. If successful, your app will appear on the Apps tab. You may have to refresh the page to see your new app in the list.
42 |
43 | - Edit your app API key and instance URL
44 | - Use the file manager to edit app.js and set APP_API_KEY to the key for your new app. The API key is shown on the app details in the Apps tab of the admin console. Set INSTANCE_URL to empty.
45 |
46 | - Make your app files public.
47 | - Figure out where your app files are stored. If you used the default storage settings to import the app, it'll be the default local file service named 'files'.
48 | - Go to the Files tab in the admin console. Find your file service. Double click and find the folder for your app, e.g., 'AddressBookForAngularJS'.
49 | - Go to the Services tab in the admin console and click the 'files' service. Click the Config tab and add the app folder name 'AddressBookForAngularJS' as a public path. Now select the relevant container from the Container drop down. If you used the default storage settings to import the app then select "local" from the drop down list. Save your changes.
50 |
51 | #Running the Address Book app
52 |
53 | You can launch the app from the Apps tab in the admin console.
54 |
55 | When the app starts up you can register a new user, or log in as an existing user. Currently the app does not support registering and logging in admin users.
56 |
57 | #Example API calls
58 |
59 | The DreamFactory Address Book for AngularJS uses $resource and $http to make API calls. The code has been organized in a modular fashion. Hence every module is composed of its own JS, html and css file. Every module has a resource factory defined to interact with DreamFactory API.
60 |
61 | The general form of a DreamFactory REST API call is:
62 |
63 | ` http[s]:///api/v2/[]/[][?=]`
64 |
65 | Use interceptors for prepend instance_url to every api call instead of prepending it everywhere. For more information on interceptors check [official documentation](https://docs.angularjs.org/api/ng/service/$http#interceptors).
66 |
67 | ```javascript
68 | angular.module('your-app', [
69 | //dependencies
70 | ])
71 |
72 | .config([
73 | '$httpProvider',
74 |
75 | function ($httpProvider) {
76 | $httpProvider.interceptors.push('httpInterceptor');
77 | }
78 | ])
79 |
80 |
81 | // Interceptor: Called before making the ajax request. This can be a global place to handle error
82 | // or handle errors etc.
83 |
84 | .factory('httpInterceptor', [
85 | function () {
86 | return {
87 | request: function (config) {
88 |
89 | // Append instance url before every api call
90 | if (config.url.indexOf('/api/v2') > -1) {
91 | config.url = INSTANCE_URL + config.url;
92 | };
93 |
94 | return config;
95 | }
96 | }
97 | }
98 | ])
99 | ```
100 |
101 | Now there are two approaches to make an api call. The first one can be done using [$http](https://docs.angularjs.org/api/ng/service/$http) service. Example:
102 |
103 | ```javascript
104 | angular.module('your-app', [])
105 |
106 | .controller('ModelViewController', [
107 | '$scope', '$http',
108 |
109 | function ($scope, $http) {
110 | $http({
111 | method: 'GET',
112 | url: '/api/v2/_table/Model',
113 | params: {
114 | // query params
115 | }
116 | }).then(function (result) {
117 | $scope.data = result.data.resource;
118 | }, function (error) {
119 | // handle error
120 | })
121 | }
122 | ])
123 | ```
124 |
125 | If you are using angular-resource package, which is a wrapper around $http, you can make a factory for each model. Example:
126 |
127 | ```javascript
128 | angular.module('your-app', [ 'ngResource' ])
129 |
130 | .factory('Model', [
131 | '$resource',
132 |
133 | function ($resource) {
134 | return $resource('/api/v2/_table/Model', {}, {
135 | query: {
136 | method: 'GET',
137 | isArray: false // true if response is an array
138 | },
139 | create: {
140 | method: 'POST',
141 | // url: '/api/v2/Model/create' // You can provide different urls for different CRUD operations
142 | },
143 | update: {
144 | method: 'PUT'
145 | },
146 | remove: {
147 | method: 'DELETE'
148 | }
149 | })
150 | }
151 | ])
152 |
153 | .controller('ModelViewController', [
154 | '$scope', 'Model',
155 |
156 | function ($scope, Model) {
157 | Model.query({
158 | // query params
159 | })
160 | .$promise.then(function (result) {
161 | // success
162 | $scope.list = result.resource
163 | }, function (error) {
164 | // handle error
165 | });
166 | }
167 | ]);
168 | ```
169 |
170 | ##Example of login and registration
171 |
172 |
173 | Recommened approach is to create a service which handles login and registration. This service will take care of handling the response after api calls and storing session token. Example:
174 |
175 | ```javascript
176 | angular.module('your-app', [ 'ngCookies' ])
177 |
178 | .service('AccountService', [
179 | '$http', '$q', '$cookies', '$window',
180 |
181 | function ($http, $q, $cookies, $window) {
182 |
183 | // Handle response from DreamFactory for login and register
184 | var handleResult = function (result) {
185 | // set header X-DreamFactory-Session-Token for all api calls
186 | $http.defaults.headers.common['X-DreamFactory-Session-Token'] = result.data.session_token;
187 | $cookies.session_token = result.data.session_token
188 |
189 | //set user in localStorage and $rootScope for future use.
190 | $rootScope.user = result.data;
191 | try {
192 | $window.localStorage.user = JSON.parse(result.data);
193 | } catch (e) { }
194 | }
195 |
196 | // Login
197 | this.login = function (creds) {
198 |
199 | var deferred = $q.defer();
200 |
201 | $http.post('/api/v2/user/session', creds).then(function (result) {
202 | handleResult(result);
203 | deferred.resolve(result.data);
204 | }, deferred.reject);
205 | };
206 |
207 | // Register
208 |
209 | this.register = function (options) {
210 | var deferred = $q.defer();
211 |
212 | $http.post('/api/v2/user/register?login=true', options).then(function (result) {
213 | handleResult(result)
214 | deferred.resolve(result.data);
215 | }, deferred.reject);
216 |
217 | return deferred.promise;
218 | };
219 | }
220 | ])
221 | ```
222 | The API request will return a session token when the (optional) login=true parameter is appended to the register url. So with this parameter appended, the new registered user doesn't have to login to get a session token. And then from your view controller these methods can be called which returns a promise object. Upon success you can redirect them to application dashboard.
223 |
224 |
225 | ##Example of fetching records
226 |
227 | ####all records in table using $http:
228 |
229 | ```javascript
230 | $http.get('/api/v2/_table/Contact', {
231 | // query params
232 | }).then(function (result) {
233 | // success
234 | }, function () {
235 | // error
236 | });
237 | ```
238 |
239 | ####all records in table using $resource factory as mentioned in previous examples:
240 |
241 | ```javascript
242 | .controller('ViewController', [
243 | '$scope', 'Contact',
244 |
245 | function ($scope, Contact) {
246 | $scope.contacts = Contact.query({
247 | // query params
248 | });
249 |
250 | // $scope.contacts will be populated with data when call is done.
251 | // You can also have success and error callback on the same object.
252 | $scpoe.contacts.$promise.then(function () {
253 | // success
254 | }, function () {
255 | // error
256 | });
257 | }
258 | ])
259 | ```
260 |
261 | ####with fields
262 |
263 | Pass query params for api calls in above example for specific fields. An example with $http service will be like:
264 |
265 | ```javascript
266 | // Only need id, first_name and last_name in list.
267 | $http.get('/api/v2/_table/contact', {
268 | fields: [ 'id', 'first_name', 'last_name' ]
269 | }).then(function (result) {
270 | // success
271 | })
272 | ```
273 |
274 | ####with filter
275 |
276 | ```javascript
277 | // Get contact with id 2.
278 | $http.get('/api/v2/_table', {
279 | filter: 'id=2'
280 | }).then(function (result) {
281 | // success
282 | })
283 | ```
284 |
285 | ##Example of creating a record
286 |
287 | ####single record:
288 |
289 | ```javascript
290 | var group = {
291 | name: 'My Group'
292 | };
293 |
294 | $http.post('/api/v2/_table/contact_group', group).then(function (result) {
295 | // success
296 | }, function () {
297 | // error
298 | })
299 | ```
300 |
301 | or using angular-resource (Assuming a factory is defined as mentioned in previous examples)
302 |
303 | ```javascript
304 | .controller([
305 | '$scope', 'Group',
306 |
307 | function ($scope, Group) {
308 | $scope.newGroup = { name: 'My Group' };
309 | Group.create($scope.newGroup).$promise.then(function (result) {
310 | // success
311 | }, function () {
312 | // error
313 | })
314 | }
315 | ]);
316 | ```
317 |
318 | ##Example of deleting records
319 |
320 | ####with a single id:
321 |
322 | ```javascript
323 | // delete group with id 2
324 | $http.delete('/api/v2/table/contact_group', {
325 | params: {
326 | filter: 'id=2'
327 | }
328 | });
329 | ```
330 |
331 | ####with multiple ids
332 |
333 | ```javascript
334 | // delete group with id 2,3 and 4
335 | $http.delete('/api/v2/table/contact_group', {
336 | params: {
337 | filter: 'id=2,3,4'
338 | }
339 | });
340 | ```
341 |
342 | ####with fields
343 |
344 | ```javascript
345 | // delete contact with first_name 'Andy'
346 | $http.delete('/api/v2/table/contact', {
347 | params: {
348 | filter: 'first_name=Andy'
349 | }
350 | });
351 | ```
352 |
353 |
354 | #Additional Resources
355 |
356 | More detailed information on the DreamFactory REST API is available [here](http://wiki.dreamfactory.com/DreamFactory/API).
357 |
358 | The live API documentation included in the admin console is a great way to learn how the DreamFactory REST API works.
359 |
--------------------------------------------------------------------------------