This is the JSON response we are getting. Missing some very simple VUEJS logic to meet our requirements.
If the JSON object is empty we need to show the DIV accordingly. But, when I try with length function it's not working. Can anyone help to solve this issue?
{
"moduleInfo": {
"moduleType": "LONG"
},
"FlightElements": {
"modulenames": {
"Ele1": "Flight Parts",
"Ele2": "Flight Wings"
}
}
}
<!-- If moduleType is LONG and "Modulesnames" are available -->
<div class="display-container" v-if='moduleType=="LONG" && !FlightElements.modulenames.length'>
<p>Display Modules</p>
<div>
<!-- If moduleType is LONG and "Modulesnames" is empty -->
<div class="display-container" v-if='moduleType=="LONG" && FlightElements.modulenames.length'>
<p>Display Empty Modules</p>
<div>
Try to check if object property(object) is empty:
new Vue({
el: "#demo",
data() {
return {
mod:{
"moduleInfo": {"moduleType": "LONG"},
"FlightElements": {
"modulenames": {"Ele1": "Flight Parts","Ele2": "Flight Wings"}
}
},
mod1:{
"moduleInfo": {"moduleType": "LONG"},
"FlightElements": {
"modulenames": {}
}
}
}
}
})
Object.entries(objectToCheck).length === 0
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo" style="display: flex; gap: 1em;">
<div style="background: green;">
<div class="display-container" v-if='mod.moduleInfo.moduleType=="LONG" && Object.entries(mod.FlightElements.modulenames).length !== 0'>
<p>Display Modules</p>
</div>
<div class="display-container" v-if='mod.moduleInfo.moduleType=="LONG" && Object.entries(mod.FlightElements.modulenames).length === 0 '>
<p>Display Empty Modules</p>
</div>
</div>
<div style="background: red;">
<div class="display-container" v-if='mod1.moduleInfo.moduleType=="LONG" && Object.entries(mod1.FlightElements.modulenames).length !== 0'>
<p>Display Modules</p>
</div>
<div class="display-container" v-if='mod1.moduleInfo.moduleType=="LONG" && Object.entries(mod1.FlightElements.modulenames).length === 0 '>
<p>Display Empty Modules</p>
</div>
</div>
</div>
Try to use lodash _.isEmpty(value)
<!-- If moduleType is LONG and "Modulesnames" is empty -->
<div class="display-container" v-if='moduleType=="LONG" && _.isEmpty(FlightElements.modulenames)'>
<p>Display Empty Modules</p>
<div>
A few notes.
JSONs in Javascript don't have a length property. Calling json.length will always return undefined. If you had an array of JSONs, however, then you would be able to call .length on it.
const json = { "key": "example" }
const array = [ { "key": "example1" }, { "key": "example2" } ]
console.log(json.length)
console.log(array.length)
// #=> undefined
// #=> 2
If you wanted the length of a JSON via it's keys, you'd have to count how many keys it has. Check this answer for more details on that.
Second, you shouldn't necessarily need lodash as makbeth's comment indicates, you should be able to simply call the modulenames key. It will return false if it has no contents.
Take a JSON like below:
const json = {
"user": { }
}
If you call json in an if, it will return true. Calling json.user, however, will return false.
if (json.user) {
console.log("Hello!")
} else {
console.log("Goodbye!")
}
// #=> Goodbye!
It seems to me, assuming that the code you posted is what's actually being used, that you're simply not calling the correct JSON keys here.
You first need to save the JSON you're getting from the API into a variable, and then you can access those keys properly.
Something like this:
<template>
<div v-if="data.moduleType === 'LONG' && data.FlightElements.moduleNames">
<p>Display Modules</p>
</div>
<div v-if="data.moduleType === 'LONG' && !data.FlightElements.moduleNames">
<p>Display Empty Modules</p>
</div>
</template>
<script>
export default {
data() {
return {
data = {}
}
},
methods: {
async fetchData() {
this.data = callApi();
}
}
}
</script>
Related
I have a sidebar that you can see below:
<template>
<section>
<div class="sidebar">
<router-link v-for="(element, index) in sidebar" :key="index" :to="{ name: routes[index] }" :class='{active : (index==currentIndex) }'>{{ element }}</router-link>
</div>
<div class="sidebar-content">
<div v-if="currentIndex === 0">
Profile
</div>
<div v-if="currentIndex === 1">
Meine Tickets
</div>
</div>
</section>
</template>
<script>
export default {
mounted() {
EventBus.$on(GENERAL_APP_CONSTANTS.Events.CheckAuthentication, () => {
this.authenticated = authHelper.validAuthentication();
});
console.log()
this.checkRouter();
},
data(){
return {
currentIndex:0,
isActive: false,
sidebar: ["Profile", "Meine Tickets"],
routes: ["profile", "my-tickets"],
authenticated: authHelper.validAuthentication(),
}
},
computed: {
getUser() {
return this.$store.state.user;
},
},
methods: {
changeSidebar(index) {
this.object = this.sidebar[index].products;
this.currentIndex=index;
},
checkRouter() {
let router = this.$router.currentRoute.name;
console.log(router);
if(router == 'profile') {
this.currentIndex = 0;
} else if(router == 'my-tickets') {
this.currentIndex = 1;
}
},
},
}
</script>
So when the link is clicked in the sidebar, the route is being changed to 'http://.../my-account/profile' or 'http://.../my-account/my-tickets'. But the problem is currentIndex doesn't change therefore, the content doesn't change and also I cannot add active class into the links. So how do you think I can change the currentIndex, according to the routes. Should I fire an event, could you help me with this also because I dont know how to do it in Vue. I tried to write a function like checkRouter() but it didn't work out. Why do you think it is happening? All solutions will be appreciated.
So if I understand correctly, you want currentIndex to be a value that's based on the current active route? You could create it as a computed property:
currentIndex: function(){
let route = this.$router.currentRoute.name;
if(router == 'profile') {
return 0;
} else if(router == 'my-tickets') {
return 1;
}
}
I think you could leverage Vue's reactivity a lot more than you are doing now, there's no need for multiple copies of the same element, you can just have the properties be reactive.
<div class="sidebar-content">
{{ sidebar[currentIndex] }}
</div>
Also, you might consider having object be a computed property, something like this:
computed: {
getUser() {
return this.$store.state.user;
},
object() {
return this.sidebar[currentIndex].products;
}
},
Just use this.$route inside of any component template. Docs .You can do it simple without your custom logic checkRouter() currentIndex. See simple example:
<div class="sidebar-content">
<div v-if="$route.name === 'profile'">
Profile
</div>
<div v-if="$route.name === 'my-tickets'">
Meine Tickets
</div>
</div>
I have created an app that requests from an API the data and creates flexboxes. Now I added a search box and I would like to filter the articles by their contact and/or title.
I've also created a computed property to filter the returned list of items but when I replace in line 11 the paginated('items') with paginated('filteredArticles') that returns nothing.
What did I do wrong?
<template>
<div id="app">
<div class="search-wrapper">
<input type="text"
class="search-bar"
v-model="search"
placeholder="Search in the articles"/>
</div>
<paginate ref="paginator" class="flex-container" name="items" :list="items">
<li v-for="(item, index) in paginated('items')" :key="index" class="flex-item">
<div id="image"><img :src="item.image && item.image.file" /></div>
<div id="date">{{ item.pub_date }}</div>
<div id="title"> {{ item.title }}</div>
<div class="article">{{item.details_en}}</div>
</li>
</paginate>
<paginate-links for="items" :limit="2" :show-step-links="true"></paginate-links>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
items: [],
paginate: ["items"],
search:'',
};
},
created() {
this.loadPressRelease();
},
methods: {
loadPressRelease() {
axios
.get(`https://zbeta2.mykuwaitnet.net/backend/en/api/v2/media-center/press-release/?page_size=61&type=5`)
.then((response) => {
this.items = response.data.results;
});
},
},
computed:{
filteredArticles() {
return this.items.filter(item=>item.includes(this.search))
}
}
};
</script>
You need fields you want to search and connvert search string and fields with toLowerCase() or toUpperCase():
computed : {
filteredArticles() {
if (!this.search) return this.items
return this.items.filter(item => {
return (item.title.toLowerCase().includes(this.search.toLowerCase()) || item.contact.toLowerCase().includes(this.search.toLowerCase()));
})
}
}
Your computed doesn't seem correct. Since items is an array of objects, you'd need to do this:
filteredArticles() {
if (!this.search) {
return this.items;
}
return this.items.filter(item => {
return item.title.includes(this.search);
})
}
Note that this will only search the title field, and it's case sensitive.
I am trying to create a form with vuelidate. In one field I would like to check if the name is taken or not. I have some async methods to get names and ids from the server and assigning them to arrays, and I have a custom validator that checks if the name exists, either by checking the includes in the array, or by checking a computed value that already checks the array.
Neither of the methods seems to work however. If I check the array, its seems to be empty since it always returns false (even tho the array has values according to the vue tools in the browser). If I check the the computed value, I get an error with undefined.
So my question is, what is the simplest why to validate whether a value exists, and why isn't my current code wokring?
<template>
<div>
<form class="needs-validation" #submit.prevent="submitForm" method="post">
<div class="form-group row">
<label class="col-sm-2 col-form-label">Name:</label>
<div class="col-sm-10">
<input
type="text"
class="form-control"
:class="{ 'is-invalid': $v.form.name.$error }"
id="name"
placeholder="enter name"
v-model="form.name">
<span
class="text-danger"
v-if="!$v.form.name.required && $v.form.name.$dirty">name is required</span>
<span
class="text-danger"
v-if="!$v.form.name.isUnique && $v.form.name.$dirty">name not unique</span>
</div>
</div>
</form>
</div>
</template>
<script>
import { required } from 'vuelidate/lib/validators'
const isUnique = (value, vm) => {
if (value === '') return true
if (vm.names) return !vm.names.includes(value)
return true
}
export default {
data() {
return {
form: {
name: ""
}
ids: [],
names: []
}
}
validations: {
form: {
name: {
required,
isUnique
}
}
}
async created() {
try {
const response = await this.$http.get('/get_data/?fields=id,name')
var array_id = []
var array_name = []
for (var data of response.data) {
array_id.push(data['id'])
array_name.push(data['name'])
}
this.ids = array_id
this.names = array_name
}
}
}
<script>
Seem like you miss the right way to write down methods
form: {
name: {
required,
isUnique : this.isUnique
}
}
},
methods: {
isUnique = (value, vm) => {
if (value === '') return true
if (vm.names) return !vm.names.includes(value)
return true
}
}
My vue component like this :
<template>
<div class="row">
<div class="col-md-3" v-for="item in items">
...
</div>
</div>
</template>
<script>
export default {
...
computed: {
items() {
...
}
},
...
}
</script>
If the loop complete, I want to call a statement or method
So the statement is executed when the loop completes
How can I do it?
Update :
From Kira San answer, I try like this :
<template>
<div class="row">
<div class="col-md-3" v-for="(item, key) in items" v-for-callback="{key: key, array: items, callback: callback}">
...
</div>
</div>
</template>
<script>
export default {
...
computed: {
items() {
const n = ...
return n
}
},
directives: {
forCallback(el, binding) {
let element = binding.value
if (element.key == element.array.length - 1)
if (typeof element.callback === 'function') {
element.callback()
}
}
},
methods: {
callback() {
console.log('v-for loop finished')
}
}
}
</script>
The console log not display
My items is object
If do console.log(n) in items, the result like this :
Look at this example.
new Vue({
el: '#app',
computed: {
items() {
return {item1: 'value1', item2: 'value2'}
}
},
methods: {
callback() {
console.log('v-for loop finished')
}
},
directives: {
forCallback(el, binding) {
let element = binding.value
var key = element.key
var len = 0
if (Array.isArray(element.array)) {
len = element.array.length
}
else if (typeof element.array === 'object') {
var keys = Object.keys(element.array)
key = keys.indexOf(key)
len = keys.length
}
if (key == len - 1) {
if (typeof element.callback === 'function') {
element.callback()
}
}
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<div class="row">
<div class="col-md-3" v-for="(item, key) in items" v-for-callback="{key: key, array: items, callback: callback}">
...
</div>
</div>
</div>
You can accomplish that with a directive.
Example usage:
<div class="col-md-3" v-for="(item, key) in items" v-for-callback="{key: key, array: items, callback: callback}">
<!--content-->
</div>
Create a new directive for-callback which will keep track of the elements that was rendered with v-for. It will basically check if the current key is the end of the array. If so, it will execute a callback function that you provide.
Vue.directive('for-callback', function(el, binding) {
let element = binding.value
if (element.key == element.array.length - 1)
if (typeof element.callback === 'function') {
element.callback()
}
})
Or if you don't want to define it globally. Add this to your component options instead:
directives: {
forCallback(el, binding) {
let element = binding.value
if (element.key == element.array.length - 1)
if (typeof element.callback === 'function') {
element.callback()
}
}
}
v-for-callback expects an options object.
{
key: key, // this will contain the item key that was generated with `v-for`
array: items, // the actual `v-for` array
callback: callback // your callback function (in this example, it's defined in the component methods
}
Then in your component options:
methods: {
callback() {
console.log('v-for loop finished')
}
}
You can do with an if inside the loop, like this:
<template>
<div class="row">
<div class="col-md-3" v-for="(item, index in items)">
<div v-if="index === items.length -1">
Display what ever you want here
<\div>
</div>
</div>
</template>
Or if you want to do something in js then call a function when the same condition is true, like this:
<div class="col-md-3" v-for="(item, index in items)">
<div v-if="index === items.length -1 && lastIteration()">
Display what ever you want here
<\div>
</div>
methods: {
lastIteration() {
alert("last");
}
}
I didn't test it but the ideea is good and it should work. Hope to answer your question :)
Classic scenario: I want to display a list, but when it's empty I want to display "No data".
The fact that it is somewhat complicated to do something I would expect to be simple makes me think I'm probably doing it wrong.
Here is my current solution.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script>
<div id="element">
<div v-if="empty">No item in inventory</div>
<div v-for="(index, item) in inventory">
{{item.key}}<button onclick="remove('{{index}}')">remove</button>
</div>
</div>
<script type="text/javascript">
"use strict";
var vm;
$(function() {
vm = new Vue({
el: '#element',
data: {
inventory: {"id1" : {"key" : "val1"}, "id2" : {"key" : "val2"}},
empty: false
},
watch: {
inventory: function() {
vm.empty = $.isEmptyObject(vm.inventory);
}
}
});
});
function remove(key) {
Vue.delete(vm.inventory, key);
}
</script>
Is there a better solution than this?
You can just use length of inventory in v-if, like following:
<div id="element">
<div v-if="!inventory.length">No item in inventory</div>
<div v-for="(index, item) in inventory">
{{item.key}}
<button v-on:click="remove(index)">remove</button>
</div>
</div>
Given that inventory variable is a hash and not an array, you can use any of the following to find it is empty and use that in v-if:
ECMA 5+:
Object.keys(inventory).length === 0
Pre-ECMA 5:
function isEmpty(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return JSON.stringify(obj) === JSON.stringify({});
}
As you are already using jquery, you can also do:
jQuery.isEmptyObject({}); // true
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script>
<div id="element">
<div v-if="isEmpty">No item in inventory</div>
<div v-for="(index, item) in inventory">
{{item.key}}<button #click="remove(index)">remove</button>
</div>
</div>
<script type="text/javascript">
"use strict";
var vm;
$(function() {
vm = new Vue({
el: '#element',
data: {
inventory: {"id1" : {"key" : "val1"}, "id2" : {"key" : "val2"}},
empty: false
},
methods: {
remove: function(index) {
Vue.delete(this.inventory, index);
}
},
computed: {
isEmpty: function () {
return jQuery.isEmptyObject(this.inventory)
}
}
});
});
</script>
After searching for a few minutes, I come up with a better solution to check whether the object is empty or not.
Object.keys( YOUR_OBJECT ).length == 0 // Check if it's empty.
It will return 0 if it's empty. If you want to check whether the array is empty or not you can definitely go for this solution below,
YOUR_ARRAY.length == 0 // Check if it's empty.
In my case it's an object, not array. So, it may also help. Happy coding :)
If it's an array, you can use
v-if="!array.length"
Worked Great for me...
v-if="array.length == 0"
<div v-if="Object.length > 0 ">
//Write your logic here
</div>
<div v-else>
No Records found
</div>
You can just use this
v-if="(YOUR_OBJECT).total == 0"