Reputation: 67
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
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