Skip to content

Commit 6893e46

Browse files
committed
Add helper add ons
1 parent a894ca1 commit 6893e46

2 files changed

Lines changed: 211 additions & 1 deletion

File tree

README.md

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ Wire-Up JavaScript
7272
7373
<!-- Wire-up the Symfony Form Angular Controller -->
7474
<script src="{{ asset('bundles/curiosity26angularmaterial/js/mdform.js') }}"></script>
75+
76+
<!-- Wire-up the Symfony Angular Addons -->
77+
<script src="{{ asset('bundles/curiosity26angularmaterial/js/mdform.js') }}"></script>
7578
7679
// ... The rest of your scripts here
7780
{% endblock %}
@@ -126,4 +129,108 @@ You can connect you Angular app in the base template or any extended templates.
126129
<!-- ... -->
127130
</body>
128131
</html>
129-
```
132+
```
133+
134+
Material Icons
135+
--------------
136+
137+
You can use Assetic to load Material Icons from the CDN.
138+
139+
```yaml
140+
assetic:
141+
...
142+
assets:
143+
material_icons:
144+
inputs:
145+
- https://fonts.googleapis.com/icon?family=Material+Icons
146+
````
147+
148+
```twig
149+
{% stylesheets output="style.min.css" "@material_icons" %}
150+
<link rel="stylesheet" href="{{ asset_url }}">
151+
{% endstylesheets %}
152+
````
153+
154+
Bundled Addons
155+
--------------
156+
157+
As I used Angular Material inside Symfony, I decided to make some helper directives to help with things like Toast Alerts and Dialogs.
158+
159+
## sf-alert
160+
161+
The `sf-alert` directive is an **element directive** to display toast alerts. Alerts are automatically displayed when the page load completes.
162+
163+
```HTML
164+
<sf-alert sf-alert-action="Yea" sf-alert-parent="#wrapper">This is the message in the alert</sf-alert>
165+
```
166+
167+
### Attributes
168+
169+
| Attribute | Definition |
170+
--
171+
| sf-alert-action | The text as it appears on the action button |
172+
| sf-alert-position | The position of the alert using any combination of 'top, right, bottom, left, end, start' |
173+
| sf-alert-auto-wrap | Automatically wrap the contents of the Toast message. *Defaults to true* |
174+
| sf-alert-capsule | Adds `md-capsule` to the Toast |
175+
| sf-alert-hide-delay | The number of milliseconds the message displays before hiding the message. 0 disables auto-hiding and is the default value. |
176+
| sf-alert-parent | The CSS selector of the element the Toast message will align to. Best to use an ID here, as the first item found will be used. |
177+
178+
## sf-dialog-link
179+
180+
The `sf-dialog` directive can be used as an **attribute** or a **class**. I've found this directive to be very useful.
181+
182+
When the directive is applied to an existing `a` or `md-button` element, the URL in the `href` or `ng-href` attribute are loaded
183+
and rendered using the `$mdDialog` service.
184+
185+
The `$http` service is used to make the request call and load the response. The `sf-dialog` directive overrides the
186+
`$http` service to pass the request header `X-Requested-With: XMLHttpRequest` in order to allow smarty to use
187+
`{% app.request.isXmlHttpRequest() %}`.
188+
189+
```twig
190+
{# homepage.html.twig #}
191+
<md-button ng-href="{{ path('add_object') }}" sf-dialog-link>Open Dialog</md-button>
192+
```
193+
194+
```twig
195+
{# add_object.html.twig #}
196+
{% extends app.request.isXmlHttpRequest() ? '::dialog.html.twig' : '::base.html.twig' %}
197+
198+
{% block title %}
199+
Page Title
200+
{% endblock %}
201+
202+
{% block body %}
203+
...
204+
{% endblock %}
205+
```
206+
207+
```twig
208+
{# dialog.html.twig #}
209+
210+
<md-toolbar class="md-primary">
211+
<div class="md-toolbar-tools">
212+
<h2>{% block title %}{% endblock %}</h2>
213+
<span flex></span>
214+
<md-button ng-click="dialog.closeDialog()" class="md-icon-button" aria-label="Close">
215+
<md-icon>close</md-icon>
216+
</md-button>
217+
</div>
218+
</md-toolbar>
219+
{% block body %}{% endblock %}
220+
```
221+
222+
### Attributes
223+
224+
| Attribute | Definition |
225+
--
226+
| href, ng-href | The URL to load in the dialog |
227+
| sf-dialog-method | The HTTP Method used for the request |
228+
| sf-dialog-data | Sets the `data` attribute for the `$http` service |
229+
| sf-dialog-controller | Set the controller to be used for the dialog. A default controller is used which provides a method `closeDialog` that should be bound to a close button. The controller, default or custom, is aliased as 'dialog'. |
230+
| sf-dialog-success | A callback triggered when `$mdDialog.hide()` is called. |
231+
| sf-dialog-cancel | A callback triggered when `$mdDialog.cancel()` is called. |
232+
| sf-dialog-error | A callback triggered when an error is reported the HTTP response with the returned `$err` object as the only parameter. Default behavior shows a Toast with the error message. |
233+
| sf-dialog-fullscreen | An option to toggle whether the dialog should show in fullscreen or not. *Defaults to false.* |
234+
| sf-dialog-has-backdrop | Whether there should be an opaque backdrop behind the dialog. *Default true.* |
235+
| sf-dialog-click-outside-to-cancel | Whether the user can click outside the dialog to close it. *Default false.* |
236+
| sf-dialog-disable-parent-scroll | Whether to disable scrolling while the dialog is open. *Default true.* |

Resources/public/js/mdaddons.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/**
2+
* Created by alexboyce on 9/24/16.
3+
*/
4+
5+
angular.module('symfony.mdAddons', ['ngMaterial'])
6+
.directive('sfAlert', ['$mdToast', '$document', function($mdToast, $document) {
7+
return {
8+
restrict: 'E',
9+
link: function(scope, element, attr) {
10+
element.addClass('ng-hide');
11+
12+
var toast = $mdToast.simple();
13+
toast.action(attr['sfAlertAction'] || 'OK');
14+
toast.position(attr['sftAlertPosition'] || 'top right');
15+
toast.capsule(attr['sfAlertCapsule']);
16+
toast.hideDelay(attr['sfAlertHideDelay'] || 0);
17+
toast.autoWrap(attr['sfAlertAutoWrap'] == undefined ? true : attr['sfAlertAutoWrap']);
18+
toast.parent(!!attr['sfAlertParent'] ? $document[0].querySelector(attr['sfAlertParent'])[0] : $document[0].body);
19+
toast.textContent(element.text());
20+
// Doing the following safely injects the HTML without opening up to XSS vulnerabilities
21+
toast._options.template ='<md-toast md-theme="{{ toast.theme }}" ng-class="{\'md-capsule\': toast.capsule}">' +
22+
' <div class="md-toast-content">' +
23+
' <span flex role="alert" aria-relevant="all" aria-atomic="true">' +
24+
' ' + element.html() +
25+
' </span>' +
26+
' <md-button class="md-action" ng-if="toast.action" ng-click="toast.resolve()" ng-class="{\'md-highlight\': toast.highlightAction}">' +
27+
' {{ toast.action }}' +
28+
' </md-button>' +
29+
' </div>' +
30+
'</md-toast>';
31+
32+
$mdToast.show(toast);
33+
}
34+
};
35+
}])
36+
.directive('sfDialogLink', [function() {
37+
return {
38+
restrict: 'AC',
39+
controller: ['$scope', '$mdDialog', '$mdToast', '$element', '$http',
40+
function($scope, $mdDialog, $mdToast, $element, $http) {
41+
var href = $element.attr('href') || $element.attr('ng-href');
42+
var method = $element.attr('sfDialogMethod') || 'GET';
43+
var data = $element.attr('sfDialogData') || false;
44+
var controller = $element.attr('sfDialogController') || function($scope, $mdDialog) {
45+
this.closeDialog = function() {
46+
$mdDialog.cancel();
47+
}
48+
};
49+
var fullscreen = $element.attr('sfDialogFullscreen') || false;
50+
var hasBackdrop = $element.attr('sfDialogHasBackdrop') != false;
51+
var escToClose = $element.attr('sfDialogEscapeToClose') != false;
52+
var clickOutsideToClose = $element.attr('sfDialogClickOutsideToClose') || false;
53+
var disableParentScroll = $element.attr('sfDisableParentScroll') != false;
54+
var success = $element.attr('sfDialogSuccess') || false;
55+
var cancel = $element.attr('sfDialogCancel') || false;
56+
var error = $element.attr('sfDialogError') || false;
57+
var options, dialog;
58+
59+
$element.on('click', showDialog);
60+
61+
function showDialog($event) {
62+
if (!!href) {
63+
$event.preventDefault();
64+
$http({
65+
method: method,
66+
url: href,
67+
data: data,
68+
headers: {
69+
'X-Requested-With': 'XMLHttpRequest'
70+
}
71+
}).then(function (response) {
72+
options = {
73+
clickOutsideToClose: clickOutsideToClose,
74+
template: '<md-dialog flex-xs="90" flex-gt-xs="70" flex-gt-md="50">' +
75+
' <md-dialog-content>' +
76+
' ' + response.data +
77+
' </md-dialog-content>' +
78+
'</md-dialog>',
79+
scope: $scope,
80+
event: $event,
81+
controller: controller,
82+
controllerAs: 'dialog',
83+
preserveScope: true,
84+
fullscreen: fullscreen,
85+
hasBackdrop: hasBackdrop,
86+
escapeToClose: escToClose,
87+
disableParentScroll: disableParentScroll
88+
};
89+
90+
$mdDialog.show(options).then(success, cancel);
91+
}, function ($err) {
92+
if (!!error) {
93+
error($err);
94+
}
95+
else {
96+
$mdToast.show($mdToast.simple().textContent($err.getMessage()));
97+
}
98+
});
99+
}
100+
}
101+
}]
102+
};
103+
}]);

0 commit comments

Comments
 (0)