Skip to content

Commit e3fe3c2

Browse files
committed
Merge pull request #198 from Stabzs/master
Added additional validation for directiveTemplate
2 parents b79e1f3 + ba9822e commit e3fe3c2

8 files changed

+107
-38
lines changed

README.md

+13-8
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ AngularJS-Toaster
44
**AngularJS Toaster** is an AngularJS port of the **toastr** non-blocking notification jQuery library. It requires AngularJS v1.2.6 or higher and angular-animate for the CSS3 transformations.
55

66
[![Build Status](https://travis-ci.org/jirikavi/AngularJS-Toaster.svg)](https://travis-ci.org/jirikavi/AngularJS-Toaster)
7-
[![Coverage Status](https://coveralls.io/repos/jirikavi/AngularJS-Toaster/badge.svg?branch=master&service=github&busted=1)](https://coveralls.io/github/jirikavi/AngularJS-Toaster?branch=master)
7+
[![Coverage Status](https://coveralls.io/repos/jirikavi/AngularJS-Toaster/badge.svg?branch=master&service=github&busting=3)](https://coveralls.io/github/jirikavi/AngularJS-Toaster?branch=master)
88

9-
### Current Version 1.1.0
9+
### Current Version 1.2.0
10+
11+
## Angular Compatibility
12+
AngularJS-Toaster requires AngularJS v1.2.6 or higher and specifically targets AngularJS, not Angular 2, although it could be used via ngUpgrade.
13+
If you are looking for the Angular 2 port of AngularJS-Toaster, it is located [here](https://github.com/Stabzs/Angular2-Toaster).
1014

1115
## Demo
1216
- Simple demo is at http://plnkr.co/edit/HKTC1a
@@ -129,8 +133,8 @@ There are four types of body renderings: trustedHtml', 'template', 'templateWith
129133

130134
- directive
131135
- Will use the `toast.body` argument to represent the name of a directive that you want to render as the toast's body, else it will fallback to the template bound to the `'body-template': 'toasterBodyTmpl.html'` configuration option.
132-
The directive name being passed to the `body` argument should be normalized as it exists in the markup,
133-
not camelCased as it would appear in the directive declaration (`cool-directive-name` instead of `coolDirectiveName`).
136+
The directive name being passed to the `body` argument should appear as it exists in the markup,
137+
not camelCased as it would appear in the directive declaration (`cool-directive-name` instead of `coolDirectiveName`). The directive must be usable as an attribute.
134138

135139
```js
136140
// The toast pop call, passing in a directive name to be rendered
@@ -149,7 +153,8 @@ There are four types of body renderings: trustedHtml', 'template', 'templateWith
149153
};
150154
}])
151155
```
152-
- Will use the `toast.directiveData` argument to accept data that will be bound to the directive's scope.
156+
- Will use the `toast.directiveData` argument to accept data that will be bound to the directive's scope. The directive cannot use isolateScope and will
157+
throw an exception if isolateScope is detected. All data must be passed via the directiveData argument.
153158

154159
```js
155160
// The toast pop call, passing in a directive name to be rendered
@@ -275,7 +280,7 @@ If you do not want to use animations, you can safely remove the angular-animate.
275280
### Common Issues
276281
- Toaster always shows up as "info"
277282
- Your `<toaster-container></toaster-container` might be placed inside of your routing directive.
278-
- You have multiple `<toaster-container></toaster-container` elements without unqiue `toaster-id` configuration arguments.
283+
- You have multiple `<toaster-container></toaster-container` elements without unique `toaster-id` configuration arguments.
279284
- [$sce:itype] Attempted to trust a non-string value in a content requiring a string
280285
- You have not specified: `bodyOutputType: 'trustedHtml'` when passing html as a body argument.
281286
- My toasts do not show up when I pop them, but after I perform another action.
@@ -294,7 +299,7 @@ If you do not want to use animations, you can safely remove the angular-animate.
294299
Inspired by http://codeseven.github.io/toastr/demo.html.
295300

296301
## Copyright
297-
Copyright © 2013-2015 [Jiri Kavulak](https://twitter.com/jirikavi).
302+
Copyright © 2013-2016 [Jiri Kavulak](https://twitter.com/jirikavi).
298303

299304
## License
300-
AngularJS-Toaster is under MIT license - http://www.opensource.org/licenses/mit-license.php
305+
AngularJS-Toaster is under MIT license - http://www.opensource.org/licenses/mit-license.php

bower.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "AngularJS-Toaster",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"main": [
55
"toaster.js",
66
"toaster.css"

package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angularjs-toaster",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"description": "AngularJS Toaster is a customized version of toastr non-blocking notification javascript library",
55
"author": "Jiri Kavulak",
66
"license": "MIT",
@@ -14,11 +14,11 @@
1414
"angular-animate": "~1.2.8",
1515
"angular-mocks": "^1.4.7",
1616
"jasmine-core": "^2.3.4",
17-
"karma": "^0.13.14",
18-
"karma-chrome-launcher": "^0.2.1",
17+
"karma": "^0.13.21",
18+
"karma-chrome-launcher": "^0.2.2",
1919
"karma-coverage": "^0.5.3",
20-
"karma-jasmine": "^0.3.6",
21-
"coveralls": "^2.11.4"
20+
"karma-jasmine": "^0.3.7",
21+
"coveralls": "^2.11.6"
2222
},
2323
"jspm": {
2424
"main": "toaster",

test/directiveTemplateSpec.js

+58-15
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
'use strict';
44

55
describe('directiveTemplate', function () {
6-
createDirectives();
7-
86
var toaster, scope, $compile;
97

108
beforeEach(function () {
9+
createDirectives();
10+
1111
// load dependencies
1212
module('testApp');
13-
module('toaster')
13+
module('toaster');
1414

1515
// inject the toaster service
1616
inject(function (_toaster_, _$rootScope_, _$compile_) {
@@ -62,20 +62,38 @@ describe('directiveTemplate', function () {
6262
expect(container[0].innerText).toBe('Unrestricted Template');
6363
});
6464

65-
it('should not bind Element-restricted templates', function () {
65+
it('should not bind Element-only-restricted templates', function () {
66+
var hasError = false;
6667
var container = compileContainer();
67-
pop({ type: 'info', body: 'element-template', bodyOutputType: 'directive' });
68-
69-
expect(container[0].innerText).toBe('');
70-
expect(container[0].innerText).not.toBe('Element Template');
68+
69+
try {
70+
pop({ type: 'info', body: 'element-template', bodyOutputType: 'directive' });
71+
} catch(e) {
72+
var message = 'Directives must be usable as attributes. ' +
73+
'Add "A" to the restrict option (or remove the option entirely). Occurred for directive element-template.';
74+
75+
expect(e.message).toBe(message);
76+
hasError = true;
77+
}
78+
79+
expect(hasError).toBe(true);
7180
});
7281

73-
it('should not bind Class-restricted templates', function () {
82+
it('should not bind Class-only-restricted templates', function () {
83+
var hasError = false;
7484
var container = compileContainer();
75-
pop({ type: 'info', body: 'class-template', bodyOutputType: 'directive' });
76-
77-
expect(container[0].innerText).toBe('');
78-
expect(container[0].innerText).not.toBe('Class Template');
85+
86+
try {
87+
pop({ type: 'info', body: 'class-template', bodyOutputType: 'directive' });
88+
} catch(e) {
89+
var message = 'Directives must be usable as attributes. ' +
90+
'Add "A" to the restrict option (or remove the option entirely). Occurred for directive class-template.';
91+
92+
expect(e.message).toBe(message);
93+
hasError = true;
94+
}
95+
96+
expect(hasError).toBe(true);
7997
});
8098

8199
it('should throw an error if directiveName argument is not passed via body', function () {
@@ -120,12 +138,34 @@ describe('directiveTemplate', function () {
120138
try {
121139
pop({ type: 'info', body: 'non-existent-directive', bodyOutputType: 'directive' });
122140
} catch (e) {
123-
expect(e.message).toBe('non-existent-directive could not be found.');
141+
var message = 'non-existent-directive could not be found. ' +
142+
'The name should appear as it exists in the markup,' +
143+
' not camelCased as it would appear in the directive declaration,' +
144+
' e.g. directive-name not directiveName.'
145+
146+
expect(e.message).toBe(message);
124147
hasError = true;
125148
}
126149

127150
expect(hasError).toBe(true);
128151
});
152+
153+
it('should throw an error if the directive uses isolate scope', function () {
154+
var hasError = false;
155+
compileContainer();
156+
157+
try {
158+
pop({ type: 'info', body: 'isolate-scope', bodyOutputType: 'directive' });
159+
} catch (e) {
160+
var message = 'Cannot use a directive with an isolated scope.' +
161+
' The scope must be either true or falsy (e.g. false/null/undefined). Occurred for directive isolate-scope.';
162+
163+
expect(e.message).toBe(message)
164+
hasError = true;
165+
}
166+
167+
expect(hasError).toBe(true);
168+
})
129169

130170

131171
function compileContainer() {
@@ -165,6 +205,9 @@ describe('directiveTemplate', function () {
165205
})
166206
.directive('unrestrictedTemplate', function () {
167207
return { template: 'Unrestricted Template' }
168-
});
208+
})
209+
.directive('isolateScope', function () {
210+
return { template: 'isolate scope template', scope: {}}
211+
});
169212
}
170213
})

toaster.css

+2-1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ button.toast-close-button {
9797
#toast-container {
9898
position: fixed;
9999
z-index: 999999;
100+
pointer-events: auto;
100101
/*overrides*/
101102

102103
}
@@ -236,4 +237,4 @@ button.toast-close-button {
236237
:not(.no-leave)#toast-container > div.ng-leave.ng-leave-active,
237238
:not(.no-enter)#toast-container > div.ng-enter {
238239
opacity: 0;
239-
}
240+
}

toaster.js

+25-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
/*
66
* AngularJS Toaster
7-
* Version: 1.1.0
7+
* Version: 1.2.0
88
*
99
* Copyright 2013-2016 Jiri Kavulak.
1010
* All Rights Reserved.
@@ -200,11 +200,31 @@
200200
if (angular.isUndefined(directiveName) || directiveName.length <= 0)
201201
throw new Error('A valid directive name must be provided via the toast body argument when using bodyOutputType: directive');
202202

203-
var directiveExists = $injector.has(attrs.$normalize(directiveName) + 'Directive');
203+
var directive;
204+
205+
try {
206+
directive = $injector.get(attrs.$normalize(directiveName) + 'Directive');
207+
} catch(e) {
208+
throw new Error(directiveName + ' could not be found. ' +
209+
'The name should appear as it exists in the markup, not camelCased as it would appear in the directive declaration,' +
210+
' e.g. directive-name not directiveName.');
211+
}
204212

205-
if (!directiveExists)
206-
throw new Error(directiveName + ' could not be found.');
207213

214+
var directiveDetails = directive[0];
215+
216+
if (directiveDetails.scope !== true && directiveDetails.scope) {
217+
throw new Error('Cannot use a directive with an isolated scope. ' +
218+
'The scope must be either true or falsy (e.g. false/null/undefined). ' +
219+
'Occurred for directive ' + directiveName + '.');
220+
}
221+
222+
if (directiveDetails.restrict.indexOf('A') < 0) {
223+
throw new Error('Directives must be usable as attributes. ' +
224+
'Add "A" to the restrict option (or remove the option entirely). Occurred for directive ' +
225+
directiveName + '.');
226+
}
227+
208228
if (scope.directiveData)
209229
scope.directiveData = angular.fromJson(scope.directiveData);
210230

@@ -213,7 +233,7 @@
213233
elm.append(template);
214234
});
215235
}
216-
}
236+
};
217237
}])
218238
.directive(
219239
'toasterContainer', [

0 commit comments

Comments
 (0)