1
votes

the problem has been solved, read through the question to see how step by step fix the problems

I read about composition api(https://vue-composition-api-rfc.netlify.com/#api-introduction), and tried to convert my existing code which is based on typescript on Option api in vuejs, and i failed to make it work, and kinda confused about how composition api really work.

my working code is below:

<script lang="ts">
  import Vue from "vue";
  import {inputFields} from "@/mixins/inputField/inputField";
  import VuePersianDatetimePicker from "vue-persian-datetime-picker"
  import {util_timestampz_formatter, util_timestampz_now} from "@/utils/commonUtility";
  import {InputFieldHelper} from "@/mixins/Helpers/FormGeneratorInputFieldHelper/InputField.Helper.mixin";

  export default Vue.extend({
    name: "DateTimePicker",
    props: ['instance', "value", "formSection", "isEnable"],
    components: {
      datePicker: VuePersianDatetimePicker
    },
    data() {
      return {
        inputField: new InputFieldHelper(this.value, this.instance, this.formSection, this.isEnable)
      }
    },
    mounted() {
      //@ts-ignore
      this.instance.min = util_timestampz_now();
    },
    methods: {
      updateValue() {
        let new_data = this.inputField.updateValue();
        new_data ? this.$emit("updateValue", new_data) : "";
      },
      updateDateTime(time: string) {
        //@ts-ignore
        this.inputField.inputValue = util_timestampz_formatter(this.inputField.inputValue);
        //@ts-ignore
        this.updateValue();
      },
    }
  });
</script>

the converted version in composition api is below which is not run properly:

<script lang="ts">
  import Vue from "vue";
  import {reactive, createComponent, onMounted} from "@vue/composition-api";
  import VuePersianDatetimePicker from "vue-persian-datetime-picker"
  import {util_timestampz_formatter, util_timestampz_now} from "@/utils/commonUtility";
  import {InputFieldHelper} from "@/mixins/Helpers/FormGeneratorInputFieldHelper/InputField.Helper.mixin";
  import {FormGeneratorInputField} from "@/types";

  export default createComponent({
    name: "DateTimePicker",
    props: {
      instance,
      value,
      formSection,
      isEnable,
    },
    components: {
      datePicker: VuePersianDatetimePicker
    },
    setup(props, context) {
      const inputField = reactive(new InputFieldHelper(props.value, props.instance, props.formSection, props.isEnable));
      onMounted(() => {
        props.instance.min = util_timestampz_now();
      });
      const updateValue = () => {
        let new_data = inputField.updateValue();
        new_data ? context.$emit("updateValue", new_data) : "";
      };
      const updateDateTime = (time: string) => {
        inputField.inputValue = util_timestampz_formatter(inputField.inputValue);
        updateValue();
      };
      return {
        inputField, updateValue, updateDateTime
      }
    }
  });
</script>

which webstorm shows 3 error:

enter image description here

is there anyway to fix this problems properly?

.................

update1:

i did mistake in using props(i thought i new vue, it does not need the type, and it takes it automatically in typescript from parent component). thanks to paleo the problem has been gone and it fixed.

and to anyone which has similar problem in getting type error for props like what you see in below picture enter image description here

thanks to https://frontendsociety.com/using-a-typescript-interfaces-and-types-as-a-prop-type-in-vuejs-508ab3f83480 ,you can pass your created type interface as function like below: enter image description here

but there is still some issues which is need to be fixed, like below :

1.when i removed reactive, it seems the reactivity is not working properly, and in the template which says "unresolved variable or missing import statement" enter image description here

..........................

update2 sharing props and using composition functions:

to me, after converting option based api to composition api, it seemed that it was better than the new one, thanks to paleo and Maximilian shrwazmuller(i strongly suggest to see this free video: https://www.youtube.com/watch?v=V-xK3sbc7xI), and google search struggling with this https://vue-composition-api-rfc.netlify.com/api.html#template-refs i re-write the code to be something like below:

<script lang="ts">
  import {createComponent, onMounted} from "@vue/composition-api";
  import VuePersianDatetimePicker from "vue-persian-datetime-picker"
  import {util_timestampz_formatter, util_timestampz_now} from "@/utils/commonUtility";
  import {
    useInputFieldComposition,
    inputFieldProps
  } from "@/mixins/Helpers/FormGeneratorInputFieldHelper/inputField.composition";
  import {ComponentPropsOptions} from "@vue/composition-api/dist/component/componentProps";

  export default createComponent({
    name: "DateTimePicker",
    props: inputFieldProps as ComponentPropsOptions,
    components: {
      datePicker: VuePersianDatetimePicker
    },
    setup(props, context) {
      const {inputField, updateValue} = useInputFieldComposition(props, context);
      onMounted(() => {
        props.instance.min = util_timestampz_now();
      });
      const updateDateTime = (time: string) => {
        inputField.inputValue = util_timestampz_formatter(inputField.inputValue);
        updateValue();
      };
      return {
        inputField, updateValue, updateDateTime
      }
    }
  });
</script>

as you can see, it has been pretty neat, because everything which will be shared among components are in another file(named: inputFiled.compositons) like below:

//inputField.composition.ts
import {PropType, reactive} from "@vue/composition-api";
import {FormGeneratorInputField} from "@/types";
import {InputFieldHelper} from "@/mixins/Helpers/FormGeneratorInputFieldHelper/InputField.Helper.mixin";

export const inputFieldProps = {
  instance: {
    type: Object as PropType<FormGeneratorInputField>,
    required: true
  },
  value: {
    type: Object,
    required: true
  },
  formSection: {
    type: String,
    required: true
  },
  isEnable: {
    type: Boolean,
    required: true
  },
};
export const useInputFieldComposition = (props: any, context: any) => {
  let inputField  = reactive(new InputFieldHelper(props.value, props.instance, props.formSection, props.isEnable));
  const updateValue = (): any => {
    let new_data = inputField.passInfoMixedValue();
    new_data ? context.emit("updateValue", new_data) : "";
    return new_data;
  };

  return {
    props, inputField, updateValue
  }

as you can see with this feature we can use typescript to the fullest potential.

and finally i should tell you if you following the path and using vue-composition api, your project will work perfectly, but webstorm will show you some warning about the code specificly in your template, like unresolved variable. these new syntax support is under develop which you can see in this issue https://youtrack.jetbrains.com/issue/WEB-41565 so don't be worry about it and use it.

1
Three // @ts-ignore… ouch!Paleo

1 Answers

1
votes

Read the documentation on props. Here is an example:

    props: {
      instance: {
        type: Object,
        required: true
      },
      value: {
        type: Object,
        required: true
      },
      formSection: {
        type: Object,
        required: true
      },
      isEnable: {
        type: Boolean,
        required: false
      },
    },

In order to enable better types, PropType can be used:

import { PropType } from "@vue/composition-api/dist/component/componentProps";
// …

    props: {
      instance: {
        type: Object as PropType<FormGeneratorInputField>,
        required: true
      },
      // …

To emit an event, follow the suggestion in your error message: context.emit("updateValue").

Regarding the instance of InputFieldHelper, I suggest to refactor this class implementation:

class InputFieldHelper {
  readonly inputField: {
    inputValue: string | null
  }

  constructor(/* … */) {
    this.inputField = reactive({
      inputValue: null
    })
  }
}

Then, use it:

const helper = new InputFieldHelper(props.value, props.instance, props.formSection, props.isEnable);

const inputField = helper.inputField;