1 | # lodash-decorators
|
2 |
|
3 | Decorators using lodash functions. View the [API docs](https://steelsojka.github.io/lodash-decorators) for more in depth documentation.
|
4 |
|
5 | [![Build Status](https://travis-ci.org/steelsojka/lodash-decorators.svg)](https://travis-ci.org/steelsojka/lodash-decorators)
|
6 | [![npm version](https://badge.fury.io/js/lodash-decorators.svg)](http://badge.fury.io/js/lodash-decorators)
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | - [Install](#install)
|
13 | - [Polyfills](#polyfills)
|
14 | - [Usage](#usage)
|
15 | - [Decorators](#decorators)
|
16 | - [Example](#example)
|
17 | - [Optional Params and Casing](#optional-params-and-casing)
|
18 | - [Example](#example)
|
19 | - [Partials](#partials)
|
20 | - [Example](#example-1)
|
21 | - [Composition](#composition)
|
22 | - [Example](#example-2)
|
23 | - [Instance Decorators](#instance-decorators)
|
24 | - [Mixin](#mixin)
|
25 | - [Example](#example-3)
|
26 | - [Attempt](#attempt)
|
27 | - [Example](#example-4)
|
28 | - [Bind](#bind)
|
29 | - [Example](#example-5)
|
30 | - [Example](#example-6)
|
31 | - [v4 Breaking Changes](#v4-breaking-changes)
|
32 | - [Not all decorators can be applied to or forced on getters/setters.](#not-all-decorators-can-be-applied-to-or-forced-on-getterssetters)
|
33 | - [No longer force instance decorator onto prototype](#no-longer-force-instance-decorator-onto-prototype)
|
34 | - [All decorators now take arguments](#all-decorators-now-take-arguments)
|
35 | - [Removal of extensions and validation package](#removal-of-extensions-and-validation-package)
|
36 | - [Prototype decorator order no longer throws an error](#prototype-decorator-order-no-longer-throws-an-error)
|
37 | - [Other breaking changes](#other-breaking-changes)
|
38 | - [v4 Improvements](#v4-improvements)
|
39 |
|
40 |
|
41 |
|
42 | ## Install
|
43 |
|
44 | `npm install --save lodash lodash-decorators`
|
45 |
|
46 | ### Polyfills
|
47 |
|
48 | This library requires `Map` and `WeakMap` to be available globally. If `Map` or `WeakMap` is not supported in your environment then use a polyfill.
|
49 |
|
50 | ## Usage
|
51 |
|
52 | For more in depth documentation please visit [Lodash](http://lodash.com/docs)
|
53 |
|
54 | Decorators are exported as both start case and lower case.
|
55 |
|
56 | `import { Debounce } from 'lodash-decorators';`
|
57 |
|
58 | is the same as
|
59 |
|
60 | `import { debounce } from 'lodash-decorators';`
|
61 |
|
62 | They can also be imported directly.
|
63 |
|
64 | `import Debounce from 'lodash-decorators/debounce';`
|
65 |
|
66 | ### Decorators
|
67 |
|
68 | These decorators are included in the package. These are also exported as lowercase for those who prefer lowercase decorators.
|
69 |
|
70 | - `After`
|
71 | - `AfterAll`
|
72 | - `Ary`
|
73 | - `Attempt`
|
74 | - `Before`
|
75 | - `BeforeAll`
|
76 | - `Bind`
|
77 | - `BindAll`
|
78 | - `Curry`
|
79 | - `CurryAll`
|
80 | - `CurryRight`
|
81 | - `CurryRightAll`
|
82 | - `Debounce`
|
83 | - `DebounceAll`
|
84 | - `Defer`
|
85 | - `Delay`
|
86 | - `Flip`
|
87 | - `Flow`
|
88 | - `FlowRight`
|
89 | - `Memoize`
|
90 | - `MemoizeAll`
|
91 | - `Mixin`
|
92 | - `Negate`
|
93 | - `Once`
|
94 | - `OnceAll`
|
95 | - `OverArgs`
|
96 | - `Partial`
|
97 | - `PartialRight`
|
98 | - `Rearg`
|
99 | - `Rest`
|
100 | - `Spread`
|
101 | - `Tap`
|
102 | - `Throttle`
|
103 | - `ThrottleAll`
|
104 | - `ThrottleGetter`
|
105 | - `ThrottleSetter`
|
106 | - `Unary`
|
107 | - `Wrap`
|
108 |
|
109 | #### Example
|
110 |
|
111 | ```javascript
|
112 | import { Debounce, Memoize } from 'lodash-decorators';
|
113 |
|
114 | class Person {
|
115 | constructor(firstName, lastName) {
|
116 | this.firstName = firstName;
|
117 | this.lastName = lastName;
|
118 | }
|
119 |
|
120 | @Debounce(100)
|
121 | save(date) {
|
122 | return this.httpService.post(data);
|
123 | }
|
124 |
|
125 | @Memoize(item => item.id)
|
126 | doSomeHeavyProcessing(arg1, arg2) {}
|
127 | }
|
128 | ```
|
129 |
|
130 | ### Optional Params and Casing
|
131 |
|
132 | If a decorator does not require params or has optional params then the decorator does not require invocation.
|
133 | Decorators are also exported in lower case as well as start case.
|
134 |
|
135 | #### Example
|
136 |
|
137 | ```javascript
|
138 | // These are both valid decorator usages.
|
139 | class Person {
|
140 | @Memoize()
|
141 | doSomething() {}
|
142 |
|
143 | @Memoize
|
144 | doSomething2() {}
|
145 |
|
146 | @memoize()
|
147 | doSomething3() {}
|
148 |
|
149 | @memoize
|
150 | doSomething4() {}
|
151 | }
|
152 | ```
|
153 |
|
154 | ### Partials
|
155 |
|
156 | Some decorators work slightly differently than you would expect
|
157 | them to work than lodash.
|
158 |
|
159 | - `Partial`
|
160 | - `PartialRight`
|
161 | - `Wrap`
|
162 |
|
163 | These can take a `Function` as their first argument or a `String`.
|
164 | If the argument is a `String` then a `Function` is resolved from
|
165 | the current object.
|
166 |
|
167 | #### Example
|
168 |
|
169 | ```javascript
|
170 | import { Partial, Wrap } from 'lodash-decorators'
|
171 |
|
172 | class Person {
|
173 | constructor(firstName, lastName) {
|
174 | this.firstName = firstName;
|
175 | this.lastName = lastName;
|
176 | }
|
177 |
|
178 | getName(type) {
|
179 | return type === 'firstName' ? this.firstName : this.lastName
|
180 | }
|
181 |
|
182 | @Partial('getName', 'firstName')
|
183 | getFirstName() {}
|
184 |
|
185 | @Partial('getName', null)
|
186 | getLastName() {}
|
187 |
|
188 | @Wrap('getName')
|
189 | getUpperCaseName(fn) {
|
190 | return fn().toUpperCase();
|
191 | }
|
192 | }
|
193 |
|
194 | const person = new Person('Joe', 'Smith');
|
195 |
|
196 | person.getFirstName(); // 'Joe'
|
197 | person.getLastName(); // 'Smith'
|
198 | person.getUpperCaseName(); // JOE SMITH
|
199 | ```
|
200 |
|
201 | ### Composition
|
202 |
|
203 | You can use methods like `compose` and `flow` similiar to
|
204 | partials. The arguments are resolved the same way partials
|
205 | are resolved.
|
206 |
|
207 | #### Example
|
208 |
|
209 | ```javascript
|
210 | import { Flow } from 'lodash-decorators'
|
211 | import { kebabCase } from 'lodash';
|
212 |
|
213 | class Person {
|
214 | @Flow('getName', kebabCase)
|
215 | logName;
|
216 |
|
217 | constructor(firstName, lastName) {
|
218 | this.firstName = firstName;
|
219 | this.lastName = lastName;
|
220 | }
|
221 |
|
222 | getName() {
|
223 | return `${this.firstName} ${this.lastName}`;
|
224 | }
|
225 | }
|
226 |
|
227 | const person = new Person('Joe', 'Smith');
|
228 |
|
229 | person.logName(); // joe-smith
|
230 | ```
|
231 |
|
232 | ### Instance Decorators
|
233 |
|
234 | Normally decorators are applied to the prototype method
|
235 | of the class you are working with, but with some of these
|
236 | decorators that is not the desired behavour. These
|
237 | decorators are applied at the instance level.
|
238 |
|
239 | - `Debounce`
|
240 | - `Throttle`
|
241 | - `Memoize`
|
242 | - `After`
|
243 | - `Before`
|
244 | - `Curry`
|
245 | - `CurryRight`
|
246 | - `Once`
|
247 | - `Flow`
|
248 | - `FlowRight`
|
249 | - `Rearg`
|
250 | - `Negate`
|
251 | - `Flip`
|
252 | - `Bind`
|
253 | - `Partial`
|
254 | - `PartialRight`
|
255 |
|
256 | ### Mixin
|
257 |
|
258 | You can mixin methods into a class by using the `Mixin` decorator.
|
259 |
|
260 | #### Example
|
261 |
|
262 | ```javascript
|
263 | import { Mixin } from 'lodash-decorators';
|
264 |
|
265 | const MyOtherApi = {
|
266 | someCoolMethod() {
|
267 | // Do something cool
|
268 | }
|
269 | };
|
270 |
|
271 | @Mixin(MyOtherApi)
|
272 | class Person {}
|
273 |
|
274 | Person.prototype.someCoolMethod === MyOtherApi.someCoolMethod; // => true
|
275 | ```
|
276 |
|
277 | ### Attempt
|
278 |
|
279 | You can wrap a method in a lodash attempt method.
|
280 |
|
281 | #### Example
|
282 |
|
283 | ```javascript
|
284 | import { Attempt } from 'lodash-decorators';
|
285 |
|
286 | class Person {
|
287 | @Attempt()
|
288 | throwAnError() {
|
289 | throw new Error();
|
290 | }
|
291 |
|
292 | @Attempt()
|
293 | doNotThrowAnError() {
|
294 | return '0_o';
|
295 | }
|
296 | }
|
297 |
|
298 | const person = new Person();
|
299 |
|
300 | let result = person.throwAnError();
|
301 |
|
302 | result instanceof Error; // => true
|
303 |
|
304 | result = person.doNotThrowAnError();
|
305 |
|
306 | result === '0_o'; // => true
|
307 | ```
|
308 |
|
309 | ### Bind
|
310 |
|
311 | Bind takes arguments based on lodash's bind and binds the `Function` to
|
312 | the current instance object.
|
313 |
|
314 | #### Example
|
315 |
|
316 | ```javascript
|
317 | import { Bind } from 'lodash-decorators'
|
318 |
|
319 | class Person {
|
320 | constructor(firstName, lastName) {
|
321 | this.firstName = firstName;
|
322 | this.lastName = lastName;
|
323 | }
|
324 |
|
325 | @Bind()
|
326 | getName() {
|
327 | return `${this.firstName} ${this.lastName}`;
|
328 | }
|
329 |
|
330 | // It can also function as a partial
|
331 | @Bind('Joe')
|
332 | getUpperCaseName(name) {
|
333 | return name.toUpperCase();
|
334 | }
|
335 | }
|
336 |
|
337 | const person = new Person('Joe', 'Smith');
|
338 |
|
339 | person.getName.call(null); // Joe Smith
|
340 | person.getUpperCaseName(); // JOE
|
341 | ```
|
342 |
|
343 | You can also bind entire classes with `bindAll` or `bind`.
|
344 |
|
345 | #### Example
|
346 |
|
347 | ```javascript
|
348 | import { BindAll } from 'lodash-decorators'
|
349 |
|
350 | @BindAll()
|
351 | class Person {
|
352 | constructor(firstName, lastName) {
|
353 | this.firstName = firstName;
|
354 | this.lastName = lastName;
|
355 | }
|
356 |
|
357 | getName() {
|
358 | return `${this.firstName} ${this.lastName}`;
|
359 | }
|
360 | }
|
361 |
|
362 | const person = new Person('Joe', 'Smith');
|
363 |
|
364 | person.getName.call(null); // Joe Smith
|
365 | ```
|
366 |
|
367 | ### v4 Breaking Changes
|
368 |
|
369 | Version 4 is a rewrite of the library and has many breaking changes.
|
370 |
|
371 | #### Not all decorators can be applied to or forced on getters/setters.
|
372 |
|
373 | Only certain decorators make sense to be applied to getters/setters. Before you could specify the target of the decorator like `debounce.set(15)`. This behavior is
|
374 | removed and decorators that make sense to apply to getters/setters are configured to be applied to methods and either the getter or the setter. For example:
|
375 |
|
376 | ```javascript
|
377 | class MyClass {
|
378 | // This only gets applied to the setter as it doesn't make sense to apply it to the getter.
|
379 | @Debounce(1000)
|
380 | get value() {
|
381 | return this._value;
|
382 | }
|
383 |
|
384 | set value(val) {
|
385 | this._value = val;
|
386 | }
|
387 |
|
388 | @Debounce(15)
|
389 | fn() {}
|
390 | }
|
391 | ```
|
392 |
|
393 | This keeps the API cleaner and doesn't require the developer to know how the decorator applies to the descriptor. Some decorators have explicit version that apply to either getters of setters, such as `ThrottleGetter` and `ThrottleSetter`.
|
394 |
|
395 | #### No longer force instance decorator onto prototype
|
396 |
|
397 | There is no longer a `Proto` decorator attached to instance decorators. Most instance decorators now have a counterpart that applies to the prototype instead of the instance. `Debounce.Proto()` is now `DebounceAll()`.
|
398 |
|
399 | #### All decorators now take arguments
|
400 |
|
401 | All decorators now take arguments. So instead of `@Once` you would do `@Once()`. This keeps the API consistent and doesn't require the developer to remember which decorators take arguments.
|
402 |
|
403 | #### Removal of extensions and validation package
|
404 |
|
405 | All extensions like `enumerable` have been removed in favor of [core-decorators](https://github.com/jayphelps/core-decorators.js). There may be some slight over lap like `debounce` and `throttle`. Fair warning, instance decorators may not play nice with other implementations of instance decorators.
|
406 |
|
407 | We want to keep lodash decorators focused specifically on lodash specific functions.
|
408 |
|
409 | #### Prototype decorator order no longer throws an error
|
410 |
|
411 | If a prototype decorator comes after an instance decorator it will be ignored since there is no way to apply it in the chain.
|
412 |
|
413 | #### Other breaking changes
|
414 |
|
415 | - `Attempt` now takes an argument to line up with lodash API.
|
416 | - `Bind` used on a class no longer delegates to `BindAll`. Use `BindAll` instead.
|
417 | - `Curry`, `Partial`, `Flow`, `FlowRight` are now instance decorators.
|
418 |
|
419 | ### v4 Improvements
|
420 |
|
421 | - Ships with TypeScript typings.
|
422 | - Predictable performance.
|
423 | - Improvements to Bind decorator.
|
424 | - Improved API for decorator factory.
|
425 | - More and better unit tests.
|
426 | - Better performance with instance decorators.
|
427 | - Single imports with `import { Debounce } from 'lodash-decorators/debounce'`;
|
428 | - Composition decorators can be used on properties. These will generate the composed function.
|