Vue3 with Vue.draggable-next drag and drop problem - javascript

I'm trying to migrate from Vue2 to Vue3 of my project. It's a simple html project with Vue and having drag and drop table. It works fine in vue2 but not in vue3. Getting this error in console : "vue#next:8001 [Vue warn]: Failed to resolve component: draggable
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement. at "
<!DOCTYPE html>
<html lang="en" >
<meta charset="utf-8"/>
<head>
<script type="text/javascript" src="https://unpkg.com/vue#next"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue-draggable-next#2.1.1/dist/vue-draggable-next.global.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"/>
<script src="https://cdn.jsdelivr.net/npm/sortablejs#latest/Sortable.min.js"></script>
</head>
<body>
<div id='myMain'>
<h2> {{ title }} </h2>
{{ title2 }}
<br><br>
<table>
<draggable :list="schoolInfo" >
<template v-for='(school, index) in schoolInfo' >
<tr>
<td>
<input v-model="school['name']">
</td>
<td>
<input v-model="school['address']" >
</td>
<td>
<input v-model="school['city']">
</td>
<td>
<input v-model="school['phone']" >
</td>
</tr>
</template>
</draggable>
</table>
</div>
<script>
const schoolData = Vue.createApp({
data() {
return {
title:"this is my school",
title2:'title2 here it iss',
schoolInfo: [
{ id: 0,
name:"school1",
address:"address1",
city:"city1",
phone:"phone1"
},
{ id: 1,
name:"school2",
address:"address2",
city:"city2",
phone:"phone2"
},
{ id: 3,
name:"school3",
address:"address3",
city:"city3",
phone:"phone3"
},
{ id: 4,
name:"school4",
address:"address4",
city:"city4",
phone:"phone4"
}
],
}
}
})
schoolData.component('draggable',VueDraggableNext)
schoolData.mount('#myMain')
</script>
</body>
</html>

Remove:
components: {
draggable: VueDraggableNext,
},
Add:
schoolData.component('draggable',VueDraggableNext);
before schoolData.mount('#myMain')
This should resolve the component.

Related

Strings containing tags are not rendered properly inside <td>

I'm trying to show how **hello** will be converted to <b>hello</b> and rendered as hello. I made a table for this and you can check it here in jsfiddle
html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>The HTML5 Herald</title>
<script src="js/scripts.js"></script>
</head>
<body>
<div id="app">
<table style="width:100%">
<tr>
<th>Input</th>
<th>Output</th>
<th>View</th>
</tr>
<tr v-for="example in examples" v-bind:key="example">
<td>{{example.input}}</td>
<td><pre>{{example.output}}</pre></td>
<td>{{example.output}}</td>
</tr>
</table>
</div>
</body>
</html>
JS
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
examples: [{input:"**a**", output:"<b>a</b>"}]
}
})
Everything works fine except for the third column, which is <td>{{example.output}}</td>. I checked that the column was replaced to <td><b>a</b></td> from the inspector, but the bold style isn't applied. Does it have to do with Vue? When I type the value <b>a</b> instead of passing the data through vue, I can see the string bolded. How can I make it styled?
do you mean something like this v-html?
new Vue({
el: "#app",
data: {
message: 'Hello Vue!',
examples: [{input:"**a**", output:"<b>a</b>"}, {input:"*a*", output:"<i>a</i>"}]
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table style="width:100%">
<tr>
<th>Input</th>
<th>Output</th>
<th>View</th>
</tr>
<tr v-for="(example, index) in examples" v-bind:key="index">
<td>{{example.input}}</td>
<td ><pre v-html="example.output"></pre></td>
<td v-html="example.output"></td>
</tr>
</table>
</div>
You have to use v-html directive to render string as html. Changing <td>{{example.output}}</td> of the third td to <td v-html="example.output"></td> should work

'v-model' directives cannot update the iteration variable itself

I read an article about Renderless Components,it split a component into a presentational component (view part) and a renderless component(logical part) via $scopedSlots property.Here is a simple Tag component. when you press enter , you'll add a new tag
<!DOCTYPE html>
<html>
<head>
<script src="http://vuejs.org/js/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<custom-component v-model="tags">
<div slot-scope="{tags,addTag,newTag}">
<span v-for="tag in tags">
{{tag}}
</span>
<input type="text" #keydown.enter.prevent="addTag" v-model="newTag">
</div>
</custom-component>
</div>
<script>
Vue.component('custom-component',{
props:['value'],
data(){
return {
newTag:''
}
},
methods:{
addTag(){
this.$emit('input',[...this.value,this.newTag])
this.newTag = ''
}
},
render(h){
return this.$scopedSlots.default({
tags:this.value,
addTag:this.addTag,
newTag:this.newTag
})
}
})
new Vue({
el:'#app',
data:{
tags:[
'Test',
'Design'
]
}
})
</script>
</body>
</html>
But,it doesn't work,it seems that the newTag always is ''(empty string), when I use SPA way,
the emulator says "'v-model' directives cannot update the iteration variable 'newTag' itself" ,Here is demo on jsbin
The solution is , as mentioned in the article ,use :value attribute binding, and an #input event binding ,instead of v-model. demo on jsbin
<!DOCTYPE html>
<html>
<head>
<script src="http://vuejs.org/js/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<custom-component v-model="tags">
<div slot-scope="{tags,addTag,inputAttrs,inputEvents}">
<span v-for="tag in tags">
{{tag}}
</span>
<input type="text" v-bind="inputAttrs" v-on="inputEvents">
</div>
</custom-component>
</div>
<script>
Vue.component('custom-component',{
props:['value'],
data(){
return {
newTag:''
}
},
methods:{
addTag(){
this.$emit('input',[...this.value,this.newTag])
this.newTag = ''
}
},
render(h){
return this.$scopedSlots.default({
tags:this.value,
addTag:this.addTag,
inputAttrs:{
value:this.newTag
},
inputEvents:{
input:(e) => {
this.newTag = e.target.value
},
keydown:(e) => {
if(e.keyCode === 13){
e.preventDefault()
this.addTag()
}
}
}
})
}
})
new Vue({
el:'#app',
data:{
tags:[
'Test',
'Design'
]
}
})
</script>
</body>
</html>
I don't know why v-model doesn't work.
EDIT
Questions above have been answered clearly, while I got another question after I read a reference link, and still v-model doesn't work question
<!DOCTYPE html>
<html>
<head>
<script src="http://vuejs.org/js/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<base-test v-slot="sp">
<input type="text" v-model="sp.foo">
<div>{{ sp}}</div>
</base-test>
</div>
<script>
Vue.component('base-test', {
template: `
<div>
<slot :foo="foo"></slot>
</div>
`,
data(){
return{
foo: 'Bar',
}
}
});
// Mount
new Vue({
el: '#app',
});
</script>
</body>
</html>
As we can see, sp is an object. why v-model seems to be not working this time?
My solution was very simple (see v-model="tags[index]"):
Instead of doing this:
<template v-for="tag in tags">
<TagView :key="tag.key" v-model="tag" />
</template>
You should do this:
<template v-for="(tag, index) in tags">
<TagView :key="tag.key" v-model="tags[index]" />
</template>
The reason is you cannot pass iterated object tag into v-model for modifications. Please find more info about this: Iterating a list of objects with foreach
Consider the following two JavaScript examples:
for (let value of array) {
value = 10
}
function (value) {
value = 10
}
In both cases trying to assign 10 to value will only have an effect locally, it won't have any impact beyond the local scope. The caller, for example, would not be affected by the change.
Now consider these two examples where an object is used instead, where the object is of the form { value: 9 }:
for (let valueWrapper of array) {
valueWrapper.value = 10
}
function (valueWrapper) {
valueWrapper.value = 10
}
In this case the change is not limited to the local scope as we're updating objects. External code, such as the caller of the function, would also be impacted by this change to the value property as it can see the same object.
These examples are equivalent to trying to update a value using v-model in a variety of cases. The first two examples are equivalent to:
<template v-for="value in array">
<input v-model="value">
</template>
and:
<template v-slot="{ value }">
<input v-model="value">
</template>
The arguments passed to v-slot can very much be thought of as analogous to function parameters. Neither the loop nor the scoped slot would work as desired, exactly the same as they don't for their pure JavaScript equivalents.
However, the latter two of my four examples would be equivalent to:
<template v-for="valueWrapper in array">
<input v-model="valueWrapper.value">
</template>
and:
<template v-slot="{ valueWrapper }">
<input v-model="valueWrapper.value">
</template>
These should work fine as they are updating a property on an object.
However, to go back to the original question, it's important that we're binding the appropriate object. In this case we would need to bind the newTag property of the component. Copying that property to another object wouldn't work either as v-model would just be updating an irrelevant object.
I think we shouldn't modify the passed data to a slot, pretty much like component props. However, I think it could be a bug.
1st approach
The v-model directive works using a nested field in the passed data to the slot.
<!DOCTYPE html>
<html>
<head>
<script src="http://vuejs.org/js/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<custom-component v-model="tags">
<div slot-scope="{tags,addTag,input}">
<span v-for="tag in tags">
{{tag}}
</span>
<input type="text" #keydown.enter.prevent="addTag" v-model="input.value">
</div>
</custom-component>
</div>
</body>
</html>
Vue.component('custom-component',{
props:['value'],
data(){
return {
input: {
value: ''
}
}
},
methods:{
addTag(){
this.$emit('input',[...this.value,this.input.value])
console.log([...this.value,this.input.value])
this.input.value = ''
}
},
render(h){
return this.$scopedSlots.default({
tags:this.value,
addTag:this.addTag,
input:this.input
})
}
})
new Vue({
el:'#app',
data:{
tags:[
'Test',
'Design'
]
}
})
2nd approach
Use the input event to get the input value attribute directly
<!DOCTYPE html>
<html>
<head>
<script src="http://vuejs.org/js/vue.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<custom-component v-model="tags">
<div slot-scope="{tags,addTag}">
<span v-for="tag in tags">
{{tag}}
</span>
<input type="text" #keydown.enter.prevent="addTag">
</div>
</custom-component>
</div>
</body>
</html>
Vue.component('custom-component',{
props:['value'],
data(){
return {
newTag:''
}
},
methods:{
addTag(evt){
console.log(evt.target.value)
this.$emit('input',[...this.value, evt.target.value])
evt.target.value = ''
}
},
render(h){
return this.$scopedSlots.default({
tags:this.value,
addTag:this.addTag,
newTag:this.newTag
})
}
})
new Vue({
el:'#app',
data:{
tags:[
'Test',
'Design'
]
}
})
You can also check these related issues:
StackOverflow
Using v-model inside scoped slots
Issues and Forum
https://forum.vuejs.org/t/v-model-and-slots/17616
https://github.com/vuejs/vue/issues/9726
Please use Ref of Vuejs Object for you solution, here use refence and testing for me, use tailwind, vue3, as a standalone component.
remember to rate this helps me a lot
// import function ref extiende of object
// importamos la funcion ref que extiende objeto vue
import { ref } from 'vue';
export default {
data() {
return {
// create refence with object for change
// creamos una referencia con los objetos que van a cambiar
fruits: ref({
oranges: {
name: 'naranjas',
value: false
},
apple: {
name: 'manzanas',
value: false
},
pear: {
name: 'peras',
value: false
}
})
};
}
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<template>
<div>
<!-- use tailwindcss vue -->
<div class="w-2xl bg-blue-50 text-gray-400 font-bold">
<!-- iteration is here -->
<div v-for="fruit in fruits" :key="fruit">
<label for="">{{ fruit.name }}</label>
<!-- this can be a component example: #headlessui/vue-->
<input v-model="fruit.value" type="checkbox">
</div>
</div>
<div class="w-2xl bg-blue-50 text-gray-400 font-bold">
{{fruits}}
</div>
</div>
</template>
I had tried many ways to change the v-model but nothing just by referencing the javascript object

How do I render a data property?

I have this super basic starting point:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" type="text/css" href="Content/dictionary.css" />
<script src="Scripts/kuroshiro.min.js"></script>
</head>
<body>
<div id="searchResultsVue">
<table>
<tr v-for="r in results">
{{ r.Result.ent_seq }}
</tr>
</table>
</div>
<script src="https://vuejs.org/js/vue.js"></script>
<script>
var searchResultsVue = new Vue({
el: '#searchResultsVue',
data: { results: [{ Result: { ent_seq: 'asd' } }] }
});
</script>
</body>
</html>
but I get
[Vue warn]: Property or method "r" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I don't understand
This is an issue with HTML's lenient rendering practices. When it's building the Dom elements for a table it's expecting a certain structure and anything deviating from that structure is pushed outside the definitions.
And so
<table>
<tr v-for="r in results">
{{ r.Result.ent_seq }}
</tr>
</table>
Acts like
<table>
<tr v-for="r in results">
</tr>
</table>
{{ r.Result.ent_seq }}
The Error is then that it is seeing the call to the loop variable outside the loop.
As seen in this fiddle Adding a table definition tag around your code stops it from being pushed.
You need to fix your markup. tr needs td as its child to work properly.
<tr v-for="r in results">
<td>{{ r.Result.ent_seq }}</td>
</tr>
You have to use the td tag inside tr.
It seems there is something special about table-rows
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div id="searchResultsVue">
<table>
<tr v-for="r in results">
<td>{{ r.Result.ent_seq }}</td>
</tr>
</table>
</div>
<script src="https://vuejs.org/js/vue.js"></script>
<script>
var searchResultsVue = new Vue({
el: '#searchResultsVue',
data: { results: [{ Result: { ent_seq: 'asd' } }] }
});
</script>
</body>
</html>

Templates in Ractive.js

I am new to Ractive.js and just playing around with it. I am trying to build a simple app, but strangely, it's not working on my pc and I am getting an error in console "Could not find template element with id #template", where #template refers to the id assigned to script tag. Nothing gets rendered on my browser. However, the same code is running fine in JSFiddle. Can someone explain what is happening here?
Here is the fiddle.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Get Weather Details</title>
<meta name="description" content="Get weather for your city">
<link href="https://fonts.googleapis.com/css?family=Arimo" rel="stylesheet">
<link rel="stylesheet" href="weather.css">
<script src='https://cdn.ractivejs.org/latest/ractive.js'></script>
<script src="weather.js"></script>
</head>
<body>
<div id="main" class="page-container">
<script id='template' type='text/ractive'>
<div class="weather-widget">
<div class="input-group">
<label>City Name</label>
<input type="text" class="form-control" placeholder="Enter City Name" />
</div>
<div class="input-group">
<label>Recent Searches</label>
<select>
<option>Select from recent searches</option>
</select>
</div>
<div class="weather-details">
<table>
<tbody>
<tr>
<th>Wind</th>
<td>{{country}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</script>
</div>
</body>
</html>
Code in weather.js:
var ractive = new Ractive({
el: '#main',
template: '#template',
data: {
country: 'London'
}
});
At the time weather.js executes, the element hasn't yet been created. You could either move the <script src='weather.js'></script> tag to the bottom of the page (inside <body>, after the topmost <div> containing the template element the script refers to), or add a defer attribute: <script defer src='weather.js'></script>.

My AngularJS code doesn't work

I am learning AngularJS and ended up with the following code for ToDoList basic app. I viewed it in a browser it didn't work. I am new to the Angular and mightn't get obvious things, so I thought if my app name is
todoApp
Then I should put
$scope.todoApp
instead of
$scope.todo
but turned out that's not an issue.
<!DOCTYPE html>
<html ng-app="todoApp">
<head>
<title>To DO List</title>
<link href="bootstrap.css" rel="stylesheet">
<link href="bootstrap-theme.css" rel="stylesheet>
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript">
var model = {
user: "Adam",
items: [{ action: "Buy flowers", done: false },
{ action: "Get Shoes", done: false },
{ action: "Collect Tickets", done: true },
{ action: "Call Joe", done: false }]
};
var todoApp = angular.module("todoApp", []);
todoApp.controller("ToDoCtrl", function($scope) {
$scope.todo = model;
});
</script>
</head>
<body ng-controller="ToDoCtrl">
<div class="page-header">
<h1>
{{todo.user}}'s To Do List
<span class="label label-default">{{todo.items.length}}</span>
</h1>
</div>
<div class="panel">
<div class="input-group">
<input class="form-control" />
<span class="input-group-btn">
<button class="btn btn-default">Add</button>
</span>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Description</th>
<th>Done</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in todo.items">
<td>{{item.action}}</td>
<td><input type="checkbox" ng-model="item.done" /></td>
<td>{{item.done}}</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
That's what I get in a browser..
And that's what I guess I am supposed to get...
Why it doesn't work?
Your HTML getting invalid because you are missing " in link tag's rel attribute. Here you are missing:
<link href="bootstrap-theme.css" rel="stylesheet>
^ ==> missing "
Working DEMO /* updated css */
Have a look at Invalid HTML in DEMO. Here you can see after link tag HTML is colored green.

Categories