Busenur Adıbelli
Busenur Adıbelli

Reputation: 67

How to set compilerOptions.isCustomElement for VueJS 3 in Quasar Project

I have a project that I created with Vue 3 and Quasar 2.

I created the WhatsappTextMessageEditor component and I use vue3-emoji-picker as EmojiPicker in this component. At first the components were working as I wanted, but then I started getting this error.

[Vue warn]: Failed to resolve component: EmojiPicker
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement. 

WhatsappTextEditorMessage component is like this;

<script>

import { computed, ref, reactive, watch } from "vue";
import SelectItemByCategory from "components/master/SelectItemByCategory.vue";
import EmojiPicker from "vue3-emoji-picker";
// import css
import "vue3-emoji-picker/css";
import { LocalStorage } from 'quasar';
export default {
  components: {
    EmojiPicker,
  },
  setup(props) {
    const variableCounter = ref(1); // Değişken sayacını başlatın

    function addVariable() {
      for (let i = 0; i < languagesTabs.value.length; i++) {
        const currentText = model.value[i][name][0][listName];
        this.nextVariable = `{{${variableCounter.value}}} `;

        // Kullanıcıdan alınan metnin sonuna bir sonraki sıradaki değişkeni ekleyin
        model.value[i][name][0][listName] += this.nextVariable;
        // console.log(model.value[i][name][0][listName], "model.value[i][name][0][listName]");
        // Değişken sayacını artırın

        variableCounter.value++;

      }
    }
    const languagesTabs = ref([]);
    // const $q = useQuasar()
    // const languages = computed(()=> {
    //   return $q.localStorage.getItem('languages')
    // })
    const model = computed({
      get: () => {
        if (props.modelInfo && props.modelInfo.length > 1) {
          if (props.modelInfo.ID !== 0) {
            return props.modelInfo;
          } else {
            return "";
          }
        } else {
          let modelInfo = [];
          for (let i = 0; i < languagesTabs.value.length; i++) {
            let modelObject = {};
            modelObject["CreateDate"] = null;
            modelObject["CreateUserID"] = 0;
            modelObject["CreateUserName"] = null;
            //modelObject['Description'] = ''
            modelObject[props.listName] = "";
            modelObject["EditDate"] = null;
            modelObject["EditUserID"] = 0;
            modelObject["EditUserName"] = null;
            //modelObject['ID'] = 0
            modelObject["ID"] = 0;
            modelObject["LanguageID"] = languagesTabs.value[i].ID;
            modelObject["LanguageName"] = languagesTabs.value[i].Name;
            modelObject["IsoCode"] = languagesTabs.value[i].IsoCode;
            modelInfo.push(modelObject);
          }
          return modelInfo;
        }
      },
      set: (newValue) => {
        return newValue;
      },
    });
    function getLanguages() {
      this.$gf
        .GetApi(this.$gf_p.Auth, "Auth", "GetLanguages", null, true)
        .then((response) => {
          languagesTabs.value = JSON.parse(response.data.result);
        });
    }
    return {
      model,
      languagesTabs,
      getLanguages,

      // languages,
      // $q
    };
  },
  props: {
    modelInfo: {
      type: Array,
    },
    variableModel: {
      type: Array,
    },
    listName: {
      type: String,
      default: "Description",
    },
    name: {
      type: String,
      default: "AgreementLanguages",
    },
    placeholder: {
      type: String,
      default: "Enter Text",
    },
    styleText: {
      type: Boolean,
      default: true,
    },
    boldBtn: {
      type: Boolean,
      default: true,
    },
    italicBtn: {
      type: Boolean,
      default: true,
    },
    strikethroughBtn: {
      type: Boolean,
      default: true,
    },
    codeBtn: {
      type: Boolean,
      default: true,
    },
    emojiBtn: {
      type: Boolean,
      default: true,
    },
    variableMultiple: {
      type: Boolean,
      default: true,
    },
  },
  components: {
    SelectItemByCategory,
  },
  data() {
    return {
      variableBtn: true,
      selectItems: LocalStorage.getItem('WhatsappTextMessageVariables') || [],
      variableCount: 0,
      nextVariable: "",
      showEmojiPicker: false,
      activeTab: 0,
      textRules: [(v) => !!v || "Metin alanı boş bırakılamaz"],
      boldActive: false,
      italicActive: false,
      strikethroughActive: false,
      codeActive: false,
      emojiActive: false,
      whatsappTextMessageComponentVariable: {
        ID: 0,
        Name: ""
      },
      forms: {
        WhatsappTextMessageComponentVariableName: "",
        WhatsappTextMessageComponentVariableID: 1,
      },
    };
  },
  methods: {
    // getLanguages() {
    //     this.$gf
    //         .GetApi(this.$gf_p.Auth, "Auth", "GetLanguages", null, true)
    //         .then((response) => {
    //             this.languagesTabs = JSON.parse(response.data.result);
    //         });
    // },
    updateVariableBtn() {
      if (!this.variableMultiple && this.selectItems.length === 1) {
        this.variableBtn = false;
      } else if (this.selectItems.length === 0) {
        this.variableBtn = true;
      } else {
        this.variableBtn = true;
      }
    },


    toggleEmojiPicker() {
      this.showEmojiPicker = !this.showEmojiPicker;
      this.emojiActive = !this.emojiActive;
    },
    applyFormat(language, format, index) {
      const text = language[this.name][0][this.listName];
      const selectedText = window.getSelection().toString();
      let newText = text;

      switch (format) {
        case "bold":
          if (this.boldActive) {
            newText = newText.replace(/\*/g, "");
          } else {
            newText = newText.replace(selectedText, `*${selectedText}*`);
          }
          this.boldActive = !this.boldActive;
          break;
        case "italic":
          if (this.italicActive) {
            newText = newText.replace(/_/g, "");
          } else {
            newText = newText.replace(selectedText, `_${selectedText}_`);
          }
          this.italicActive = !this.italicActive;
          break;
        case "strikethrough":
          if (this.strikethroughActive) {
            newText = newText.replace(/~/g, "");
          } else {
            newText = newText.replace(selectedText, `~${selectedText}~`);
          }
          this.strikethroughActive = !this.strikethroughActive;
          break;
        case "code":
          if (this.codeActive) {
            newText = newText.replace(/```/g, "");
          } else {
            newText = newText.replace(
              selectedText,
              "```" + selectedText + "```"
            );
          }
          this.codeActive = !this.codeActive;
          break;
      }

      language[this.name][0][this.listName] = newText;
    },

    getFormattedMessage(text) {
      // Check if text is defined and not empty
      if (text && text.length > 0) {
        // Apply the formatting based on your styles
        const formattedText = text
          .replace(/\*([^*]+)\*/g, '<span class="bold-text">$1</span>') // Bold
          .replace(/_([^_]+)_/g, '<span class="italic-text">$1</span>') // Italic
          .replace(/~([^~]+)~/g, '<span class="strikethrough-text">$1</span>') // Strikethrough
          .replace(/```([^`]+)```/g, '<span class="code-text">$1</span>'); // Code

        return formattedText;
      } else {
        return ""; // Return an empty string if text is not defined or empty
      }
    },
    onSelectEmoji(emoji, language, index) {
      language[this.listName] = language[this.listName] || "";
      language[this.listName] += emoji.i;
    },
    addVariable() {
      for (let i = 0; i < this.languagesTabs.length; i++) {
        const currentText = this.model[i][this.name][0][this.listName];
        this.variableCount = (currentText.match(/{{(\d+)}}/g) || []).length;
        this.nextVariable = `{{${this.variableCount + 1}}} `;
        const updatedText = currentText + " " + this.nextVariable;


        // Metni güncelle
        this.model[i][this.name][0][this.listName] = updatedText;
      }

      // selectItems dizisine yeni değişkeni ekleyin (döngü dışında)
      const newItem = {
        WhatsappTextMessageID: this.variableCount, // İlgili özellikleri doldurun
        VariableIndex: this.nextVariable,
        TextMessageVariables: 0,
        TextMessageVariablesName: "",
      };
      this.selectItems.push(newItem);
      // nextVariable'ı güncellemeye gerek yok
    },
    removeVariable(index, item) {
      this.selectItems.splice(index, 1); // Remove the item at the specified index
      for (let i = 0; i < this.languagesTabs.length; i++) {
        if (this.model[i][this.name][0][this.listName].includes(item.VariableIndex)) {
          // Remove the variable from the model data
          this.model[i][this.name][0][this.listName] = this.model[i][this.name][0][this.listName].replace(item.VariableIndex, '');
          this.variableBtn = true;
        }
      }
    },

    updateTextMessageVariables(item, event) {
      // if (!this.initialUpdateApplied && this.selectItems.length > 0) {
      //   console.log("IFFFFFFFFF")
      //   this.selectItems.forEach((element) => {
      //     item.VariableIndex = element.VariableIndex;
      //     item.WhatsappTextMessageID = element.WhatsappTextMessageID;
      //     event.ID = element.TextMessageVariables;
      //     event.Name = element.TextMessageVariablesName;

      //   });

      //   this.initialUpdateApplied = true;
      // }
      // else {
      const { ID, Name } = event;
      this.selectItems.forEach((element) => {
        if (element.VariableIndex === item.VariableIndex) {
          element.TextMessageVariables = ID;
          element.TextMessageVariablesName = Name;
          item.VariableIndex = element.VariableIndex;
          item.WhatsappTextMessageID = element.WhatsappTextMessageID;
        }
      });
      this.$emit('selectItems', this.selectItems);
      // }

    },
    replaceVariables(text, selectItems) {
      // Regular expression to match variables like {{1}}
      const variableRegex = /{{(\d+)}}/g;

      // Replace each variable with the corresponding TextMessageVariablesName
      const replacedText = text.replace(variableRegex, (match, variableIndex) => {
        const index = parseInt(variableIndex) - 1; // Convert variableIndex to zero-based index
        if (selectItems[index] && selectItems[index].TextMessageVariablesName) {
          return selectItems[index].TextMessageVariablesName;
        }
        return match; // If no match found, return the original variable
      });

      return replacedText;
    },


  },
  watch: {
    selectItems: {
      handler(newSelectItems) {
        // `selectItems` değiştiğinde bu kod çalışacak
        // `updateTextMessageVariables` yöntemini çağırabilirsiniz
        for (let i = 0; i < newSelectItems.length; i++) {
          const item = newSelectItems[i];
          const event = {
            ID: item.TextMessageVariables,
            Name: item.TextMessageVariablesName
          };

          this.updateTextMessageVariables(item, event);
        }
        this.updateVariableBtn();
      },
      deep: true // Derin izleme etkinleştirildi
    },



    // 'languagesTabs': {
    //     handler(newVal) {
    //         // forms.WhatsappTextMessageLanguageList dizisini güncelleyin
    //         this.forms.WhatsappTextMessageLanguageList = newVal.map((language) => ({
    //             LanguageID: language.ID, // Dil ID'si
    //             LanguageName: language.Name, // Dil adı
    //             MessageText: language.text, // q-input alanındaki metin
    //         }));
    //     },
    //     deep: true, // Derinlemesine izleme etkinleştirildi
    // },
  },
  computed: {
    theme() {
      return this.$store.state.users.UserProfile.UserProfile.DarkMode
        ? "dark"
        : "light";
    },
    formattedText() {
      return this.replaceVariables(
        this.model[this.activeTab][this.name][0][this.listName],
        this.selectItems
      );
    },

  },
  mounted() {
    this.getLanguages();
    this.selectItems = LocalStorage.getItem('WhatsappTextMessageVariables') || [];

  },
};
</script>
<template>
  <q-page style="min-height: 0;max-width: 100%">
    <q-tabs v-model="activeTab" style="max-width: 100%">
      <q-tab v-for="(language, index) in languagesTabs" :key="index" :name="index" :label="language.IsoCode"></q-tab>
    </q-tabs>

    <q-tab-panels style="max-width: 100%" v-model="activeTab">
      <q-tab-panel v-for="(language, index) in languagesTabs" :key="index" :name="index">
        <div v-if="styleText" class="buttons">
          <q-btn v-if="boldBtn" outline padding="xs" :color="boldActive ? 'primary' : 'grey'" icon="format_bold"
            @click="applyFormat(model[index], 'bold', index)" />
          <q-btn v-if="italicBtn" outline padding="xs" :color="italicActive ? 'primary' : 'grey'" icon="format_italic"
            @click="applyFormat(model[index], 'italic', index)" />
          <q-btn v-if="strikethroughBtn" outline padding="xs" :color="strikethroughActive ? 'primary' : 'grey'"
            icon="strikethrough_s" @click="applyFormat(model[index], 'strikethrough', index)" />
          <q-btn v-if="codeBtn" outline padding="xs" :color="codeActive ? 'primary' : 'grey'" icon="code"
            @click="applyFormat(model[index], 'code', index)" />
          <q-btn v-if="emojiBtn" outline padding="xs" :color="emojiActive ? 'primary' : 'grey'" icon="emoji_emotions"
            @click="toggleEmojiPicker" />
        </div>
        <EmojiPicker :native="true" @select="onSelectEmoji($event, model[index][name][0], index)" :theme="theme"
          :hideGroupNames="false" />

        <q-form>
          <!-- {{ this.selectItems }} -->

          <q-input type="textarea" counter v-model="model[index][name][0][listName]"
            :label="'Metin (' + language.IsoCode + ')'" :rules="textRules"></q-input>

          <q-chat-message bg-color="secondary" stamp="now" text-color="white">
            <template v-slot:name>{{
              this.$store.state.users.UserProfile.UserProfile.Name
            }}</template>
            <template v-slot:avatar>
              <img class="q-message-avatar q-message-avatar--received" :src="this.$store.state.users.UserProfile.UserProfile.UserAvatar
                    ? require(`../../assets/avatar/${this.$store.state.users.UserProfile.UserProfile.UserAvatar}.jpg`)
                    : require(`../../assets/avatar/0.jpg`)
                  " />
            </template>

            <!-- <div>
                              Already building an app with it...
                          </div> -->

            <q-spinner-dots v-if="!model[index][name][0][listName]" size="2rem" />
            <div v-else v-html="getFormattedMessage(formattedText)"></div>
          </q-chat-message>

        </q-form>
      </q-tab-panel>
    </q-tab-panels>
    <div class="row" style="text-align: center;">
      <div class="col-12 col-md-8">

        <div v-for="(item, index) in selectItems" :key="index">
          <div class="row">
            <div class="col-12 col-md-3">
              {{ item.VariableIndex }}
            </div>
            <div class="col-12 col-md-8">

              <SelectItemByCategory ref="selectItemComponentRefName"
                :categoryName="'WhatsappTextMessageComponentVariable'"
                :PropertyName="'WhatsappTextMessageComponentVariable'" :ParentName="'WhatsappTextMessage'"
                :SelectItemByCategory="{
                  ID: item.TextMessageVariables,
                  Name: item.TextMessageVariablesName
                }" @change="updateTextMessageVariables(item, $event)" />

            </div>
            <div class="col-12 col-md-1">
              <q-btn flat icon="close" color="primary" @click="removeVariable(index, item)" />
            </div>
          </div>
        </div>
      </div>


      <div class="col-12 col-md-4">
        <q-btn v-if="this.variableBtn" flat icon="add" :label="$gf.getLabel('Degisken', 'Common')" color="primary"
          @click="addVariable()" />
      </div>
    </div>

  </q-page>
</template>

How can I solve the unresolved error of the component I mentioned above?

Upvotes: 2

Views: 813

Answers (1)

cfischbeck
cfischbeck

Reputation: 31

To configure isCustomElement, you need to adjust the vueLoaderOptions within the Quasar configuration. The compilerOptions should be nested inside vueLoaderOptions​ ​​ The location of this configuration depends on whether you are using Webpack or Vite as your build tool. For Webpack, the configuration goes under quasar.config.js > build. For Vite, the structure is slightly different. In the case of Vite, the configuration should be placed within the build property of quasar.config.js, specifically under viteVuePluginOptions and then within the template object​

Here's an example of how the configuration would look in quasar.config.js for a Vite-based project:

// quasar.config.js
// ...
build: {
  viteVuePluginOptions: { //You may have to add or uncomment this option.
    template: {
      compilerOptions: {
        isCustomElement: (tag) => tag.startsWith('my-')
      }
    }
  }
}
// ...

This setting tells Vue to treat elements that start with 'my-' as custom elements. Modify the lambda function as per your requirements (e.g., to check for 'lottie-player' or any other tag relevant to your project).

Upvotes: 1

Related Questions