├── .gitattributes
├── .gitignore
├── .travis.yml
├── Gruntfile.js
├── LICENSE.md
├── README.md
├── api
├── bower.json
├── dist
├── angular-cog.js
└── angular-cog.min.js
├── example
├── index.html
└── main.controller.js
├── karma.conf.js
├── package.json
├── src
└── angular-cog.js
├── tests
└── angular-cog.tests.js
└── why.jpg
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .tmp
3 | .sass-cache
4 | bower_components
5 | coverage
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 0.10
4 | before_script: "npm install grunt-cli -g && grunt"
5 | notifications:
6 | email: false
7 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | grunt.initConfig({
4 | ngmin: {
5 | directives: {
6 | src: ['src/*.js'],
7 | dest: 'build/directives.js'
8 | }
9 | },
10 | pkg: grunt.file.readJSON('package.json'),
11 | concat: {
12 | dist: {
13 | src: ['build/*.js'],
14 | dest: 'dist/angular-cog.js'
15 | }
16 | },
17 | uglify: {
18 | options: {
19 | banner: '/*! <%= pkg.name %> - v<%= pkg.version %> */'
20 | },
21 | dist: {
22 | src: 'dist/angular-cog.js',
23 | dest: 'dist/angular-cog.min.js'
24 | }
25 | },
26 | clean: ["build"],
27 | watch: {
28 | scripts: {
29 | files: ['src/**/*.js', 'tests/**/*.js'],
30 | tasks: ['ngmin', 'uglify', 'clean', 'karma:unit:run'],
31 | options: {
32 | debounceDelay: 250,
33 | },
34 | }
35 | },
36 | karma: {
37 | unit: {
38 | configFile: 'karma.conf.js',
39 | // run karma in the background
40 | background: true,
41 | // which browsers to run the tests on
42 | browsers: ['Chrome']
43 | }
44 | }
45 | });
46 |
47 | grunt.loadNpmTasks('grunt-contrib-concat');
48 | grunt.loadNpmTasks('grunt-ngmin');
49 | grunt.loadNpmTasks('grunt-contrib-uglify');
50 | grunt.loadNpmTasks('grunt-contrib-copy');
51 | grunt.loadNpmTasks('grunt-contrib-clean');
52 | grunt.loadNpmTasks('grunt-contrib-watch');
53 | grunt.loadNpmTasks('grunt-karma');
54 |
55 | grunt.registerTask('default', ['ngmin', 'concat', 'uglify', 'clean']);
56 |
57 | };
58 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Chinmay Kulkarni
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | angular-cog
2 | ===========
3 | declarative ajax requests for angularjs
4 |
5 | ### Cog?
6 | A subordinate member of an organization who performs necessary but usually minor or routine functions.
7 |
8 | ### Groundwork first
9 | angular-cog exposes few directives that act as helpers for ajax requests. For every request 4 attributes are most important
10 |
11 | 1. Url
12 | 2. Data to be sent
13 | 3. Headers
14 | 3. Whether reponse is success or error
15 |
16 | angular-cog allows you to configure these from html directive and remove the clutter from javascript.
17 |
18 | ### Installation
19 | Installation is very straight forward. Grab the latest zip from github. Copy angular-cog.min.js in your root, and refer it in your page.
20 | ```html
21 |
22 | ```
23 | Then,add it as dependency in your module
24 | ```javascript
25 | angular.module('yourApp', ['angularCog']);
26 | ```
27 | ### Directive
28 | ```html
29 |
30 |
38 |
39 | ```
40 |
41 | Attribute | Meaning | Example
42 | --- | --- | ---
43 | ```cog-*verb*``` | url to call, [get,post,put,delete] decides the http verb | ```cog-get="/users"```, ```cog-post="/users"```
44 | ```cog-model``` | data that will be passed with post and put request | ```cog-model="user"```
45 | ```cog-success``` | expression to evaluate if response is 200 | ```cog-success="users = $data"```, ```cog-success="setUsers($data)```
46 | ```cog-error``` | expression to evaluate if response is not 200 | ```cog-error="error($headers)"```
47 | ```cog-trigger``` | by default, request will be sent as soon as directive is compiled, in case you would like to delay the request you could use cog-trigger. angular-cog will watch the expression and fire the request as soon as it becomes true. **applicable to cog-get only** | ```cog-trigger="user != null"```
48 | ```cog-no-spinner``` | if you don't want to display spinner for this particular request, refer [spinner](#spinner) for more information | ```cog-no-spinner="true"```
49 | ```cog-absolute-url``` | by default CogConfig.rootUrl will be prepended to every url, if you don't want to then use this attribute | ```cog-absolute-url="true"```
50 | ```cog-config``` | header object to pass to angular $http |
51 |
52 | **Note:** ```cog-post``` and ```cog-put``` are only allowed on ```
106 | ```
107 | **cog-put**
108 | ```html
109 |
111 |
112 |
118 | ```
119 | **Note:** for ```cog-put``` and ```cog-post```, ```cog-model``` is necessary. else the request will be sent with blank payload. Also, ```cog-put``` and ```cog-post``` can be only used with ```
28 |
29 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/example/main.controller.js:
--------------------------------------------------------------------------------
1 | angular.module('example', ['angularCog']);
2 |
3 | function MainController($scope) {
4 | $scope.users = [];
5 | }
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | var grunt = require('grunt');
2 | module.exports = function(karma) {
3 | karma.set({
4 | /**
5 | * From where to look for files, starting with the location of this file.
6 | */
7 | basePath: '.',
8 |
9 | /**
10 | * This is the list of file patterns to load into the browser during testing.
11 | */
12 | files: [
13 | "bower_components/angular/angular.min.js",
14 | "bower_components/angular/angular-mocks.js",
15 | "dist/*.js",
16 | "tests/*.js"
17 | ],
18 |
19 | preprocessors: {
20 | 'dist/*.js': ['coverage']
21 | },
22 |
23 | frameworks: ['jasmine'],
24 | plugins: ['karma-jasmine', 'karma-chrome-launcher', 'karma-coverage'],
25 |
26 | logLevel: 'WARN',
27 | /**
28 | * How to report, by default.
29 | */
30 | reporters: ['dots', 'coverage'],
31 |
32 | coverageReporter: {
33 | type: 'html',
34 | dir: 'coverage/'
35 | },
36 | /**
37 | * On which port should the browser connect, on which port is the test runner
38 | * operating, and what is the URL path for the browser to use.
39 | */
40 | port: 7019,
41 | urlRoot: '/',
42 |
43 | /**
44 | * Disable file watching by default.
45 | */
46 | autoWatch: true,
47 |
48 | /**
49 | * The list of browsers to launch to test ondest * default, but other browser names include:
50 | * Chrome, ChromeCanary, Firefox, Opera, Safari, PhantomJS
51 | *
52 | * Note that you can also use the executable name of the browser, like "chromium"
53 | * or "firefox", but that these vary based on your operating system.
54 | *
55 | * You may also leave this blank and manually navigate your browser to
56 | * http://localhost:9018/ when you're running tests. The window/tab can be left
57 | * open and the tests will automatically occur there during the build. This has
58 | * the aesthetic advantage of not launching a browser every time you save.
59 | */
60 | browsers: [
61 | 'Chrome'
62 | ]
63 | });
64 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-cog",
3 | "version": "1.0.0",
4 | "description": "declarative $http for angularjs",
5 | "main": "Gruntfile.js",
6 | "preinstall" : "npm install -g grunt && npm install grunt-cli -g",
7 | "devDependencies": {
8 | "grunt": "~0.4.1",
9 | "grunt-contrib-concat": "*",
10 | "grunt-contrib-uglify": "*",
11 | "grunt-contrib-copy": "*",
12 | "grunt-contrib-clean": "*",
13 | "grunt-ngmin": "0.0.3",
14 | "grunt-contrib-watch": "~0.5.3",
15 | "karma": "^0.12.6",
16 | "grunt-karma": "^0.8.2",
17 | "karma-jasmine": "^0.1.5",
18 | "karma-chrome-launcher": "^0.1.3",
19 | "karma-coverage": "^0.2.1"
20 | },
21 | "scripts": {
22 | "test": "./node_modules/karma/bin/karma start"
23 | },
24 | "repository": "",
25 | "author": "chinmaymk",
26 | "license": "MIT"
27 | }
28 |
--------------------------------------------------------------------------------
/src/angular-cog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main module
3 | */
4 | angular.module('angularCog', []);
5 |
6 |
7 | /**
8 | * GET directive, this sends request immedietly
9 | * @param {[type]} MakeRequestService [description]
10 | * @return {[type]} [description]
11 | */
12 | angular.module('angularCog').directive('cogGet', function(MakeRequestService) {
13 | return {
14 | restrict: 'A',
15 | scope: true,
16 | link: function(scope, element, attrs) {
17 | //check if there's a trigger
18 | if (attrs.cogTrigger) {
19 | //watch for the expression
20 | scope.$watch(attrs.cogTrigger, function(newVal, oldVal) {
21 | //if the changed value becomes true, fire away request!
22 | if (newVal) {
23 | MakeRequestService('get', null, scope, attrs);
24 | }
25 | })
26 | } else {
27 | //make immediate request if there isn't
28 | MakeRequestService('get', null, scope, attrs);
29 | }
30 | }
31 | }
32 | });
33 |
34 | /**
35 | * POST directive, listens to form submit
36 | * @param {[type]} MakeRequestService [description]
37 | * @return {[type]} [description]
38 | */
39 | angular.module('angularCog').directive('cogPost', function(MakeRequestService) {
40 | return {
41 | restrict: 'A',
42 | require: '^form',
43 | scope: true,
44 | link: function(scope, element, attrs) {
45 | element.bind("submit", function() {
46 | var data = scope[attrs.cogModel];
47 | MakeRequestService('post', data, scope, attrs);
48 | });
49 | }
50 | }
51 | })
52 |
53 | /**
54 | * PUT directive, listens to form submit
55 | * @param {[type]} MakeRequestService [description]
56 | * @return {[type]} [description]
57 | */
58 | angular.module('angularCog').directive('cogPut', function(MakeRequestService) {
59 | return {
60 | restrict: 'A',
61 | require: '^form',
62 | scope: true,
63 | link: function(scope, element, attrs) {
64 | element.bind("submit", function() {
65 | var data = scope[attrs.cogModel];
66 | MakeRequestService('put', data, scope, attrs);
67 | });
68 | }
69 | }
70 | })
71 |
72 | /**
73 | * DELETE directive, listens to click event
74 | * @param {[type]} MakeRequestService [description]
75 | * @return {[type]} [description]
76 | */
77 | angular.module('angularCog').directive('cogDelete', function(MakeRequestService) {
78 | return {
79 | restrict: 'A',
80 | scope: true,
81 | link: function(scope, element, attrs) {
82 | element.bind("click", function() {
83 | console.log('called');
84 | MakeRequestService('delete', null, scope, attrs);
85 | });
86 | }
87 | }
88 | });
89 |
90 | /**
91 | * Helper service for making http calls, just to DRY up directives
92 | * @param {[type]} $http [description]
93 | * @param {[type]} CogConfig [description]
94 | * @param {[type]} SpinnerService [description]
95 | * @return {[type]} [description]
96 | */
97 | angular.module('angularCog').factory('MakeRequestService', function($http, CogConfig, SpinnerService) {
98 | /**
99 | * Does actual request
100 | * @param {string} verb http verb
101 | * @param {object|string} data data to be sent to server
102 | * @param {object} scope angularjs scope
103 | * @param {object} attrs attributes of element
104 | */
105 | function MakeRequest(verb, data, scope, attrs) {
106 | //send the http request
107 | $http({
108 | method: verb,
109 | //gets url based on verb and attribute
110 | url: getUrl(verb, attrs),
111 | data: data,
112 | config: scope[attrs.CogConfig]
113 | }).success(function(data, status, headers, config) {
114 | //common function to set scope variables
115 | responseReceived(data, status, headers, config);
116 | //evaluates cog-success expression of directive
117 | scope.$eval(attrs.cogSuccess);
118 | //global success handler
119 | CogConfig.success(data, status, headers, config);
120 | }).error(function(data, status, headers, config) {
121 | //common function to set scope variables
122 | responseReceived(data, status, headers, config);
123 | //evaluates cog-error expression of directive
124 | scope.$eval(attrs.cogError);
125 | //global error handler
126 | CogConfig.error(data, status, headers, config);
127 | });
128 |
129 | //sometimes $apply is not called immedietly, so this fix
130 | if (!scope.$$phase) {
131 | scope.$apply();
132 | }
133 |
134 | //start the spinner if enabled
135 | if (CogConfig.enableSpinner && !attrs.cogNoSpinner) {
136 | SpinnerService.spin();
137 | }
138 |
139 | /**
140 | * Common function that gets called for setting scope variables
141 | * @param {object|string} data http response
142 | * @param {int} status response code
143 | * @param {object} headers [description]
144 | * @param {object} config [description]
145 | * @return {[type]} [description]
146 | */
147 | function responseReceived(data, status, headers, config) {
148 | scope.$data = data;
149 | scope.$status = status;
150 | scope.$headers = headers;
151 | scope.$config = config;
152 | //stop the spinner once response is recieved
153 | if (CogConfig.enableSpinner && !attrs.cogNoSpinner) {
154 | SpinnerService.stop();
155 | }
156 | //pass the data to global log
157 | CogConfig.log(data, status, headers, config);
158 | }
159 | };
160 |
161 | /**
162 | * return appropriate url based on verb
163 | * @param {string} verb http verb
164 | * @param {object} attrs [description]
165 | * @return {[type]} [description]
166 | */
167 | function getUrl(verb, attrs) {
168 | var url = attrs.cogAbsoluteUrl ? '' : CogConfig.rootUrl;
169 | switch (verb) {
170 | case 'get':
171 | url += attrs.cogGet;
172 | break;
173 | case 'post':
174 | url += attrs.cogPost;
175 | break;
176 | case 'put':
177 | url += attrs.cogPut;
178 | break;
179 | case 'delete':
180 | url += attrs.cogDelete;
181 | break;
182 | }
183 | return url;
184 | }
185 | //return MakeRequest as factory handler
186 | return MakeRequest;
187 | })
188 |
189 | /**
190 | * Hacked a service to display spinner on screen
191 | * I'm not proud of it, but it had to be done.
192 | * @param {[type]} CogConfig [description]
193 | * @return {[type]} [description]
194 | */
195 | angular.module('angularCog').service("SpinnerService", function(CogConfig) {
196 |
197 | //html elements and styles
198 | var spinnerStyle = [
199 | "position:absolute;",
200 | "top:50%;",
201 | "left:50%;",
202 | "z-index:2000"
203 | ].join('');
204 | var spinner = angular.element("");
205 | spinner.attr("style", spinnerStyle)
206 | //spinner.find("img").attr("src", CogConfig.imagePath);
207 |
208 | var backdropStyle = ["position:fixed;",
209 | "top:0;",
210 | "left:0;",
211 | "width:100%;",
212 | "height:100%;",
213 | "background:white;",
214 | "text-align:center;",
215 | "opacity:0.7;"
216 | ].join('');
217 | var backdrop = angular.element("");
218 | backdrop.attr("style", backdropStyle);
219 |
220 | //keep track of all requests
221 | //makes life easier when multiple ajax requests are fired from same view
222 | var requests = [];
223 | var isSpinning = false;
224 | return {
225 | //enqueue the request
226 | //append backdrop
227 | //append the spinner
228 | //if you dont spinner for a particular request set this to true
229 | spin: function() {
230 | requests.push(1);
231 | if (!isSpinning) {
232 | angular.element(document.body).append(backdrop);
233 | angular.element(document.body).append(spinner);
234 | isSpinning = true;
235 | }
236 | },
237 | //dequeue the request
238 | //remove the backdrop
239 | //remove spinner
240 | stop: function() {
241 | requests.pop();
242 | if (requests.length == 0) {
243 | backdrop.remove();
244 | spinner.remove();
245 | isSpinning = false;
246 | }
247 | }
248 | }
249 | });
250 |
251 | /**
252 | * Configuration for angular-cog
253 | * @return {[type]} [description]
254 | */
255 | angular.module('angularCog').provider('CogConfig', function() {
256 | this.rootUrl = '';
257 | this.log = angular.noop;
258 | this.imagePath = "spinner.gif";
259 | this.error = angular.noop;
260 | this.success = angular.noop;
261 | this.enableSpinner = true;
262 | var self = this;
263 | this.$get = function() {
264 | return self;
265 | }
266 | });
267 |
--------------------------------------------------------------------------------
/tests/angular-cog.tests.js:
--------------------------------------------------------------------------------
1 | describe('angular-cog directive', function() {
2 |
3 | // we declare some global vars to be used in the tests
4 | var elm, // our directive jqLite element
5 | scope, // the scope where our directive is inserted
6 | httpBackend;
7 |
8 | // load the modules we want to test
9 | beforeEach(module('angularCog'));
10 |
11 | // before each test, creates a new fresh scope
12 | // the inject function interest is to make use of the angularJS
13 | // dependency injection to get some other services in our test
14 | // here we need $rootScope to create a new scope
15 | beforeEach(inject(function($rootScope, $compile, $httpBackend) {
16 | scope = $rootScope.$new();
17 | scope.success = function() {
18 | scope.done = true;
19 | }
20 | scope.error = function() {
21 | scope.notdone = true;
22 | }
23 | httpBackend = $httpBackend;
24 | httpBackend.when("GET", "/users").respond([{}, {}, {}]);
25 | httpBackend.when("GET", "/notusers").respond(404);
26 | }));
27 |
28 | afterEach(function() {
29 | httpBackend.verifyNoOutstandingExpectation();
30 | httpBackend.verifyNoOutstandingRequest();
31 | });
32 |
33 | function compileDirective(tpl) {
34 | // function to compile a fresh directive with the given template, or a default one
35 | // compile the tpl with the $rootScope created above
36 | // wrap our directive inside a form to be able to test
37 | // that our form integration works well (via ngModelController)
38 | // our directive instance is then put in the global 'elm' variable for further tests
39 | var template = tpl || '';
40 |
41 | // inject allows you to use AngularJS dependency injection
42 | // to retrieve and use other services
43 | inject(function($compile) {
44 | $compile(template)(scope);
45 | });
46 | // $digest is necessary to finalize the directive generation
47 | scope.$digest();
48 | }
49 |
50 | // make successful request
51 | it('should make a get request to /users', function() {
52 | compileDirective('');
53 | httpBackend.flush();
54 | expect(scope.done).toBe(true);
55 | });
56 |
57 | // make successful request
58 | it('should make a failed get request to /users', function() {
59 | compileDirective('');
60 | httpBackend.flush();
61 | expect(scope.notdone).toBe(true);
62 | });
63 | });
--------------------------------------------------------------------------------
/why.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chinmaymk/angular-cog/5020d2602712321a19432ae7cd3e5fd6381c9121/why.jpg
--------------------------------------------------------------------------------