Assing uploaded file value to input field multiple times using vue.js - vue.js

Assign filename to input field on upload and then show success message working fine,but when i try to delete file and upload again it's not working!
new Vue({
el: "#app",
data() {
return {
form: {
message: '',
fileurl: ''
},
loading: false
}
},
methods: {
uploadImage(event) {
this.form.fileurl = 'uploaded!'
},
deleteFile(furl) {
this.form.fileurl = ''
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
span {
color: red;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h3>
File Upload / remove Demo
</h3>
<hr />
<div class="custom-file attach_file" v-show="!form.fileurl">
<input type="file" id="file" name="file" #change="uploadImage($event)">
<input type="text" v-model="form.fileurl">
</div>
<p v-if="form.fileurl"> {{ form.fileurl }} <span #click="deleteFile(form.fileurl)">Delete</span></p>
</div>
I am not getting any console error as well.
This is what i have tried so far.
Can you guys please have a look at this!

The problem is that #change is not trigger when you choose the same file. The simplest solution is reset value of input when you click delete
this.$refs.fileToUpload.value = '';
new Vue({
el: "#app",
data() {
return {
form: {
message: '',
fileurl: ''
},
loading: false
}
},
methods: {
uploadImage(event) {
this.form.fileurl = 'uploaded!'
},
deleteFile(furl) {
this.form.fileurl = ''
this.$refs.fileToUpload.value = '';
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
span {
color: red;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h3>
File Upload / remove Demo
</h3>
<hr />
<div class="custom-file attach_file" v-show="!form.fileurl">
<input type="file" id="file" name="file" class="custom-file-input" #change="uploadImage($event)" ref="fileToUpload">
<input type="text" v-model="form.fileurl">
</div>
<p v-if="form.fileurl"> {{ form.fileurl }} <span #click="deleteFile(form.fileurl)">Delete</span></p>
</div>

You need to clear you input field first.
Update your upload method with this.
uploadImage(event) {
this.form.fileurl = 'uploaded!'
this.success = false
this.$nextTick(() => {
this.success = true
})
Have a look at this working demo
https://jsfiddle.net/asimshahzad/8c9pcfys/

Related

VueJS complex v-if

I want to show a button ONLY if two conditions are met. First I try v-if with only one condition at a time:
v-if="editMode"
v-if="$can('customersdelete')"
using only one condition at a time, the button is display, so i think both conditions are true. If i use:
v-if="editMode && $can('customersdelete')"
The button isn't display. $can is a mixin, to validate user has permission to do something.
<script>
export default {
methods: {
$can(permissionName) {
return Permissions.indexOf(permissionName) !== -1;
},
},
};
</script>
I don't know why this is not working...
Apparently, any subsequent v-if is ignored and only the first one is taken into account, as demonstrated by the following example:
Vue.config.devtools = false;
Vue.config.productionTip = false;
const Permissions = ['customersdelete'];
new Vue({
el: '#app',
data() {
return { editMode: true };
},
methods: {
$can(permissionName) {
return Permissions.indexOf(permissionName) !== -1;
},
},
})
code {
background-color: #f5f5f5;
border: 1px solid #eee;
padding: 2px 5px;
color: red;
border-radius: 3px;
display: inline-block;
}
div {
margin-top: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<label><input type="checkbox" v-model="editMode">editMode</label><br><br>
<div>$can('customersdelete') => <code v-text="$can('customersdelete')"></code></div>
<div>editMode => <code v-text="editMode"></code></div>
<hr />
<div v-if="$can('customersdelete')">
<code>v-if="$can('customersdelete')"</code>
</div>
<div v-if="editMode">
<code v-if="editMode">v-if="editMode"</code>
</div>
<div v-if="editMode && $can('customersdelete')">
<code>v-if="editMode && $can('customersdelete')"</code>
</div>
<div v-if="editMode"
v-if="$can('customersdelete')">
<code>v-if="editMode" v-if="$can('customersdelete')"</code>
</div>
<div v-if="$can('customersdelete')"
v-if="editMode">
<code>v-if="$can('customersdelete')" v-if="editMode"</code>
</div>
</div>
I would suggest use function and then use in v-if.
example
v-if="isButtonShow()"
and below is the method portion:
methods:{
isButtonShow(){
if(this.editMode){
if(this.$can('customersdelete'){
return true;
}
}
return false;
}
}

How to set aspect ratio to vue cropperjs

From vue-cropper.js example I can't find way to set aspect ratio like on jquery version in options.
In jquery version you can pass options to element where is crop used, on this one I can't find answer..
<div id="app">
<h2 style="margin: 0;">Vue CropperJS</h2>
<hr/>
<input type="file" name="image" accept="image/*"
style="font-size: 1.2em; padding: 10px 0;"
#change="setImage" />
<br/>
<div style="width: 400px; height:300px; border: 1px solid gray; display: inline-block;">
<vue-cropper
ref='cropper'
:guides="true"
:view-mode="2"
drag-mode="crop"
:auto-crop-area="0.5"
:min-container-width="250"
:min-container-height="180"
:background="true"
:rotatable="true"
:src="imgSrc"
alt="Source Image"
:img-style="{ 'width': '400px', 'height': '300px' }">
</vue-cropper>
</div>
<img :src="cropImg" style="width: 200px; height: 150px; border: 1px solid gray" alt="Cropped Image" />
<br/>
<br />
<button #click="cropImage" v-if="imgSrc != ''" style="margin-right: 40px;">Crop</button>
<button #click="rotate" v-if="imgSrc != ''">Rotate</button>
</div>
</template>
<script>
import VueCropper from 'vue-cropperjs';
export default {
components: {
VueCropper,
},
data() {
return {
imgSrc: '',
cropImg: '',
};
},
methods: {
setImage(e) {
const file = e.target.files[0];
if (!file.type.includes('image/')) {
alert('Please select an image file');
return;
}
if (typeof FileReader === 'function') {
const reader = new FileReader();
reader.onload = (event) => {
this.imgSrc = event.target.result;
// rebuild cropperjs with the updated source
this.$refs.cropper.replace(event.target.result);
};
reader.readAsDataURL(file);
} else {
alert('Sorry, FileReader API not supported');
}
},
cropImage() {
// get image data for post processing, e.g. upload or setting image src
this.cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL();
},
rotate() {
// guess what this does :)
this.$refs.cropper.rotate(90);
},
},
};
</script>
Nothing mentioned on:
https://github.com/Agontuk/vue-cropperjs..
I need to set aspect ratio to 1:1;
Any help?
Thanks
I added this to my vue component:
:aspectRatio="1/1"
:initialAspectRatio="1/1"
And it works as expected

Toggle form inside v-for using vue.js

How can i toggle form inside v-for loop,I have a form inside v-for which i want to display (toggle) on click.
But when i click all the form inside the v-for gets toggled.
Secondly is it better approach to keep the form inside the loop,when have large amount of data inside loop or load it as a separate component.
This is what i am trying to do.
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript"
},
{
text: "Learn Vue"
},
{
text: "Play around in JSFiddle"
},
{
text: "Build something awesome"
}
],
show: ''
},
methods: {
toggle: function(todo) {
this.show = !this.show
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo,key) in todos">
<p>
{{ key+1 }} - {{ todo.text}} <span #click="toggle(todo)"><b>Contact</b></span>
<div v-if="show">
<hr />
<p>
<label>Message</label>
<input type="text">
</p>
<hr />
</div>
</p>
</li>
</ol>
</div>
There is only 1 reactive variable show. Setting it to true while all form is using v-if="show", will show everything.
You can set show to something that each form uniquely have. For example, its text, and perform a v-if using its text.
demo: https://jsfiddle.net/jacobgoh101/umaszo9c/
change v-if="show" to v-if="show === todo.text"
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo,key) in todos">
<p>
{{ key+1 }} - {{ todo.text}} <span #click="toggle(todo)"><b>Contact</b></span>
<div v-if="show === todo.text">
<hr />
<p>
<label>Message</label>
<input type="text">
</p>
<hr />
</div>
</p>
</li>
</ol>
</div>
change toggle method
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript"
},
{
text: "Learn Vue"
},
{
text: "Play around in JSFiddle"
},
{
text: "Build something awesome"
}
],
show: ''
},
methods: {
toggle: function(todo) {
if (this.show === todo.text)
this.show = false
else
this.show = todo.text
}
}
})
property "show" should be a prop of todo,not prop of data
new Vue({
el: "#app",
data: {
todos: [{
text: "Learn JavaScript"
},
{
text: "Learn Vue"
},
{
text: "Play around in JSFiddle"
},
{
text: "Build something awesome"
}
].map(o=>({...o,show:false}))
},
methods: {
toggle: function(todo) {
todo.show = !todo.show
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo,key) in todos">
<p>
{{ key+1 }} - {{ todo.text}} <span #click="toggle(todo)"><b>Contact</b></span>
<div v-if="todo.show">
<hr />
<p>
<label>Message</label>
<input type="text">
</p>
<hr />
</div>
</p>
</li>
</ol>
</div>

Update fields inside v-for loop using vue.js

How can i update multiple fields inside the v-for loop using Vue.js,as i have fields which i iterate using v-for loop and want to update any field.
besides this i am able to delete and add new fields successfully.
Here what i am trying to do
Note:i can update fields with static values,but needs to update it with real values.
new Vue({
el: "#app",
data: {
users: [{
name: 'test',
phone: '1234'
}, ],
form: {
name: '',
phone: ''
},
show_link: true,
IsOn: false,
},
methods: {
addRow: function() {
this.IsOn = true
this.show_link = false
},
removeSelected(index) {
this.users.splice(index, 1);
},
saveForm() {
this.users.push({
name: this.form.name,
phone: this.form.phone
})
this.form.name = '';
this.form.phone = '';
},
updateForm(key) {
this.users[key].name = 'john doe', // should be the entered value
this.users[key].phone = '0000' // should be the entered value
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(user,key) in users">
<label>
<input type="text" v-model="user.name">
<input type="text" v-model="user.phone">
<input type="button" value="Update" #click="updateForm(key)">
<input type="button" value="Delete" #click="removeSelected(key)">
</label>
</li>
</ol>
<a v-if="show_link" #click="addRow">Add Another</a>
<div v-if="IsOn">
<hr />
<input type="text" name="name" v-model="form.name">
<input type="text" name="name" v-model="form.phone">
<input type="button" value="Save" #click="saveForm">
</div>
</div>

Vue.js 2.0 data binding issue - one data field's change will trigger the other data fields' filter

Following is the code, I define a my data in Vue as below:
{
result: {
name: 'user A',
age: 20,
male: true
},
showDetails: true
}
There are several filter/computed/methods in the view will do the result formatting.
The problem is even I just change the value of the showDetailds, the methods and filters defined for the result will be triggered as well.
May I know any issue in my code ? Or it's the natural of Vue.js (I don't believe.) ?
function countTheCall(key) {
let counter = document.getElementById(key)
if (!counter) {
let container = document.getElementById('statList')
var item = document.createElement('li')
item.innerHTML = `${key}: <span id='${key}'>0</span>`
container.appendChild(item)
counter = document.getElementById(key)
}
counter.innerText = _.parseInt(counter.innerText) + 1
}
var vm = new Vue({
el: '#app',
data() {
return {
result: {
name: 'user A',
age: 20,
male: true
},
showDetails: true
}
},
methods: {
countTheCall: function(key) {
let counter = document.getElementById(key)
counter.innerText = _.parseInt(counter.innerText) + 1
},
trimValue: function(value) {
console.log('Invoke method - trimValue.')
countTheCall('methodCalls of trimValue')
return _.trim(value)
},
getValFromResult: function(key) {
console.log('Invoke method - getValFromResult.')
countTheCall('methodCalls of getValFromResult')
return _.get(this.result, key, 'null')
}
},
computed: {
displayName: function() {
console.log('Invoke computed value - computedVal.')
countTheCall('computedCalls of displayName')
return `${this.result.name} (${this.result.age})`
}
},
filters: {
convertString: function(val) {
countTheCall('filterCalls of convertString')
return _.upperFirst(val)
},
getValFromObject: function(obj, key) {
console.log(`[filter] getValue From Object by ${key}`)
countTheCall('filterCalls of getValFromObject')
return _.get(obj, key, 'null')
},
convertDateString: function(datestr, format = 'MMM DD (ddd), YYYY') {
console.log(`[filter] convertDateString ${datestr}`)
countTheCall('filterCalls of convertDateString')
let m = moment(datestr)
let formattedStr = ''
if (m.isValid()) {
formattedStr = m.format(format)
}
return formattedStr
}
}
})
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
label {
font-style: italic;
color: black;
}
span {
font-weight: bold;
color: darkblue;
}
.callStat {
float: right;
width: 400px;
border: solid darkgrey 2px;
padding: 5px;
margin-right: 40px;
}
<body>
<script type="text/javascript" src="//cdn.bootcss.com/vue/2.2.4/vue.min.js"></script>
<script type="text/javascript" src="//cdn.bootcss.com/moment.js/2.17.1/moment-with-locales.min.js"></script>
<script type="text/javascript" src="//cdn.bootcss.com/lodash.js/4.17.4/lodash.min.js"></script>
<div class="callStat">
<h4>The Stat. of Calls</h4>
<ul id="statList"></ul>
</div>
<div id="app">
<h2>User Profile</h2>
<div>
<label>User Name:</label>
<input type="text" v-model="result.name"></input>
</div>
<div>
<label>Show Details:</label>
<input type="checkbox" id="showDetails" v-model="showDetails"></input>
<label for="showDetails">show detail section?</label>
</div>
<hr/>
<div>
<label>User Name:</label>
<ul>
<li>result.name: <span>{{trimValue(result.name)}}</span></li>
<li>result.unknownFiled: <span>{{result.unknownFiled}}</span></li>
<li>getValFromResult('name'): <span>{{ getValFromResult('name')}} </span></li>
<li>getValFromResult('unknownField'): <span>{{ getValFromResult('unknownField')}} </span></li>
<li>computed Display Name : <span>{{ displayName }} </span></li>
<li>convertString by filter : <span>{{ result.name | convertString }} </span></li>
<li>user birthDay: <span>{{ result.birthDay | convertDateString}}</span></li>
</ul>
</div>
</div>
</body>
This is natural to Vue.
Methods & Filters run on every DOM re-render.
This is why it's best to use Computed whenever possible - these re-run only on change of connected properties.