Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow specifying a dynamic default value via a function #1407

Open
papandreou opened this issue Jun 5, 2018 · 22 comments
Open

Allow specifying a dynamic default value via a function #1407

papandreou opened this issue Jun 5, 2018 · 22 comments
Labels

Comments

@papandreou
Copy link
Contributor

- Do you want to request a feature or report a bug?

feature

- What is the current behavior?

The default value of a field has to be a fixed string/number/boolean (depending on the field type).

- What is the expected behavior?

It would be great to be able specify a function that would return the desired value (maybe even allow it to return a promise for it, for even greater flexibility).

My use case is that I would like to generate uuids for my collection entries and editor components, and store them in a field with widget: 'hidden'.

Would be great to be able to do something like:

CMS.registerEditorComponent({
  // ...
  fields: [{
    name: 'uuid',
    label: 'UUID',
    widget: 'hidden',
    default() {
      return generateUuid();
    }
  } /*, ... */ ]
});
@erquhart
Copy link
Contributor

erquhart commented Jun 7, 2018

I've been thinking about this recently - we'd need a way to do it for fields in the config.yml, too.

@erquhart
Copy link
Contributor

erquhart commented Jun 8, 2018

@papandreou check out my proposal in #1409, you brought up a good case here that helped. The caveat is that we need to get rid of widget: hidden, and instead allow some sort of visible: false property for any widget. That way the dynamic default value function that will ship with widgets can be used automatically.

@tech4him1
Copy link
Contributor

Related to #725.

@papandreou
Copy link
Contributor Author

@erquhart, cool. That approach looks good to me!

@papandreou
Copy link
Contributor Author

we'd need a way to do it for fields in the config.yml, too

I don't know if it's a standard thing, but I noticed that the yaml parser I use mentions a funky !<tag:yaml.org,2002:js/function> syntax for declaring functions: https://github.com/nodeca/js-yaml#load-string---options-

Might be too weird, though :)

@erquhart
Copy link
Contributor

erquhart commented Aug 3, 2018

Yeah, I'm aware of js-in-yaml, and agree that it's weird. Extensions shipping functions feels pretty sane.

@tech4him1
Copy link
Contributor

A possible implementation for editor components (not config.yml), was created in #1511.

@Korka13
Copy link

Korka13 commented Nov 7, 2018

Hello,

I don't know if what I'm trying to reach is already possible or it could be related to this request.
I have a hugo site deployed on netlify through github, using netlify cms.
I would like to set the netlify cms config.yml to return automatically a default field for the post collection.
The field is the weight and I want it to count how many posts are published and return a negative number bigger than that,
With hugo I use -{{ len (where .Site.Pages "Section" "post") }} in the archetypes but I can't figure out if I can do something similar with netlify cms

Thanks!

@papandreou
Copy link
Contributor Author

papandreou commented Dec 19, 2018

Managed to hack around this by creating a custom uuid widget:

CMS.registerWidget(
  'uuid',
  class extends React.Component {
    static propTypes = {
      onChange: PropTypes.func.isRequired,
      forID: PropTypes.string,
      value: PropTypes.node,
      classNameWrapper: PropTypes.string.isRequired
    };

    static defaultProps = {
      value: ''
    };

    componentDidMount() {
      const { value, onChange } = this.props;

      if (!value) {
        onChange(generateUuid());
      }
    }

    render() {
      const { value, classNameWrapper, forID } = this.props;

      return (
        <span id={forID} className={classNameWrapper}>
          {value}
        </span>
      );
    }
  }
);

@erquhart
Copy link
Contributor

Makes sense - a dedicated id widget might be a good idea to include with the cms, too.

@papandreou
Copy link
Contributor Author

I guess -- if it's the only use case we can come up with for the dynamic default value feature 😆

@erquhart
Copy link
Contributor

Lol exactly, I still haven't thought of another one

Sent with GitHawk

@Undistraction
Copy link
Contributor

Undistraction commented Dec 23, 2018

@erquhart Would it be worth thinking about this from a different perspective? Could you add a 'uid' field that supported interpolation in the same way that 'slug' does? They are very similar - both are used once on creation. If this was supported you could do:

uid: '{{year}}-{{month}}-{{day}}-{{hour}}-{{minute}}-{{second}}-{{collection}}-{{slug}}'

You could simplify this by adding a {{dateTime}} interpolation. Another option would be for Netlify CMs to provide its own {{uuid}} interpolation (using uuid):

uid: '{{uuid}}-{{collection}}-{{slug}}'

If you think this is worthwhile I'll happily put together a PR.

@erquhart
Copy link
Contributor

The issue there is consistency and avoiding privileged entities. Frontmatter values are currently always generated by a widget, how do you see your solution working within that context?

Sent with GitHawk

@Undistraction
Copy link
Contributor

Undistraction commented Dec 24, 2018

I suppose it depends on how you look at the uuid field. It is a field that should never change after creation, so the idea that it needs a widget doesn't really make sense because it's very reason for being is that it should never be edited, and the creation of uuids is pretty standard, so I see no problem with Netlify CMS handling the creation in the same way it handles the creation of the filename, once at creation.

I can see your concerns about this field having unique behaviour, but a uuid field is by definition unique: it should never change. It has to be stored as a field because there is nowhere else to store it, but I don't think that means it has to be treated like other fields. It is a special case. I think there's a good argument for a uuid being generated for everything by default and used for tracking relations.

Anyhow, I think a robust solution is needed, as this is currently quite a big weakness in the library: the fact that relations are based on fields that are not protected from user editing makes the use of relations deeply unstable.

@erquhart
Copy link
Contributor

Definitely agree on the need for it. I think the ideal approach is probably a combination of our suggestions: supporting placeholders in the default field for widgets, and adding a built in uuid placeholder. Then you could do:

{ name: id, widget: hidden, default: "{{uuid}}" }

Sent with GitHawk

@Undistraction
Copy link
Contributor

That sounds like a good solution to me.

I guess the other thing to consider is whether all items that are created should have a uuid field: is this something that should happen by default? This would definitely be a great help when the need for a relation emerges later on.

@Undistraction
Copy link
Contributor

An additional consideration. Because of how a hidden widget is treated it doesn't seem to be possible to use a widget to set a default value, but not show that value within the CMS. For any widget other than 'hidden', an Editor component will be rendered to the UI. In the case of a uuid widget. In short, using a Widget to provide a uuid is far from ideal because it must display that value (or an empty widget) to the user, despite a uuid being an internal fields. I think this is another good reason to pursue #1975.

@alexisreina
Copy link

alexisreina commented Jan 23, 2019

I just came across the same issue, and I agree width @Undistraction that It will better to approach it differently, as the uuid is an internal value that has no meaning for the cms user. As the project I'm working on does not include React, I create an uuid widget like @papandreou but with createClass and no external dependencies ment to be used in script tag. The uuid function is from the nanoid package.

  <!-- Include the script that builds the page and powers Netlify CMS -->
  <script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
  <script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
  <script>
    CMS.registerWidget(
      'uuid',
      createClass({
        getDefaultProps: function() {
          return {
            value: ''
          }
        },
        uuid: function(options) {
          options = options || {};
          var size = options.size || 21;
          var url = options.url || "Uint8ArdomValuesObj012345679BCDEFGHIJKLMNPQRSTWXYZ_cfghkpqvwxyz-";
          var id = "";

          if (typeof self === "undefined" || (!self.crypto && !self.msCrypto)) {
            while (0 < size--) {
              id += url[(Math.random() * 64) | 0];
            }
            return id;
          }

          // else
          var crypto = self.crypto || self.msCrypto;
          var bytes = crypto.getRandomValues(new Uint8Array(size));
          while (0 < size--) {
            id += url[bytes[size] & 63];
          }
          return id;
        },
        componentDidMount: function() {
          var value = this.props.value;
          var onChange = this.props.onChange;
          var uuid = this.uuid;

          if (!value) {
            onChange(uuid());
          }
        },
        render: function() {
          var value = this.props.value;
          var classNameWrapper = this.props.classNameWrapper;
          var forID = this.props.forID;

          return h('span', { id: forID, className: classNameWrapper }, value);
        }
      })
    );
  </script>

@erquhart
Copy link
Contributor

Relevant: #1975 (comment)

@erquhart
Copy link
Contributor

I think the bigger issue here is that widgets need to set their own default values, the CMS has no way of doing that correctly.

@stale
Copy link

stale bot commented Oct 29, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants