Skip to content

Commit

Permalink
Allow setFieldValue to accept a dotted path ...
Browse files Browse the repository at this point in the history
e.g. you can do setFieldValue('replicator.1.foo', 'bar') to update the foo field on the second replicator set to bar

This uses the eventual underscore 'set' method from jashkenas/underscore#2961. Once it's merged we can call the method directly and not need to include these files.
  • Loading branch information
jasonvarga committed Jul 23, 2022
1 parent 5ff2732 commit 20a4d90
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 1 deletion.
5 changes: 5 additions & 0 deletions resources/js/bootstrap/globals.js
@@ -1,5 +1,6 @@
import { marked } from 'marked';
import { translate, translateChoice } from '../translations/translator';
import { default as set } from './set';

export function cp_url(url) {
url = Statamic.$config.get('cpUrl') + '/' + url;
Expand Down Expand Up @@ -34,6 +35,10 @@ export function data_get(obj, path, fallback=null) {
return value !== undefined ? value : fallback;
};

export function data_set(obj, path, value) {
set(obj, path.split('.'), value);
}

export function clone(value) {
if (value === undefined) return undefined;

Expand Down
37 changes: 37 additions & 0 deletions resources/js/bootstrap/set.js
@@ -0,0 +1,37 @@
import isObject from 'underscore/modules/isObject.js';
import toPath from 'underscore/modules/_toPath.js';
import contains from 'underscore/modules/contains.js';


var arrayIndex = /^\d+$/;

// Internal function of `set`.
function deepSet(obj, path, value) {
var key = path[0];

if (path.length === 1) {
obj[key] = value;
return;
}

if (!isObject(obj[key])) {
var nextKey = path[1];
obj[key] = arrayIndex.test(nextKey) ? [] : {};
}

return deepSet(obj[key], path.slice(1), value);
}

// Set the value on `path` of `object`.
// If any property in `path` does not exist it will be created.
// Returns mutated object (`obj`).
export default function set(obj, path, value) {
path = toPath(path);

if (!isObject(obj) || !path.length) return obj;
if (contains(path, '__proto__')) throw new Error('Prototype assignment attempted');

deepSet(obj, path, value);

return obj;
}
2 changes: 1 addition & 1 deletion resources/js/components/publish/Container.vue
Expand Up @@ -111,7 +111,7 @@ export default {
mutations: {
setFieldValue(state, payload) {
const { handle, value } = payload;
state.values[handle] = value;
data_set(state.values, handle, value);
},
setValues(state, values) {
state.values = values;
Expand Down

3 comments on commit 20a4d90

@jgonggrijp
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasonvarga If you are very sure, you can override _.toPath in order to enable dotted paths everywhere, no wrappers required.

@jasonvarga
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not override Underscore functions. But maybe I'm misunderstanding what you're suggesting.

@jgonggrijp
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it's up to you whether you want to do it, I don't necessarily recommend it. _.toPath is one of the configurable parts of Underscore, similar to _.iteratee, _.templateSettings and _.partial.placeholder. Apart from providing consistency, it exists so that users can adjust the way Underscore interprets property paths to their needs.

Your hesitation is justified, because it will also change the way Underscore works for other dependents. I'm just pointing out that the option is there, and if you want to have dotted string paths everywhere, overriding _.toPath is definitely the most efficient way to achieve that.

Please sign in to comment.