Convert JQuery function w/each to vanilla JS - javascript

I need some help. I am using a multilingual static site w/JQuery and JSON but I would like to use it w/simple JS. Most part of the code is finished but I cannot resolve the commented part of the JS successfully (w/JQuery it is working well).
var language, translate, jsData;
// Here is the questioned part in JQuery
translate = function(jsdata) {
$('[block]').each(function(index) {
var strTr;
strTr = jsdata[$(this).attr('block')][$(this).attr('txt')];
$(this).html(strTr);
});
};
document.querySelector("a#hu").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "teszt 1",
"body": "Szia Világ!"
},
"2.md": {
"title": "teszt 2",
"body": "Szia Világ megint!"
}
}
translate()
});
document.querySelector("a#en").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "test 1",
"body": "Hello Word!"
},
"2.md": {
"title": "test 2",
"body": "Hello Word again!"
}
}
translate()
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a hrf="#" id="hu">HU</a>
<a hrf="#" id="en">EN</a>
<p block="1.md" txt="title"></p>
<p block="1.md" txt="body"></p>

Please read the comments.
[...document.querySelectorAll("[block]")]
.forEach(block => block.innerHTML = jsData[block.getAttribute("block")][block.getAttribute("txt")]);
let language, jsData;
// Here is the questioned part in JQuery
const translate = () => {
[...document.querySelectorAll("[data-block]")].forEach(
block =>
block.innerHTML = jsData[block.getAttribute("data-block")][block.getAttribute("data-txt")]
);
};
document.querySelector("a#hu").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "teszt 1",
"body": "Szia Világ!"
},
"2.md": {
"title": "teszt 2",
"body": "Szia Világ megint!"
}
}
translate()
});
document.querySelector("a#en").addEventListener("click", (e) => {
// getJson('hu');
jsData = {
"1.md": {
"title": "test 1",
"body": "Hello Word!"
},
"2.md": {
"title": "test 2",
"body": "Hello Word again!"
}
}
translate()
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a hrf="#" id="hu">HU</a>
<a hrf="#" id="en">EN</a>
<p data-block="1.md" data-txt="title"></p>
<p data-block="1.md" data-txt="body"></p>
<p data-block="2.md" data-txt="title"></p>
<p data-block="2.md" data-txt="body"></p>

Related

Computed property throws an error although working

I have a this code that fetches data from a remote server and i have placed it on mounted
var get_one = 'https://api.example.com/rb.php';
axios.get(get_one)
.then(response => {
// console.log(response.data);
var data = response.data;
this.roomsData = data;
this.room_name = response.data.room_name;
this.roomsData.room_photos = response.data.room_photos;
})
.catch(e => {
this.errors.push(e)
});
when i refresh the page and {{roomsData.room_photos}} looks like this
[ { "url": "/var/www/html/uploads/609cd3aaaaefd.jpg" }, { "url": "/var/www/html/uploads/609cd3aaa9dc2.jpg" }, { "url": "/var/www/html/uploads/609cd3aab1252.jpg" }, { "url": "/var/www/html/uploads/609cd3aab147a.jpg" }, { "url": "/var/www/html/uploads/609cd3aab0226.jpg" }, { "url": "/var/www/html/uploads/609cd3aaaf92b.jpg" }, { "url": "/var/www/html/uploads/609cd3aaec480.jpg" }, { "url": "/var/www/html/uploads/609cd3ab3a61b.jpg" }, { "url": "/var/www/html/uploads/609cd3ab432cb.jpg" }, { "url": "/var/www/html/uploads/609cd3ab00b91.jpg" }, { "url": "/var/www/html/uploads/609cd3ab02040.jpg" }, { "url": "/var/www/html/uploads/609cd3ab43f3e.jpg" }, { "url": "/var/www/html/uploads/609cd3ab3a634.jpg" }, { "url": "/var/www/html/uploads/609cd3ab4729f.jpg" }, { "url": "/var/www/html/uploads/609cd3ab47168.jpg" }, { "url": "/var/www/html/uploads/609cd3ab7af65.jpg" }, { "url": "/var/www/html/uploads/609cd3ab7dae1.jpg" }, { "url": "/var/www/html/uploads/609cd3ab8738f.jpg" }, { "url": "/var/www/html/uploads/609cd3ab86f15.jpg" }, { "url": "/var/www/html/uploads/609cd3ab8af48.jpg" }, { "url": "/var/www/html/uploads/609cd3ab95423.jpg" }, { "url": "/var/www/html/uploads/609cd3abbbdf9.jpg" }, { "url": "/var/www/html/uploads/609cd3abc455e.jpg" }, { "url": "/var/www/html/uploads/609cd3abca83e.jpg" }, { "url": "/var/www/html/uploads/609cd3abca0a0.jpg" }, { "url": "/var/www/html/uploads/609cd3abcfa7a.jpg" }, { "url": "/var/www/html/uploads/609cd3abd9d73.jpg" }, { "url": "/var/www/html/uploads/609cd3ac09a00.jpg" }, { "url": "/var/www/html/uploads/609cd3ac1382f.jpg" } ]
This is my computed function
computed: {
image_viewer_data: function () {
let vd = this.roomsData.room_photos
console.log('inside computed',vd)
let modifiedArr = vd.map(function(item){
let url_parts = item.url.split('/')
return 'https://api.example.com/uploads' + '/' + url_parts[url_parts.length - 1]
});
return modifiedArr;
}
},
which produces data in the following format when i {{image_viewer_data}}
[ "https://api.example.com/uploads/609cd3aaaaefd.jpg", "https://api.example.com/uploads/609cd3aaa9dc2.jpg", "https://api.example.com/uploads/609cd3aab1252.jpg", "https://api.example.com/uploads/609cd3aab147a.jpg", "https://api.example.com/uploads/609cd3aab0226.jpg", "https://api.example.com/uploads/609cd3aaaf92b.jpg", "https://api.example.com/uploads/609cd3aaec480.jpg", "https://api.example.com/uploads/609cd3ab3a61b.jpg", "https://api.example.com/uploads/609cd3ab432cb.jpg", "https://api.example.com/uploads/609cd3ab00b91.jpg", "https://api.example.com/uploads/609cd3ab02040.jpg", "https://api.example.com/uploads/609cd3ab43f3e.jpg", "https://api.example.com/uploads/609cd3ab3a634.jpg", "https://api.example.com/uploads/609cd3ab4729f.jpg", "https://api.example.com/uploads/609cd3ab47168.jpg", "https://api.example.com/uploads/609cd3ab7af65.jpg", "https://api.example.com/uploads/609cd3ab7dae1.jpg", "https://api.example.com/uploads/609cd3ab8738f.jpg", "https://api.example.com/uploads/609cd3ab86f15.jpg", "https://api.example.com/uploads/609cd3ab8af48.jpg", "https://api.example.com/uploads/609cd3ab95423.jpg", "https://api.example.com/uploads/609cd3abbbdf9.jpg", "https://api.example.com/uploads/609cd3abc455e.jpg", "https://api.example.com/uploads/609cd3abca83e.jpg", "https://api.example.com/uploads/609cd3abca0a0.jpg", "https://api.example.com/uploads/609cd3abcfa7a.jpg", "https://api.example.com/uploads/609cd3abd9d73.jpg", "https://api.example.com/uploads/609cd3ac09a00.jpg", "https://api.example.com/uploads/609cd3ac1382f.jpg" ]
This is how i am using my computed function
<div class="carousel-item" v-for="(value,index) in image_viewer_data" >
<img class="d-block w-100" :src="value" :id="index" >
</div>
when i refresh the page i get this errors.
[Vue warn]: Error in render: "TypeError: vd.map is not a function
and
TypeError: vd.map is not a function
Why is the code simultaneously working and failing at the same time.
that warning message means that vd is not an array , thus cannot find map() . and its working because the type of object you have is iterable so it behaved like an array.
try creating a new array instance from it using Array.from() :
let vd = [ { "url": "/var/www/html/uploads/609cd3aaaaefd.jpg" }, { "url": "/var/www/html/uploads/609cd3aaa9dc2.jpg" }, { "url": "/var/www/html/uploads/609cd3aab1252.jpg" }, { "url": "/var/www/html/uploads/609cd3aab147a.jpg" } ]
let modifiedArr = Array.from(vd).map(function(item){
let url_parts = item.url.split('/');
return 'https://api.example.com/uploads' + '/' + url_parts[url_parts.length - 1];
});
console.log(modifiedArr);
seems like the vd is not an array while you getting errors. Wondering why? The possible reason, your code is accessing the vd.map before the data loads.
So, adding a null check before accessing vd will help here. Eg:
let modifiedArr = vd?.map(item=>{
...
...
}) || [];
will be a perfect blend here.
In common format,
let modifiedArr = vd && vd.map() || []

fetch and remove innerHTML from JSON using the same button

I have an external JSON file that looks like this:
[
{
"id": "1",
"label": "2017-03-30",
"value": "1675000",
"accessories": "true"
},
{
"id": "2",
"label": "2017-04-01",
"value": "1440000",
"accessories": "true"
},
{
"id": "3",
"label": "2017-04-02",
"value": "830000",
"accessories": "false"
}
]
I can fetch the JSON using the following button and it displays in the div ID of accessories:
<button id="button2">Get Customers</button>
<h1>Brands that sell Accessories:</h1>
<div id="accessories"></div>
The JavaScript I am using is:
document.getElementById('button2').addEventListener
('click', loadData);
function loadData(e) {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'data.json', true);
xhr.onload = function() {
if(this.status === 200) {
// console.log(this.responseText);
const customers = JSON.parse(this.responseText);
let output = '';
customers.forEach(function(customer) {
output += `
<ul>
<li>Company ID: ${customer.id}</li>
<li>Company label: ${customer.label}</li>
<li>Company value: ${customer.value}</li>
<li>Company Accessories: ${customer.accessories}</li>
</ul>
`;
})
document.getElementById('accessories').innerHTML = output;
}
}
xhr.send();
};
This outputs the company information in bullet points.
What I want to do next is remove the HTML by pressing the same button but I'm struggling to keep it removed.
I've tried:
function removeAcc() {
document.getElementById('accessories').innerHTML = '';
}
But it just flashes back to the same list.
Help will be very much appreciated.
you need to save the current state of your program somewhere so that you know if the "external JSON" was already loaded or not. This way you can switch the action in the eventListener like so:
document.getElementById('button2').addEventListener('click', toggleData);
function toggleData(e) {
if(hasData) {
removeAcc()
hasData = false;
} else {
loadData(e)
hasData = true;
}
}

Conditional (dynamic) destructuring

Depending on the selected language, I need to destruct the object and get the desired value.
How do I do this so as not to destruct the entire object?
const translate = {
"navMenu1": {
"en": "Menu 1",
"ru": "Меню 1"
},
"navMenu2": {
"en": "Menu 2",
"ru": "Меню 2"
},
"navMenu3": {
"en": "Menu 3",
"ru": "Меню 3"
}
}
const Header = props => {
const { lang } = props;
const {
navMenu1,
navMenu2,
navMenu3
} = translate;
return (
<header className={cnGreetingHeader}>
<div>-Logo-</div>
<nav className={cnNav}>
<div className={cnItem}>{navMenu1[lang]}</div>
<div className={cnItem}>{navMenu2[lang]}</div>
<div className={cnItem}>{navMenu3[lang]}</div>
</nav>
</header>
);
};
I want instead
<div className={cnItem}>{navMenu1[lang]}</div>
Use
<div className={cnItem}>{navMenu1}</div>
Important: I would like an answer that uses destructuring assignment, if possible.
Altougth you asked destructing, you can just map the object values, its more scalable, what happens when you have much more nav items?
<nav className={cnNav}>
{Object.values((navMenu) => (
<div key={navMenu.id} className={cnItem}>
{navMenu[lang]}
</div>
))}
</nav>
You could take a nested destructuring and take the outer name as variable.
const
translate = { navMenu1: { en: "Menu 1", ru: "Меню 1" }, navMenu2: { en: "Menu 2", ru: "Меню 2" }, navMenu3: { en: "Menu 3", ru: "Меню 3" } },
Header = props => {
const
{ lang } = props,
{
navMenu1: { [lang]: navMenu1 },
navMenu2: { [lang]: navMenu2 },
navMenu3: { [lang]: navMenu3 }
} = translate;
console.log(navMenu1);
console.log(navMenu2);
console.log(navMenu3);
};
Header({ lang: 'en' });

Filter data inside array and returns object filtered with group

I have an object like this and I need to filter the rules within each group item, however I need to also return the group name next to the filtered rule
{
"id": "rulesCompany",
"group": [
{
"name": "Cadastral",
"rule": [
{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "CNAE Primário - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
{
"name": "Dados modelados",
"rule": [
{
"title": "Nível de Atividade - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "Faturamento Presumido",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "medium"
}
]
},
]
}
For example, I need to return the group "Cadastral/Receita Federal" if I search for "Rece" in search field, but I don't know how to filter data inside data.
What I've done so far:
Module.vue
<template>
<div>
<slide-out :visible.sync="isVisible" :title="text.header">
<div slot="header">
<div>
<button class="neo-bt-icon--big slideout__close--button" #click="isVisible=false">
<svg><use xlink:href="/red/neo-assets/images/simple-line-icons.svg#arrow-2-left"></use></svg>
</button>
<h1 class="slideout__header--text">
{{ text.header }}
</h1>
<div class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input type="text" class="neo-form-field" placeholder="Buscar" v-model="searchQuery">
<input class="neo-form-toggle__field" :id="selectAllRules" #click="selectAllRules($event)" type="checkbox"/>
<label class="neo-form-toggle__label" :for="selectAllRules">selecionar tudo</label>
</div>
</div>
</div>
<div slot="content">
<div v-for="policyRule in filteredPolicyRules.group" :key="policyRule.name"
class="neo-form-group">
<li v-text="policyRule.name"></li>
<div class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input class="neo-form-toggle__field" :id="policyRule.name" #click="selectGroupRules(policyRule.rule, policyRule.name, $event)" type="checkbox" v-model="policyRules.name" />
<label class="neo-form-toggle__label" :for="policyRule.name">selecionar grupo</label>
</div>
<div class="neo-form-toggle-list neo-form-toggle-list--inline">
<div v-for="rule in policyRule.rule" :key="rule.title"
class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input class="neo-form-toggle__field" :id="rule.title" :value="rule" name="rule" type="checkbox" v-model="checkedRules"/>
<label class="neo-form-toggle__label" :for="rule.title">{{ rule.title }}</label>
<h6 class="neo-text-disabled-options">{{ rule.description }}</h6>
</div>
</div>
</div>
</div>
<div slot="footer">
<span>{{ checkedRules }}</span>
</div>
</slide-out>
</div>
</template>
<script>
import { mapState } from 'vuex';
import SlideOut from '#/components/shared/slideout/SlideOut.vue';
export default {
name: 'ModulePolicyRules',
props: [],
components: {
SlideOut,
},
data() {
return {
isVisible: false,
policyRules: [],
searchQuery: '',
checkedRules: [],
filteredRules: [],
};
},
computed: {
filteredPolicyRules() {
const me = this;
if (this.searchQuery) {
me.filteredRules.pop();
this.policyRules.group.filter((ruleGroup) => {
ruleGroup.rule.forEach((rule) => {
if (rule.title.startsWith(this.searchQuery)) {
console.log(me.filteredRules);
me.filteredRules.push(rule);
}
});
});
console.log(me.filteredRules);
return me.filteredRules;
// return this.policyRules.group.filter(item => item.name.startsWith(this.searchQuery));
}
return this.policyRules;
},
},
methods: {
async loadData() {
const rules = await this.$store.dispatch('policyrules/setPolicyRules');
this.policyRules = rules;
},
},
mounted() {
this.loadData();
},
};
</script>
<style lang="scss">
.neo-form-toggle__label {
text-transform: none;
font-weight: 600;
}
.neo-text-disabled-options {
text-transform: none;
}
</style>
Object expected result using "Rec" in search field:
{
"name": "Cadastral",
"rule": [
{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
Try this computed prop.
filteredPolicyRules() {
if (this.searchQuery) {
return this.policyRules.group.reduce((groups, { name, rule }) => {
const rules = [];
rule.forEach(r => {
if (r.title.startsWith(this.searchQuery)) {
rules.push(r);
}
});
if (rules.length > 0) {
groups.push({
name,
rules
});
}
return groups;
}, []);
}
return this.policyRules;
}
I'd suggest calling them groups and rules (plural) respectively, to avoid future confusion -- after all they are arrays.
Full demo:
const policyRules = {
"id": "rulesCompany",
"group": [{
"name": "Cadastral",
"rule": [{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "CNAE Primário - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
{
"name": "Dados modelados",
"rule": [{
"title": "Nível de Atividade - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "Faturamento Presumido",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "medium"
}
]
}]
};
new Vue({
el: '#app',
data() {
return {
searchQuery: '',
policyRules
}
},
computed: {
filteredPolicyRules() {
if (this.searchQuery) {
return this.policyRules.group.reduce((groups, { name, rule }) => {
const rules = rule.filter(this.matchFilter);
if (rules.length > 0) {
groups.push({
name,
rules
});
}
return groups;
}, []);
}
return this.policyRules;
}
},
methods: {
matchFilter(item) {
const
search = this.searchQuery.toLowerCase(),
term = (item.title || '').toLowerCase();
return term.includes(search);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="searchQuery" placeholder="Search..." />
<pre>{{ filteredPolicyRules }}</pre>
</div>
So first I've put your list into a map variable. Then I'm filtering that by checking if any of the wanted properties contain the search term. The array.filter Method returns a new array based on what entries returned true and what false.
I'm checking inside name, title and description. Also I made everything lower case so case doesn't matter.
Array.prototype.filter Reference
const term = 'CNE';
console.log(map.filter(e => {
const { name } = e;
if(contains(name, term)) return true;
for(const _r of e.rule) {
const { title, description } = _r;
if(contains(title, term)) return true;
if(contains(description, term)) return true;
}
return false;
}));
function contains(str, term) {
return str.toLowerCase().includes(term.toLowerCase());
}
And I would also suggest like Yom in his answer that you use groups and rules so you can name them better. So that then would be groups.filter(group => {[..]}) and for(const rule of group.rules)

Knockout bindings from JSON file

What am I doing wrong with my bindings? I simply get [object HTMLElement] returned for each one.
Please note this is a pared-down version and I will be wanting to access the full range of the JSON values.
Fiddle:
https://jsfiddle.net/0416f0s7/2/
Code:
<div data-bind="text: intro"></div>
function ViewModel(stories) {
var self = this;
self.stories = ko.observableArray(ko.utils.arrayMap(stories, function(story) {
return story.stories;
}));
};
$.getJSON('data.json', function(data) {
window.storyViewModel = new ViewModel(data.stories);
ko.applyBindings(window.storyViewModel);
});
JSON (filename data.json):
{
"stories": [
{
"intro": "Hi",
"outro": "Bye",
"elements": [
{
"title": "Title 1",
"image": "img/image1.jpg",
"paragraph": "Wordswordswords"
},
{
"title": "Title 2",
"image": "img/image2.jpg",
"paragraph": "More wordswordswords"
}
]
}
]
}
Your html change to
`<div data-bind="foreach:stories">
<div data-bind="text: intro"></div>
</div>
your code change to
function test(stories) {
var self = this;
self.stories = ko.observableArray(ko.utils.arrayMap(stories, function (story) {
return story;
}));
}

Categories