I have simple vue3 app. It run well with npm run dev but when I try, as described in the official documentation, npm run build to ship it to production, I got the error below:
$ npm run build
> salaty#0.0.0 build
> vue-tsc --noEmit && vite build
src/main.ts:2:17 - error TS7016: Could not find a declaration file for module './Salaty.vue'. '/home/path/to/Salaty/src/Salaty.vue.js' implicitly has an 'any' type.
2 import App from './Salaty.vue'
~~~~~~~~~~~~~~
Found 1 error.
This src/main.ts :
import { createApp } from 'vue'
import App from './Salaty.vue'
createApp(App).mount('#app')
The application run well in dev. I don't know what is the problem?! I don't think I have to rename files manually before build, do it?!
Update:
Salaty.vue src:
<script>
/*let baseUrl = 'https://api.pray.zone/v2/times/today.json';
let city = 'kafr-saqr';
let timeformat = 1;
let school = 5;*/
import config from './config.json';
import moment from 'moment'
import LoadingComp from './components/LoadingComp.vue';
import Clock from './components/Clock.vue'
export default{
data(){
return {
dataRes: null,
times: null,
now: moment(),
nextTime: null,
error: null,
day: 'today.json'
}
},
computed: {
fetchUrl(){
console.log(config.baseUrl+this.day+'?city='+config.city+'&timeformat='+config.timeformat+'&school='+config.school)
return config.baseUrl+this.day+'?city='+config.city+'&timeformat='+config.timeformat+'&school='+config.school;
},
},
methods: {
async fetchData(){
this.dataRes = null;
try{
const res = await fetch(this.fetchUrl);
this.dataRes = await res.json();
}
catch(error){
this.error= 'Error! Could not reach the API.\n' + error;
alert(this.error)
}
},
},
mounted(){
this.fetchData();
localStorage.setItem("city",config.city);
},
components:{
LoadingComp,Clock
},
watch:{
dataRes(){
if (!this.dataRes) return null;
const cTimes = config.filteredTimes.reduce((obj, key)=>({ ...obj, [key]: this.dataRes.results.datetime[0].times[key] }),{});
for (const t in cTimes){
cTimes[t]=moment(this.dataRes.results.datetime[0].date.gregorian+' '+cTimes[t]);
//console.log(cTimes[t].unix(), this.nextTime, t, typeof(this.nextTime))
if (cTimes[t].unix() > this.now.unix() && this.nextTime == null){
this.nextTime = cTimes[t];
// console.log("YYYYY> ", cTimes[t],t)
cTimes[t].next = true;
}
}
if (this.nextTime == null){
this.day = 'tomorrow.json';
}
this.times = cTimes;
},
day(){
this.fetchData();
},
/*nextTime(){
this.nextTime == null
this.fetchData();
}*/
},
};
</script>
<template>
<Clock #response="(p) => {if(p > this.nextTime) this.fetchData()}" :nextTime="this.nextTime" />
{{ dataRes? dataRes.results.location.city:''}}
<ul v-if="times">
<li v-for="(time,k,i) in times">{{k}} > {{time.format('hh:mm a')}} <strong v-if="time.next">* Upcoming</strong></li>
</ul>
{{times? times.Fajr:''}}
<div v-if="!dataRes && error == null"><LoadingComp /></div>
<pre v-if="dataRes">{{dataRes}}</pre>
</template>
Related
I'm using Pinia as Store for my Vue 3 application. The problem is that the store reacts on some changes, but ignores others.
The store looks like that:
state: () => {
return {
roles: [],
currentRole: 'Administrator',
elements: []
}
},
getters: {
getElementsForCurrentRole: (state) => {
let role = state.roles.find((role) => role.label == state.currentRole);
if (role) {
return role.permissions.elements;
}
}
},
In the template file, I communicate with the store like this:
<template>
<div>
<draggable
v-model="getElementsForCurrentRole"
group="elements"
#end="onDragEnd"
item-key="name">
<template #item="{element}">
<n-card :title="formatElementName(element.name)" size="small" header-style="{titleFontSizeSmall: 8px}" hoverable>
<n-switch v-model:value="element.active" size="small" />
</n-card>
</template>
</draggable>
</div>
</template>
<script setup>
import { NCard, NSwitch } from 'naive-ui';
import draggable from 'vuedraggable'
import { usePermissionsStore } from '#/stores/permissions';
import { storeToRefs } from 'pinia';
const props = defineProps({
selectedRole: {
type: String
}
})
const permissionsStore = usePermissionsStore();
const { getElementsForCurrentRole, roles } = storeToRefs(permissionsStore);
const onDragEnd = () => {
permissionsStore.save();
}
const formatElementName = (element) => {
let title = element.charAt(0).toUpperCase() + element.slice(1);
title = title.replace('-', ' ');
title = title.split(' ');
if (title[1]) {
title = title[0] + ' ' + title[1].charAt(0).toUpperCase() + title[1].slice(1);
}
if (typeof title == 'object') {
return title[0];
}
return title;
}
</script>
My problem is the v-model="getElementsForCurrentRole". When making changes, for example changing the value for the switch, the store is reactive and the changes are made successfully. But: If I try to change the Array order by dragging, the store does not update the order. I'm confused, because the store reacts on other value changes, but not on the order change.
What can be the issue here? Do I something wrong?
-Edit- I see the following warning on drag: Write operation failed: computed value is readonly
Workaround
As workaround I work with the drag event and write the new index directly to the store variable. But...its just a workaround. I would really appreciate a cleaner solution.
Here is the workaround code:
onDrag = (event) => {
if (event && event.type == 'end') {
// Is Drag Event. Save the new order manually directly in the store
let current = permissionsStore.roles.find((role) => role.value == permissionsStore.currentRole);
var element = current.permissions.elements[event.oldIndex];
current.permissions.elements.splice(event.oldIndex, 1);
current.permissions.elements.splice(event.newIndex, 0, element);
}
}
You should put reactive value on v-model.
getElementsForCurrentRole is from getters, so it is treated as computed value.
Similar to toRefs() but specifically designed for Pinia stores so
methods and non reactive properties are completely ignored.
https://pinia.vuejs.org/api/modules/pinia.html#storetorefs
I think this should work for you.
// template
v-model="elementsForCurrentRole"
// script
const { getElementsForCurrentRole, roles } = storeToRefs(permissionsStore);
const elementsForCurrentRole = ref(getElementsForCurrentRole.value);
I'm using this text editor https://github.com/davidroyer/vue2-editor that is based on Quilljs
I want to handle the paste event so it pastes only the plain text without any format but seems in the documentation that paste is not a supported event by default.
Is there any way to add the paste event?
I've already tried using v-on:paste in the Editor and adding the Quill custom module Clipboard but haven't had any success.
As I didn't find a way of doing it with the library I did it with the DOM
onPaste() {
const x = document.getElementById("removePasteFormat");
console.log(x);
x.addEventListener("paste", (e) => {
e.stopPropagation();
e.preventDefault();
let text = e.clipboardData.getData("text/plain");
// access the clipboard using the api
if (document.queryCommandSupported("insertText")) {
document.execCommand("insertText", false, text);
} else {
document.execCommand("paste", false, text);
}
});
},
Added the id to the div containing the text editors like this:
<div id="removePasteFormat"> *<<Here goes the text editor component>>* </div>
And register the method on mounted()
mounted() {
this.onPaste();
},
I think it would be good to make a plugin.
I made it simple.
src/utils/vue2Plugin/clipboard.ts
import Delta from 'quill/node_modules/quill-delta';
import Clipboard from 'quill/modules/clipboard';
import { Quill } from 'vue2-editor';
export class CustomClipboardPlugin extends Clipboard {
public quill!: Quill;
public options: any = {};
constructor(quill: Quill) {
super(quill, {
matchers: [],
});
this.quill = quill;
this.quill.root.addEventListener('paste', this.onPaste.bind(this), true);
}
onPaste(event: ClipboardEvent) {
event.preventDefault();
event.stopPropagation();
const range = this.quill.getSelection(true);
if (range === null) return;
let _textHtml;
if (event.clipboardData?.getData('text/html')) {
_textHtml = event.clipboardData?.getData('text/html');
} else if (event.clipboardData?.getData('text/plain')) {
_textHtml = `<p>${event.clipboardData?.getData('text/plain')}</p>`;
}
if (_textHtml) {
const pastedDelta = this.quill.clipboard.convert(_textHtml);
const delta = new Delta()
.retain(range.index)
.delete(range.length)
.concat(pastedDelta);
this.quill.updateContents(delta, Quill.sources.USER);
this.quill.setSelection(delta.length() - range.length, 0, Quill.sources.SILENT);
}
}
}
vue file
<template>
...
<VueEditor
...
:custom-modules="customModulesForEditor"
...
/>
...
</template>
// script
import CustomClipboardPlugin fro 'src/utils/vue2Plugin/clipboard.ts';
...
data() {
return {
customModulesForEditor: [{ alias: 'clipboard', module: CustomClipboardPlugin }],
};
},
...
I was wondering the same and came up with the following solution.
1 - Use the :editorOptions option referenced here
<template>
VueEditor(:editorOptions="editorSettings")
</template>
2 - Fill with the module.clipboard module with the option described here
3 - You can then handle the paste with your personal function (applied after quill's matcher). I've written mine following this answer on github
<script>
data() {
return {
editorSettings: {
modules: {
clipboard: {
matchers: [[Node.ELEMENT_NODE, this.customQuillClipboardMatcher]]
}
}
}
}
},
methods: {
customQuillClipboardMatcher(node, delta) {
delta.ops = delta.ops.map((op) => {
return {
insert: op.insert
}
})
return delta
},
}
</script>
I'm in trouble I'm trying to pass information that gets from an api rest that's in a component A and store it in a variable of a B component
i've dealt with this:
Componente A:
data: () => {
return {
server: []
};
}
axios
.post("http://0.tcp.ngrok.io:13601//accountmanagement/login", LogInUser)
.then(res => {
this._UserLogin = res.data;
if (
this._UserLogin.credencial.rol.id_rol == 1 &&
this._UserLogin.credencial.correo == LogInUser.correo
) {
this.$router.push("admin");
} else if (
this._UserLogin.credencial.rol.id_rol == 2 &&
this._UserLogin.credencial.correo == LogInUser.correo
) {
this.$router.push("usuario");
this.server = this._UserLogin
//here I try to send the already verified user to another
component clear the request returns me a json if correct if
the registration is correct:
EventBus.$emit('serverSelected', this.server);
}
})
.catch(resError => {
alert(
"El servidor es en mantenimiento por favor intente mas tarde" +
resError
);
});
Component B:
data: () => {
return {
server: []
};
}
created() {
EventBus.$on(
"serverSelected",
function(serve) {
this.server = serve;
console.log("The person is: ", this.server);
}.bind(this)
);
}
including this does not show me any error or print me the information of the record that I try to send by eventBus.emit
Call eventBus:
import eventBus from '../../evenbus'
evenbus content:
import Vue from 'vue';
export const EventBus = new Vue();
I am creating some custom components based on Element UI.
I have two issues at the moment:
- Pass all the context down from the wrapper to the component;
- When I click on the select element in the following snippet the event does not trigger the change of currentValue. I tried also with #onchange="setValue" :value="currentValue", but nothing changed.
Obviously if I use Select and Option as they come with Element UI, they do work as supposed.
The reason why I need to wrap the components is that I need to add some default classes and brand them with some custom CSS.
---CustomSelect.js
import Vue from 'vue';
import { Select } from 'element-ui';
import classnames from 'classnames';
import 'element-theme-chalk/src/select.scss';
import './select.scss';
export default Vue.component('ExampleSelect', {
functional: true,
render(h, context) {
console.log('ExampleSelect context', context);
function wrappedComponent() {
return Select;
}
function getExtendedClassName() {
return classnames('example-select', {
[context.props.classNames]: context.props.classNames
});
}
return h(
wrappedComponent(),
{
class: getExtendedClassName(),
parent: context.parent && Object.keys(context.parent).length > 0 && context.parent,
data: context.data && Object.keys(context.data).length > 0 && context.data,
props: context.props && Object.keys(context.props).length > 0 && context.props,
injections:
context.injections && Object.keys(context.injections).length > 0 && context.injections,
listeners:
context.listeners && Object.keys(context.listeners).length > 0 ? context.listeners : {}
},
context.children && Object.keys(context.children).length > 0 && context.children
);
}
});
---CustomOption.js
import Vue from 'vue';
import { Option as ExampleOption } from 'element-ui';
import classnames from 'classnames';
import 'element-theme-chalk/src/option.scss';
import './option.scss';
export default Vue.component('ExampleOption', {
functional: true,
render(h, context) {
console.log('ExampleSelect option', context);
function wrappedComponent() {
return ExampleOption;
}
function getExtendedClassName() {
return classnames('example-option', {
[context.props.classNames]: context.props.classNames
});
}
return h(
wrappedComponent(),
{
class: getExtendedClassName(),
parent: context.parent && Object.keys(context.parent).length > 0 && context.parent,
data: context.data && Object.keys(context.data).length > 0 && context.data,
props: context.props && Object.keys(context.props).length > 0 && context.props,
injections:
context.injections && Object.keys(context.injections).length > 0 && context.injections,
listeners:
context.listeners && Object.keys(context.listeners).length > 0 ? context.listeners : {}
},
context.children && Object.keys(context.children).length > 0 && context.children
);
}
});
Thank you in advance for your help.
I solved the issue.
So it looks like the names of the properties in the data object
https://v2.vuejs.org/v2/guide/render-function.html#The-Data-Object-In-Depth
Are different from the names of the properties in context:
https://v2.vuejs.org/v2/guide/render-function.html#Functional-Components
Maybe a suggestion for the future is to make them match, or create an utility that maps them allowing to pass them all at once like that.
This is useful in the context of hocs where you want to delegate the main functionality to the received component and you just want to change a few details and make them default.
Therefore, this is the correct return statement:
return h(
wrappedComponent(),
{
class: getExtendedClassName(),
name: 'ExampleInput',
componentName: 'ExampleInput',
props: context.props,
slots: context.slots(),
scopedSlots: context.scopedSlots,
data: context.data,
parent: context.parent,
on: context.listeners,
inject: context.injections,
},
context.children
);
Any examples of using AMD with TypeScript and dojo AMD? Keep getting "3" instead of an object (tslab == 3):
require( ["TypeScriptLab"], function ( tslab )
{
new tslab.Tests().run();
} );
The TypeScript looks like this:
export class TypeScriptLab {
test() {
}
}
The generated JS looks like this:
define(["require", "exports"], function(require, exports) {
var TypeScriptLab = (function () {
function TypeScriptLab() { }
TypeScriptLab.prototype.test = function () {
};
return TypeScriptLab;
})();
exports.TypeScriptLab = TypeScriptLab;
})
I defined my packages:
<script>
dojoConfig = {
async: true,
packages: [
{ name: "TSLab", location: "/IPS" }
]
};
</script>
And added a namespace prefix:
require( ["TSLab/typeScriptLab"], function ( tslab )
{
new tslab.Tests().run();
} );
And the module now loads.