This repository has been archived by the owner on Nov 25, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
ReactiveQuery.js
163 lines (156 loc) · 5.76 KB
/
ReactiveQuery.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* It could be just a String value, which will be treated as a name of parameter, or an object.
* @typedef {object|string} ReactiveQueryKey
* @property {string} name Key name
* @property {*} [value] Default value
* @property {function} [isValid] Should return `true` or `false`, receives the value as first argument
* @property {function} [onChange] Runs if the value is changed, signature: onChange(oldValue, newValue, key.name)
*/
/**
* @typedef {object} QueryParamObject
* @property {string} name of the query parameter
* @property {string} value serialized URI-encoded value of the query parameter
*/
/**
* ReactiveQuery constructor.
* @param name {string}
* @param keys {(Array.<ReactiveQueryKey>|Object.<string,ReactiveQueryKey>)} array or object of ReactiveQueryKey
* @constructor
*/
ReactiveQuery = function (name, keys) {
if (!name || !_.isString(name)) {
throw new Error("Name of type {string} is required, since it is used as a name for the query param");
}
if (!_.isObject(keys) || !_.keys(keys).length || _.each(keys, ReactiveQueryUtils.validateKeyOrDie)) {
throw new Error("Keys object required");
}
/**
* @private
* {array} normalized array of keys
*/
var entireKeys = ReactiveQueryUtils.normalizeKeys(_.clone(keys));
var self = this;
var currentData = new ReactiveDict();
// set default values if any
_.each(entireKeys, function (key) {
if (typeof key.value !== "undefined") {
currentData.set(key.name, key.value);
}
});
/**
* Get ReactiveQuery name which is used as query param name
* @returns {String} name
*/
this.getName = function () {
return name;
}
/**
* Similar to ReactiveDict, gets the value by the key
* @param key {ReactiveQueryKey} key to get data
* @param [reactive] {boolean} Default true; pass false to disable reactivity
* @throws Error If key is malformed
* @returns {*} any serializable data
*/
this.get = function (key, reactive) {
ReactiveQueryUtils.validateKeyOrDie(key);
var key = _.isObject(key) ? key.name : key;
if (reactive === false) {
return ReactiveQueryUtils.getWithoutReactivity(currentData, key)
}
return currentData.get(key);
}
/**
* Updates the data based on query params, ignores and deletes invalid data (if isValid callback is defined for the
* key). Also removes data that does not exist in params
* Typically it should be used when a url changes.
* @param params {object} query params object like this {name1: value1, name2: value2}, URI-encoded values expected
* The same structure as returned by the `iron-router` method Router.current().params.query
*/
this.setFromParams = function (params) {
var paramEncoded = params[name];
var param = null;
try {
param = _.isString(paramEncoded) ? ReactiveQueryUtils.decodeParam(paramEncoded) : null;
} catch (e) {
console.warn && console.warn("Malformed url param `" + name + "`: ", e);
}
self.setAll(param);
}
/**
* Directly sets data by the key.
* @param key {ReactiveQueryKey} key
* @param value {*} value
*/
this.set = function (key, value) {
var param = {};
param[_.isString(key) ? key : key.name] = value;
ReactiveQueryUtils.forceData(param, currentData, key);
}
/**
* Directly sets data copying all values from the data param by known keys. Values which are not set, will be
* considered as null.
* @param data {object} object with data
*/
this.setAll = function (data) {
entireKeys.forEach(function (key) {
ReactiveQueryUtils.forceData(data, currentData, key);
});
}
/**
* Provides merged data based on the customData. Ignores invalid data (if isValid callback is defined for the key)
* If no customData provided or null, returns current data.
* If value is an object, you can use $unset as an entire key, to delete values from object.
* For example {$unset: {name: 1, surname}, question: "?"} for {name: "A", surname: "B", question: ""}
* will result {question: "?"}
* @param {...*} [arguments] any number of arguments as key value, for example whatIf(key,value,key2,value2)
* @returns {object} Current (possibly modified if customData argument provided) data as a key-value pairs
*/
this.whatIf = function () {
return generateData(parseArgs(arguments), currentData, entireKeys);
}
/**
* Provides merged data based on the customData. Ignores invalid data (if isValid callback is defined for the key)
* If no customData provided or null, returns current data as {QueryParamObject}.
* If value is an object, you can use $unset as an entire key, to delete values from object.
* For example {$unset: {name: 1, surname}, question: "?"} for {name: "A", surname: "B", question: ""}
* will result {question: "?"}
* @param {...*} [arguments] any number of arguments as key value, for example whatIfAsQueryParam(key,value,key2,value2)
* @returns {QueryParamObject}
*/
this.whatIfAsQueryParam = function () {
var data = generateData(parseArgs(arguments), currentData, entireKeys);
return {
name: name,
value: ReactiveQueryUtils.encodeParam(data)
}
}
}
function parseArgs(args) {
if (typeof args === "undefined") {
return;
}
var i = 0, n = args.length, result = {};
for (; i < n; i += 2) {
if (typeof args[i] !== "undefined" && typeof args[i + 1] !== "undefined") {
var key = _.isObject(args[i]) ? args[i].name : args[i];
result[key] = args[i + 1];
}
}
if (Object.keys(result).length) {
return result;
}
}
/**
* @private
* Generate data based on current controller data, merged with custom data, using specified key
* @param customData {CustomControllerData}
* @param keyType {String} type of keys to use. 'normal' or 'uri'
* @returns {Object}
*/
function generateData(customData, currentData, entireKeys) {
var result = {};
entireKeys.forEach(function (key) {
ReactiveQueryUtils.mergeSpecial(customData, currentData, result, key);
});
return result
}