vuejs / Discussion

Vue.js discussion
167 stars 17 forks source link

Component properties type checking for null string. #374

Closed Haixing-Hu closed 9 years ago

Haixing-Hu commented 9 years ago

Hi, I write a summer note editor component as follows:

<template>
  <textarea class="form-control" name="{{name}}" v-model="model"></textarea>
</template>
<script>
/**
 * A textarea control supporting the HTML format.
 *
 * @param name
 *    the optional name of the control.
 * @param model
 *    the model bind to the control, which must be a two way binding variable.
 */
module.exports = {
  replace: true,
  inherit: true,
  props: {
    name: {
      type: String,
      required: false,
      default: ""
    },
    model: {
      type: String,
      required: true,
      twoWay: true
    }
  },
  ready: function(e) {
    console.debug("[DEBUG] html-textarea.ready: initialize the HTML textarea control.");
    //  initialize the summernote
    $(this.$el).summernote({
      lang: this.preference.language,
      height: 220,
      minHeight:220,
      maxHeight: null,
      toolbar: [
        ["font", ["bold", "italic", "underline", "clear"]],
        ["fontsize", ["fontsize"]],
        ["para", ["ul", "ol", "paragraph"]],
        ["color", ["color"]],
        ["insert", ["link", "picture", "hr"]]
      ]
    });
  }
};
</script>

And I use it in this way

   <html-textarea name="description" model="{{@ model.description}}">
   </html-textarea>

Until now everything is O.K. But if the model.description is a null value, and the Vue is on the debug model, a warning will occurs:

[Vue warn]: Invalid prop: type check failed for model="{{@ model.description}}". Expected String, got Null.

I think the type checking code should take account of the null value, and consider a null value as a value of any data type.

Haixing-Hu commented 9 years ago

Sorry, the above code does not work. The code cannot bind the content of a summernote editor to a Vue variable. But the null value type checking problem described by the code still exist even if the code does not work.

By the way, does anyone knows how to bind the HTML content of a div to a Vue variable using the JS code instead of "v-html" directive? Since the summerynote could only be initialized after the dom was ready, and I must bind the html content of the div generated by the summernote to the "model" variable.

Haixing-Hu commented 9 years ago

OK, I find a way to make the component works.

<template>
  <textarea class="form-control"></textarea>
</template>

<script>
/**
 * A textarea control supporting the HTML format.
 *
 * @param model
 *    the model bind to the control, which must be a two way binding variable.
 */
module.exports = {
  replace: true,
  inherit: true,
  props: {
    model: {
      type: String,
      required: true,
      twoWay: true
    }
  },
  codeChanged: false,
  ready: function() {
    // console.debug("[DEBUG] html-textarea.ready");
    //  initialize the summernote
    var me = this;
    $(this.$el).summernote({
      lang: this.preference.language,
      height: TEXTAREA_HEIGHT,
      minHeight: TEXTAREA_HEIGHT,
      maxHeight: null,
      toolbar: [
        ["font", ["bold", "italic", "underline", "clear"]],
        ["fontsize", ["fontsize"]],
        ["para", ["ul", "ol", "paragraph"]],
        ["color", ["color"]],
        ["insert", ["link", "picture", "hr"]]
      ],
      onInit: function() {
        $(me.$el).code(me.model);
      },
      onChange: function() {
        me.codeChanged = true;
        me.model = $(me.$el).code();
      }
    });
  },
  watch: {
    "model": function (val, oldVal) {
      // NOTE that we use a codeChanged state variable to avoid the unnecessary
      // change of the summernote code
      if (! this.codeChanged) {
        $(this.$el).code(val);
      } else {
        this.codeChanged = false;
      }
    }
  }
};
</script>

But there is still a waning if the "model" bind to this component is null.

yyx990803 commented 9 years ago

If you declared a prop as string, why pass in null? Just pass an empty string instead.

If you really want to accept more than one types, use a custom validator:

props: {
  model: {
    validator: function (val) {
      return val === null || typeof val === 'string'
    }
  }
}
anxolin commented 7 years ago

I agree with Evan, if you have a mandatory String, you should always provide a value, so the parent must provide at least an empty string.

But, for some cases, the parent doesn't actually know the value of the string (for instance it's in the middle of an AJAX request), so I believe the parent should model the value as a NULL or undefined. However, in this case it should NOT be model as a mandatory prop, it should be optional. The child component should be the one deciding how to handle a null or undefined value.