Skip to content

Commit 9520aff

Browse files
committed
Merge pull request #3785 from angular-ui/feature/one-bind
Adds ui-grid-one-bind directive set to core
2 parents e31b759 + 33fe0a2 commit 9520aff

File tree

2 files changed

+521
-0
lines changed

2 files changed

+521
-0
lines changed
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
(function(){
2+
'use strict';
3+
/**
4+
* @ngdoc overview
5+
* @name ui.grid.directive:uiGridOneBind
6+
* @summary A group of directives that provide a one time bind to a dom element.
7+
* @description A group of directives that provide a one time bind to a dom element.
8+
* As one time bindings are not supported in Angular 1.2.* this directive provdes this capability.
9+
* This is done to reduce the number of watchers on the dom.
10+
* <br/>
11+
* <h2>Short Example ({@link ui.grid.directive:uiGridOneBindSrc ui-grid-one-bind-src})</h2>
12+
* <pre>
13+
<div ng-init="imageName = 'myImageDir.jpg'">
14+
<img ui-grid-one-bind-src="imageName"></img>
15+
</div>
16+
</pre>
17+
* Will become:
18+
* <pre>
19+
<div ng-init="imageName = 'myImageDir.jpg'">
20+
<img ui-grid-one-bind-src="imageName" src="myImageDir.jpg"></img>
21+
</div>
22+
</pre>
23+
</br>
24+
<h2>Short Example ({@link ui.grid.directive:uiGridOneBindText ui-grid-one-bind-text})</h2>
25+
* <pre>
26+
<div ng-init="text='Add this text'" ui-grid-one-bind-text="text"></div>
27+
</pre>
28+
* Will become:
29+
* <pre>
30+
<div ng-init="text='Add this text'" ui-grid-one-bind-text="text">Add this text</div>
31+
</pre>
32+
</br>
33+
* <b>Note:</b> This behavior is slightly different for the {@link ui.grid.directive:uiGridOneBindIdGrid uiGridOneBindIdGrid}
34+
* and {@link ui.grid.directive:uiGridOneBindAriaLabelledbyGrid uiGridOneBindAriaLabelledbyGrid} directives.
35+
*
36+
*/
37+
//https://github.com/joshkurz/Black-Belt-AngularJS-Directives/blob/master/directives/Optimization/oneBind.js
38+
var oneBinders = angular.module('ui.grid');
39+
angular.forEach([
40+
/**
41+
* @ngdoc directive
42+
* @name ui.grid.directive:uiGridOneBindSrc
43+
* @memberof ui.grid.directive:uiGridOneBind
44+
* @element img
45+
* @restrict A
46+
* @param {String} uiGridOneBindSrc The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
47+
* @description One time binding for the src dom tag.
48+
*
49+
*/
50+
{tag: 'Src', method: 'attr'},
51+
/**
52+
* @ngdoc directive
53+
* @name ui.grid.directive:uiGridOneBindText
54+
* @element div
55+
* @restrict A
56+
* @param {String} uiGridOneBindText The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
57+
* @description One time binding for the text dom tag.
58+
*/
59+
{tag: 'Text', method: 'text'},
60+
/**
61+
* @ngdoc directive
62+
* @name ui.grid.directive:uiGridOneBindHref
63+
* @element div
64+
* @restrict A
65+
* @param {String} uiGridOneBindHref The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
66+
* @description One time binding for the href dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
67+
*/
68+
{tag: 'Href', method: 'attr'},
69+
/**
70+
* @ngdoc directive
71+
* @name ui.grid.directive:uiGridOneBindClass
72+
* @element div
73+
* @restrict A
74+
* @param {String} uiGridOneBindClass The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
75+
* @param {Object} uiGridOneBindClass The object that you want to bind. At least one of the values in the object must be something other than null or undefined for the watcher to be removed.
76+
* this is to prevent the watcher from being removed before the scope is initialized.
77+
* @param {Array} uiGridOneBindClass An array of classes to bind to this element.
78+
* @description One time binding for the class dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
79+
*/
80+
{tag: 'Class', method: 'addClass'},
81+
/**
82+
* @ngdoc directive
83+
* @name ui.grid.directive:uiGridOneBindHtml
84+
* @element div
85+
* @restrict A
86+
* @param {String} uiGridOneBindHtml The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
87+
* @description One time binding for the html method on a dom element. For more information see {@link ui.grid.directive:uiGridOneBind}.
88+
*/
89+
{tag: 'Html', method: 'html'},
90+
/**
91+
* @ngdoc directive
92+
* @name ui.grid.directive:uiGridOneBindAlt
93+
* @element div
94+
* @restrict A
95+
* @param {String} uiGridOneBindAlt The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
96+
* @description One time binding for the alt dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
97+
*/
98+
{tag: 'Alt', method: 'attr'},
99+
/**
100+
* @ngdoc directive
101+
* @name ui.grid.directive:uiGridOneBindStyle
102+
* @element div
103+
* @restrict A
104+
* @param {String} uiGridOneBindStyle The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
105+
* @description One time binding for the style dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
106+
*/
107+
{tag: 'Style', method: 'css'},
108+
/**
109+
* @ngdoc directive
110+
* @name ui.grid.directive:uiGridOneBindValue
111+
* @element div
112+
* @restrict A
113+
* @param {String} uiGridOneBindValue The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
114+
* @description One time binding for the value dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
115+
*/
116+
{tag: 'Value', method: 'attr'},
117+
/**
118+
* @ngdoc directive
119+
* @name ui.grid.directive:uiGridOneBindId
120+
* @element div
121+
* @restrict A
122+
* @param {String} uiGridOneBindId The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
123+
* @description One time binding for the value dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
124+
*/
125+
{tag: 'Id', method: 'attr'},
126+
/**
127+
* @ngdoc directive
128+
* @name ui.grid.directive:uiGridOneBindIdGrid
129+
* @element div
130+
* @restrict A
131+
* @param {String} uiGridOneBindId The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
132+
* @description One time binding for the id dom tag.
133+
* <h1>Important Note!</h1>
134+
* If the id tag passed as a parameter does <b>not</b> contain the grid id as a substring
135+
* then the directive will search the scope and the parent controller (if it is a uiGridController) for the grid.id value.
136+
* If this value is found then it is appended to the begining of the id tag. If the grid is not found then the directive throws an error.
137+
* This is done in order to ensure uniqueness of id tags across the grid.
138+
* This is to prevent two grids in the same document having duplicate id tags.
139+
*/
140+
{tag: 'Id', directiveName:'IdGrid', method: 'attr', appendGridId: true},
141+
/**
142+
* @ngdoc directive
143+
* @name ui.grid.directive:uiGridOneBindTitle
144+
* @element div
145+
* @restrict A
146+
* @param {String} uiGridOneBindTitle The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
147+
* @description One time binding for the title dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
148+
*/
149+
{tag: 'Title', method: 'attr'},
150+
/**
151+
* @ngdoc directive
152+
* @name ui.grid.directive:uiGridOneBindAriaLabel
153+
* @element div
154+
* @restrict A
155+
* @param {String} uiGridOneBindAriaLabel The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
156+
* @description One time binding for the aria-label dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
157+
*<br/>
158+
* <pre>
159+
<div ng-init="text='Add this text'" ui-grid-one-bind-aria-label="text"></div>
160+
</pre>
161+
* Will become:
162+
* <pre>
163+
<div ng-init="text='Add this text'" ui-grid-one-bind-aria-label="text" aria-label="Add this text"></div>
164+
</pre>
165+
*/
166+
{tag: 'Label', method: 'attr', aria:true},
167+
/**
168+
* @ngdoc directive
169+
* @name ui.grid.directive:uiGridOneBindAriaLabelledby
170+
* @element div
171+
* @restrict A
172+
* @param {String} uiGridOneBindAriaLabelledby The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
173+
* @description One time binding for the aria-labelledby dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
174+
*<br/>
175+
* <pre>
176+
<div ng-init="anId = 'gridID32'" ui-grid-one-bind-aria-labelledby="anId"></div>
177+
</pre>
178+
* Will become:
179+
* <pre>
180+
<div ng-init="anId = 'gridID32'" ui-grid-one-bind-aria-labelledby="anId" aria-labelledby="gridID32"></div>
181+
</pre>
182+
*/
183+
{tag: 'Labelledby', method: 'attr', aria:true},
184+
/**
185+
* @ngdoc directive
186+
* @name ui.grid.directive:uiGridOneBindAriaLabelledbyGrid
187+
* @element div
188+
* @restrict A
189+
* @param {String} uiGridOneBindAriaLabelledbyGrid The angular string you want to bind. Does not support interpolation. Don't use <code>{{scopeElt}}</code> instead use <code>scopeElt</code>.
190+
* @description One time binding for the aria-labelledby dom tag. For more information see {@link ui.grid.directive:uiGridOneBind}.
191+
* Works somewhat like {@link ui.grid.directive:uiGridOneBindIdGrid} however this one supports a list of ids (seperated by a space) and will dynamically add the
192+
* grid id to each one.
193+
*<br/>
194+
* <pre>
195+
<div ng-init="anId = 'gridID32'" ui-grid-one-bind-aria-labelledby-grid="anId"></div>
196+
</pre>
197+
* Will become ([grid.id] will be replaced by the actual grid id):
198+
* <pre>
199+
<div ng-init="anId = 'gridID32'" ui-grid-one-bind-aria-labelledby-grid="anId" aria-labelledby-Grid="[grid.id]-gridID32"></div>
200+
</pre>
201+
*/
202+
{tag: 'Labelledby', directiveName:'LabelledbyGrid', appendGridId:true, method: 'attr', aria:true}],
203+
function(v){
204+
205+
var baseDirectiveName = 'uiGridOneBind';
206+
//If it is an aria tag then append the aria label seperately
207+
//This is done because the aria tags are formatted aria-* and the directive name can't have a '-' character in it.
208+
//If the diretiveName has to be overridden then it does so here. This is because the tag being modified and the directive sometimes don't match up.
209+
var directiveName = (v.aria ? baseDirectiveName + 'Aria' : baseDirectiveName) + (v.directiveName ? v.directiveName : v.tag);
210+
oneBinders.directive(directiveName, ['gridUtil', function(gridUtil){
211+
return {
212+
restrict: 'A',
213+
require: ['?uiGrid','?^uiGrid'],
214+
link: function(scope, iElement, iAttrs, controllers){
215+
/* Appends the grid id to the beginnig of the value. */
216+
var appendGridId = function(val){
217+
var grid; //Get an instance of the grid if its available
218+
//If its available in the scope then we don't need to try to find it elsewhere
219+
if (scope.grid) {
220+
grid = scope.grid;
221+
}
222+
//Another possible location to try to find the grid
223+
else if (scope.col && scope.col.grid){
224+
grid = scope.col.grid;
225+
}
226+
//Last ditch effort: Search through the provided controllers.
227+
else if (!controllers.some( //Go through the controllers till one has the element we need
228+
function(controller){
229+
if (controller && controller.grid) {
230+
grid = controller.grid;
231+
return true; //We've found the grid
232+
}
233+
})){
234+
//We tried our best to find it for you
235+
gridUtil.logError("["+directiveName+"] A valid grid could not be found to bind id. Are you using this directive " +
236+
"within the correct scope? Trying to generate id: [gridID]-" + val);
237+
throw new Error("No valid grid could be found");
238+
}
239+
240+
if (grid){
241+
var idRegex = new RegExp(grid.id.toString());
242+
//If the grid id hasn't been appended already in the template declaration
243+
if (!idRegex.test(val)){
244+
val = grid.id.toString() + '-' + val;
245+
}
246+
}
247+
return val;
248+
};
249+
250+
// The watch returns a function to remove itself.
251+
var rmWatcher = scope.$watch(iAttrs[directiveName], function(newV){
252+
if (newV){
253+
//If we are trying to add an id element then we also apply the grid id if it isn't already there
254+
if (v.appendGridId) {
255+
var newIdString = null;
256+
//Append the id to all of the new ids.
257+
angular.forEach( newV.split(' '), function(s){
258+
newIdString = (newIdString ? (newIdString + ' ') : '') + appendGridId(s);
259+
});
260+
newV = newIdString;
261+
}
262+
263+
// Append this newValue to the dom element.
264+
switch (v.method) {
265+
case 'attr': //The attr method takes two paraams the tag and the value
266+
if (v.aria) {
267+
//If it is an aria element then append the aria prefix
268+
iElement[v.method]('aria-' + v.tag.toLowerCase(),newV);
269+
} else {
270+
iElement[v.method](v.tag.toLowerCase(),newV);
271+
}
272+
break;
273+
case 'addClass':
274+
//Pulled from https://github.com/Pasvaz/bindonce/blob/master/bindonce.js
275+
if (angular.isObject(newV) && !angular.isArray(newV)) {
276+
var results = [];
277+
var nonNullFound = false; //We don't want to remove the binding unless the key is actually defined
278+
angular.forEach(newV, function (value, index) {
279+
if (value !== null && typeof(value) !== "undefined"){
280+
nonNullFound = true; //A non null value for a key was found so the object must have been initialized
281+
if (value) {results.push(index);}
282+
}
283+
});
284+
//A non null value for a key wasn't found so assume that the scope values haven't been fully initialized
285+
if (!nonNullFound){
286+
return; // If not initialized then the watcher should not be removed yet.
287+
}
288+
newV = results;
289+
}
290+
291+
if (newV) {
292+
iElement.addClass(angular.isArray(newV) ? newV.join(' ') : newV);
293+
} else {
294+
return;
295+
}
296+
break;
297+
default:
298+
iElement[v.method](newV);
299+
break;
300+
}
301+
302+
//Removes the watcher on itself after the bind
303+
rmWatcher();
304+
}
305+
}); //End rm watchers
306+
} //End compile function
307+
}; //End directive return
308+
} // End directive function
309+
]); //End directive
310+
}); // End angular foreach
311+
})();

0 commit comments

Comments
 (0)