Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit 81e94b4

Browse files
fix(dialog): enable support for html content for alert and confirm dialogs
* dialog`::onShow( )` now uses `wrapSimpleContent( )` to wrap simple strings in `<p>` tags * dialog basic demo `showConfirm( )` uses HTML span tag to illustrate use of HMTL content * implemented generic `md-template` to safely inject HTML content into an element BREAKING CHANGE: dialog content text is now injected into **div.md-dialog-content-body** Before the template used was: ```html <md-dialog-content> <h2></h2> <p></p> </md-dialog-content> ``` Now uses: ```html <md-dialog-content> <h2></h2> <div class="md-dialog-content-body"> <p></p> </div> </md-dialog-content> ``` Fixes #1495.
1 parent 9fce4b9 commit 81e94b4

File tree

8 files changed

+132
-34
lines changed

8 files changed

+132
-34
lines changed

src/components/dialog/demoBasicUsage/script.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ angular.module('dialogDemo1', ['ngMaterial'])
2323
// Appending dialog to document.body to cover sidenav in docs app
2424
var confirm = $mdDialog.confirm()
2525
.title('Would you like to delete your debt?')
26-
.content('All of the banks have agreed to forgive you your debts.')
26+
.content('All of the banks have agreed to <span class="debt-be-gone">forgive</span> you your debts.')
2727
.ariaLabel('Lucky day')
2828
.targetEvent(ev)
2929
.ok('Please do it!')
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
#popupContainer {
22
position:relative;
33
}
4+
5+
.debt-be-gone {
6+
font-weight: bold;
7+
color:blue;
8+
text-decoration: underline;
9+
}

src/components/dialog/dialog.js

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
* @ngdoc module
33
* @name material.components.dialog
44
*/
5-
angular.module('material.components.dialog', [
6-
'material.core',
7-
'material.components.backdrop'
8-
])
5+
angular
6+
.module('material.components.dialog', [
7+
'material.core',
8+
'material.components.backdrop'
9+
])
910
.directive('mdDialog', MdDialogDirective)
1011
.provider('$mdDialog', MdDialogProvider);
1112

@@ -32,8 +33,6 @@ function MdDialogDirective($$rAF, $mdTheming) {
3233
};
3334
}
3435

35-
36-
3736
/**
3837
* @ngdoc service
3938
* @name $mdDialog
@@ -408,7 +407,7 @@ function MdDialogProvider($$interimElementProvider) {
408407
'<md-dialog md-theme="{{ dialog.theme }}" aria-label="{{ dialog.ariaLabel }}">',
409408
' <md-dialog-content role="document" tabIndex="-1">',
410409
' <h2 class="md-title">{{ dialog.title }}</h2>',
411-
' <p>{{ dialog.content }}</p>',
410+
' <div class="md-dialog-content-body" md-template="::dialog.content"></div>',
412411
' </md-dialog-content>',
413412
' <div class="md-actions">',
414413
' <md-button ng-if="dialog.$type == \'confirm\'"' +
@@ -459,6 +458,8 @@ function MdDialogProvider($$interimElementProvider) {
459458
element = $mdUtil.extractElementByName(element, 'md-dialog');
460459
angular.element($document[0].body).addClass('md-dialog-is-showing');
461460

461+
wrapSimpleContent();
462+
462463
captureSourceAndParent(element, options);
463464
configureAria(element.find('md-dialog'), options);
464465
showBackdrop(scope, element, options);
@@ -494,6 +495,26 @@ function MdDialogProvider($$interimElementProvider) {
494495
}
495496
}
496497

498+
/**
499+
* Wrap any simple content [specified via .content("")] in <p></p> tags.
500+
* otherwise accept HTML content within the dialog content area...
501+
* NOTE: Dialog uses the md-template directive to safely inject HTML content.
502+
*/
503+
function wrapSimpleContent() {
504+
if ( controller ) {
505+
var HTML_END_TAG = /<\/[\w-]*>/gm;
506+
var content = controller.content;
507+
508+
var hasHTML = HTML_END_TAG.test(content);
509+
if (!hasHTML) {
510+
content = $mdUtil.supplant("<p>{0}</p>", [content]);
511+
}
512+
513+
// Publish updated dialog content body... to be compiled by mdTemplate directive
514+
controller.content = content;
515+
}
516+
}
517+
497518
}
498519

499520
/**
@@ -538,8 +559,6 @@ function MdDialogProvider($$interimElementProvider) {
538559
// In case the user provides a raw dom element, always wrap it in jqLite
539560
options.parent = angular.element(options.parent || $rootElement);
540561

541-
542-
543562
}
544563

545564
/**

src/components/dialog/dialog.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ md-dialog {
7676
padding-top: 0;
7777
}
7878
}
79+
80+
.md-dialog-content-body {
81+
width:100%;
82+
}
7983
}
8084

8185
.md-actions {

src/components/dialog/dialog.spec.js

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ describe('$mdDialog', function() {
1919
$timeout.flush(); // flush to start animations
2020
$$rAF.flush(); // flush animations
2121
$animate.triggerCallbacks();
22+
//$rootScope.$digest();
2223
$timeout.flush(); // flush responses after animation completions
2324
}
2425
}));
@@ -42,8 +43,8 @@ describe('$mdDialog', function() {
4243
.theme('some-theme')
4344
.ok('Next')
4445
).then(function() {
45-
resolved = true;
46-
});
46+
resolved = true;
47+
});
4748

4849
$rootScope.$apply();
4950
runAnimation();
@@ -130,7 +131,7 @@ describe('$mdDialog', function() {
130131
'ok', 'cancel', 'targetEvent', 'theme'
131132
]);
132133

133-
it('shows a basic confirm dialog', inject(function($rootScope, $mdDialog, $animate) {
134+
it('shows a basic confirm dialog with simple text content', inject(function($rootScope, $mdDialog, $animate) {
134135
var parent = angular.element('<div>');
135136
var rejected = false;
136137
$mdDialog.show(
@@ -149,15 +150,13 @@ describe('$mdDialog', function() {
149150

150151
var container = angular.element(parent[0].querySelector('.md-dialog-container'));
151152
var dialog = parent.find('md-dialog');
152-
expect(dialog.attr('role')).toBe('dialog');
153-
154153
var title = parent.find('h2');
155-
expect(title.text()).toBe('Title');
156-
157154
var content = parent.find('p');
158-
expect(content.text()).toBe('Hello world');
159-
160155
var buttons = parent.find('md-button');
156+
157+
expect(dialog.attr('role')).toBe('dialog');
158+
expect(title.text()).toBe('Title');
159+
expect(content.text()).toBe('Hello world');
161160
expect(buttons.length).toBe(2);
162161
expect(buttons.eq(0).text()).toBe('Next');
163162
expect(buttons.eq(1).text()).toBe('Forget it');
@@ -169,17 +168,60 @@ describe('$mdDialog', function() {
169168
expect(rejected).toBe(true);
170169
}));
171170

171+
it('shows a basic confirm dialog with HTML content', inject(function($rootScope, $mdDialog, $animate) {
172+
var parent = angular.element('<div>');
173+
174+
$mdDialog.show(
175+
$mdDialog.confirm({
176+
parent: parent,
177+
ok: 'Next',
178+
cancel: 'Back',
179+
title: 'Which Way ',
180+
content: '<div class="mine">Choose</div>'
181+
})
182+
);
183+
184+
runAnimation();
185+
186+
var container = angular.element(parent[0].querySelector('.md-dialog-container'));
187+
var content = angular.element(container[0].querySelector('.mine'));
188+
189+
expect(content.text()).toBe('Choose');
190+
}));
191+
192+
it('shows a basic confirm dialog with HTML content using custom types', inject(function($rootScope, $mdDialog, $animate) {
193+
var parent = angular.element('<div>');
194+
195+
$mdDialog.show(
196+
$mdDialog.confirm({
197+
parent: parent,
198+
ok: 'Next',
199+
cancel: 'Back',
200+
title: 'Which Way ',
201+
content: '<my-content class="mine">Choose</my-content>'
202+
})
203+
);
204+
205+
runAnimation();
206+
207+
var container = angular.element(parent[0].querySelector('.md-dialog-container'));
208+
var content = angular.element(container[0].querySelector('.mine'));
209+
210+
expect(content.text()).toBe('Choose');
211+
}));
212+
172213
it('should focus `md-button.dialog-close` on open', inject(function($mdDialog, $rootScope, $document, $timeout, $mdConstant) {
173214
jasmine.mockElementFocus(this);
174215

175216
var parent = angular.element('<div>');
176217
$mdDialog.show({
177-
template: '<md-dialog>' +
178-
'<div class="md-actions">' +
179-
'<button class="dialog-close">Close</button>' +
180-
'</div>' +
218+
template: '' +
219+
'<md-dialog>' +
220+
' <div class="md-actions">' +
221+
' <button class="dialog-close">Close</button>' +
222+
' </div>' +
181223
'</md-dialog>',
182-
parent: parent,
224+
parent: parent
183225
});
184226
runAnimation();
185227

src/components/tabs/js/tabsDirective.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ function MdTabs () {
149149
ng-disabled="tab.scope.disabled"\
150150
md-swipe-left="$mdTabsCtrl.nextPage()"\
151151
md-swipe-right="$mdTabsCtrl.previousPage()"\
152-
md-template="::tab.label"\
152+
md-tabs-template="::tab.label"\
153153
md-scope="::tab.parent"></md-tab-item>\
154154
<md-ink-bar></md-ink-bar>\
155155
</md-pagination-wrapper>\
@@ -165,7 +165,7 @@ function MdTabs () {
165165
ng-focus="$mdTabsCtrl.hasFocus = true"\
166166
ng-blur="$mdTabsCtrl.hasFocus = false"\
167167
ng-repeat="tab in $mdTabsCtrl.tabs"\
168-
md-template="::tab.label"\
168+
md-tabs-template="::tab.label"\
169169
md-scope="::tab.parent"></md-dummy-tab>\
170170
</div>\
171171
</md-tabs-canvas>\
@@ -187,7 +187,7 @@ function MdTabs () {
187187
\'md-no-scroll\': $mdTabsCtrl.dynamicHeight\
188188
}">\
189189
<div\
190-
md-template="::tab.template"\
190+
md-tabs-template="::tab.template"\
191191
md-connected-if="tab.isActive()"\
192192
md-scope="::tab.parent"\
193193
ng-if="$mdTabsCtrl.enableDisconnect || tab.shouldRender()"></div>\

src/components/tabs/js/templateDirective.js renamed to src/components/tabs/js/tabsTemplateDirective.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
angular
22
.module('material.components.tabs')
3-
.directive('mdTemplate', MdTemplate);
3+
.directive('mdTabsTemplate', MdTabsTemplate);
44

5-
function MdTemplate ($compile, $mdUtil) {
5+
function MdTabsTemplate ($compile, $mdUtil) {
66
return {
77
restrict: 'A',
88
link: link,
99
scope: {
10-
template: '=mdTemplate',
10+
template: '=mdTabsTemplate',
1111
connected: '=?mdConnectedIf',
1212
compileScope: '=mdScope'
1313
},

src/core/core.js

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
/**
32
* Initialization function that validates environment
43
* requirements.
@@ -10,8 +9,8 @@ angular
109
'material.core.gestures',
1110
'material.core.theming'
1211
])
13-
.config( MdCoreConfigure );
14-
12+
.directive('mdTemplate', MdTemplateDirective)
13+
.config(MdCoreConfigure);
1514

1615
function MdCoreConfigure($provide, $mdThemingProvider) {
1716

@@ -24,7 +23,35 @@ function MdCoreConfigure($provide, $mdThemingProvider) {
2423
.backgroundPalette('grey');
2524
}
2625

27-
function rAFDecorator( $delegate ) {
26+
function MdTemplateDirective($compile) {
27+
return {
28+
restrict: 'A',
29+
scope: {
30+
template: '=mdTemplate'
31+
},
32+
link: function postLink(scope, element) {
33+
scope.$watch('template', assignSafeHTML);
34+
35+
/**
36+
* To add safe HTML: assign and compile in
37+
* isolated scope.
38+
*/
39+
function assignSafeHTML(value) {
40+
// when the 'compile' expression changes
41+
// assign it into the current DOM
42+
element.html(value);
43+
44+
// Compile the new DOM and link it to the current scope.
45+
// NOTE: we only compile .childNodes so that we don't get
46+
// into infinite loop compiling ourselves
47+
$compile(element.contents())(scope);
48+
}
49+
}
50+
};
51+
52+
}
53+
54+
function rAFDecorator($delegate) {
2855
/**
2956
* Use this to throttle events that come in often.
3057
* The throttled function will always use the *last* invocation before the

0 commit comments

Comments
 (0)