Commit | Line | Data |
---|---|---|
685acae4 | 1 | /// crmUi: Sundry UI helpers |
2 | (function (angular, $, _) { | |
438f2b52 | 3 | var idCount = 0; |
685acae4 | 4 | |
5 | angular.module('crmUi', []) | |
438f2b52 TO |
6 | // example: <form name="myForm">...<label crm-ui-label crm-for="myField">My Field</span>...<input name="myField"/>...</form> |
7 | // | |
8 | // Label adapts based on <input required>, <input ng-required>, or any other validation. | |
9 | // | |
10 | // Note: This should work in the normal case where <label> and <input> are in roughly the same scope, | |
11 | // but if the scopes are materially different then problems could arise. | |
12 | .directive('crmUiLabel', function($parse) { | |
13 | return { | |
14 | scope: { | |
15 | name: '@' | |
16 | }, | |
17 | transclude: true, | |
18 | template: '<span ng-class="cssClasses"><span ng-transclude></span> <span ng-show="crmRequired" class="crm-marker" title="This field is required.">*</span></span>', | |
19 | link: function(scope, element, attrs) { | |
20 | if (attrs.crmFor == 'name') { | |
21 | throw new Error('Validation monitoring does not work for field name "name"'); | |
22 | } | |
23 | ||
24 | // 1. Figure out form and input elements | |
25 | ||
26 | var form = $(element).closest('form'); | |
27 | var formCtrl = scope.$parent.$eval(form.attr('name')); | |
28 | var input = $('input[name="' + attrs.crmFor + '"],select[name="' + attrs.crmFor + '"],textarea[name="' + attrs.crmFor + '"]', form); | |
29 | if (form.length != 1 || input.length != 1) { | |
30 | if (console.log) console.log('Label cannot be matched to input element. Expected to find one form and one input.', form.length, input.length); | |
31 | return; | |
32 | } | |
33 | ||
34 | // 2. Make sure that inputs are well-defined (with name+id). | |
35 | ||
36 | if (!input.attr('id')) { | |
37 | input.attr('id', 'crmUi_' + (++idCount)); | |
38 | } | |
39 | $(element).attr('for', input.attr('id')); | |
40 | ||
41 | // 3. Monitor is the "required" and "$valid" properties | |
42 | ||
43 | if (input.attr('ng-required')) { | |
44 | scope.crmRequired = scope.$parent.$eval(input.attr('ng-required')); | |
45 | scope.$parent.$watch(input.attr('ng-required'), function(isRequired) { | |
46 | scope.crmRequired = isRequired; | |
47 | }); | |
48 | } else { | |
49 | scope.crmRequired = input.prop('required'); | |
50 | } | |
51 | ||
52 | var inputCtrl = form.attr('name') + '.' + input.attr('name'); | |
53 | scope.cssClasses = {}; | |
54 | scope.$parent.$watch(inputCtrl + '.$valid', function(newValue) { | |
55 | //scope.cssClasses['ng-valid'] = newValue; | |
56 | //scope.cssClasses['ng-invalid'] = !newValue; | |
57 | scope.cssClasses['crm-error'] = !scope.$parent.$eval(inputCtrl + '.$valid') && !scope.$parent.$eval(inputCtrl + '.$pristine'); | |
58 | }); | |
59 | scope.$parent.$watch(inputCtrl + '.$pristine', function(newValue) { | |
60 | //scope.cssClasses['ng-pristine'] = newValue; | |
61 | //scope.cssClasses['ng-dirty'] = !newValue; | |
62 | scope.cssClasses['crm-error'] = !scope.$parent.$eval(inputCtrl + '.$valid') && !scope.$parent.$eval(inputCtrl + '.$pristine'); | |
63 | }); | |
64 | ||
65 | } | |
66 | }; | |
67 | }) | |
685acae4 | 68 | |
69 | // example: <a crm-ui-lock binding="mymodel.boolfield"></a> | |
70 | // example: <a crm-ui-lock | |
71 | // binding="mymodel.boolfield" | |
72 | // title-locked="ts('Boolfield is locked')" | |
73 | // title-unlocked="ts('Boolfield is unlocked')"></a> | |
74 | .directive('crmUiLock', function ($parse, $rootScope) { | |
75 | var defaultVal = function (defaultValue) { | |
76 | var f = function (scope) { | |
77 | return defaultValue; | |
78 | } | |
79 | f.assign = function (scope, value) { | |
80 | // ignore changes | |
81 | } | |
82 | return f; | |
83 | }; | |
84 | ||
85 | // like $parse, but accepts a defaultValue in case expr is undefined | |
86 | var parse = function (expr, defaultValue) { | |
87 | return expr ? $parse(expr) : defaultVal(defaultValue); | |
88 | }; | |
89 | ||
90 | return { | |
91 | template: '', | |
92 | link: function (scope, element, attrs) { | |
93 | var binding = parse(attrs['binding'], true); | |
94 | var titleLocked = parse(attrs['titleLocked'], ts('Locked')); | |
95 | var titleUnlocked = parse(attrs['titleUnlocked'], ts('Unlocked')); | |
96 | ||
97 | $(element).addClass('ui-icon lock-button'); | |
98 | var refresh = function () { | |
99 | var locked = binding(scope); | |
100 | if (locked) { | |
101 | $(element) | |
102 | .removeClass('ui-icon-unlocked') | |
103 | .addClass('ui-icon-locked') | |
104 | .prop('title', titleLocked(scope)) | |
105 | ; | |
106 | } | |
107 | else { | |
108 | $(element) | |
109 | .removeClass('ui-icon-locked') | |
110 | .addClass('ui-icon-unlocked') | |
111 | .prop('title', titleUnlocked(scope)) | |
112 | ; | |
113 | } | |
114 | }; | |
115 | ||
116 | $(element).click(function () { | |
117 | binding.assign(scope, !binding(scope)); | |
118 | //scope.$digest(); | |
119 | $rootScope.$digest(); | |
120 | }); | |
121 | ||
122 | scope.$watch(attrs.binding, refresh); | |
123 | scope.$watch(attrs.titleLocked, refresh); | |
124 | scope.$watch(attrs.titleUnlocked, refresh); | |
125 | ||
126 | refresh(); | |
127 | } | |
128 | }; | |
129 | }) | |
02308c07 TO |
130 | .run(function($rootScope, $location) { |
131 | /// Example: <button ng-click="goto('home')">Go home!</button> | |
132 | $rootScope.goto = function(path) { | |
133 | $location.path(path); | |
134 | }; | |
135 | }) | |
685acae4 | 136 | ; |
137 | ||
138 | })(angular, CRM.$, CRM._); |