Best way to loop Obj data in HTML document with JS? - javascript

I'm developing a simple SPA framework. I have a problem. I want to render my Obj data in HTML. Below is my code and my online example
var data = {
for: {
animal: [{
name: 'dog',
alive: 'false'
},
{
name: 'cat',
alive: 'true'
}
],
human: [{
name: 'bob',
sex: 'male'
},
{
name: 'alice',
sex: 'female'
}
]
}
};
<html>
<div id="app">
<ol>
<li np-for="animal">
<np tag="text-for" data="name"></np><br>
<np tag="text-for" data="alive"></np>
</li>
</ol>
</div>
</html>
best way to solve it?

If you are not using a framework, then I suggest you using jQuery, so the full code looks like this
let data = {
animals : [
{ name: 'dog', alive : 'false'},
{ name: 'cat', alive : 'true'}
]
};
let animalList = $('#animals');
data.animals.forEach(function(data) {
animalList.append(`<li>
<b>${data.name}</b>
<i>${data.alive}</i>
</li>`);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<div id="app">
<ul id="animals">
<!-- data will automatically generated by jQuery -->
</ul>
</div>
</html>
I suggest you to using let or const for static variable instead of var, read more var vs let vs const in JavaScript
Read more about jQuery

Related

KnockoutJS - How to hide certain elements inside foreach using Observable Arrays?

I have a list of WebsiteOwners. I'm trying to build a UI which will display more information about the owners when I click on them.
this.toExpand = ko.observableArray(); //initialize an observable array
this.invertExpand = ko.observable("");
this.invertExpand = function (index) {
if (self.invertExpand[index] == false) {
self.invertExpand[index] = true;
alert(self.invertExpand[index]); //testing whether the value changed
}
else {
self.invertExpand[index] = false;
alert(self.invertExpand[index]); //testing whether the value changed
}
};
Here's the HTML code :
<div data-bind="foreach: WebsiteOwners">
<div>
<button data-bind="click: $root.invertExpand.bind(this,$index())" class="label label-default">>Click to Expand</button>
</div>
<div data-bind="visible: $root.toExpand()[$index]">
Primary Owner: <span data-bind="text:primaryOwner"></span>
Website Name : <span data-bind="text:websiteName"></span>
//...additional information
</div>
</div>
You can store one of your WebsiteOwner items directly in your observable. No need to use an index.
Don't forget you read an observable by calling it without arguments (e.g. self.invertExpand()) and you write to it by calling with a value (e.g. self.invertExpand(true))
I've included 3 examples in this answer:
One that allows only a single detail to be opened using knockout
One that allows all details to be opened and closed independently using knockout
One that does not use knockout but uses plain HTML instead 🙂
1. Accordion
Here's an example for a list that supports a single expanded element:
const websiteOwners = [
{ name: "Jane", role: "Admin" },
{ name: "Sarah", role: "Employee" },
{ name: "Hank", role: "Employee" }
];
const selectedOwner = ko.observable(null);
const isSelected = owner => selectedOwner() === owner;
const toggleSelect = owner => {
selectedOwner(
isSelected(owner) ? null : owner
);
}
ko.applyBindings({ websiteOwners, isSelected, toggleSelect });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: { data: websiteOwners, as: 'owner' }">
<li>
<span data-bind="text: name"></span>
<button data-bind="
click: toggleSelect,
text: isSelected(owner) ? 'collapse' : 'expand'"></button>
<div data-bind="
visible: isSelected(owner),
text: role"></div>
</li>
</ul>
2. Independent
If you want each of them to be able to expand/collapse independently, I suggest adding that state to an owner viewmodel:
const websiteOwners = [
{ name: "Jane", role: "Admin" },
{ name: "Sarah", role: "Employee" },
{ name: "Hank", role: "Employee" }
];
const OwnerVM = owner => ({
...owner,
isSelected: ko.observable(null),
toggleSelect: self => self.isSelected(!self.isSelected())
});
ko.applyBindings({ websiteOwners: websiteOwners.map(OwnerVM) });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: websiteOwners">
<li>
<span data-bind="text: name"></span>
<button data-bind="
click: toggleSelect,
text: isSelected() ? 'collapse' : 'expand'"></button>
<div data-bind="
visible: isSelected,
text: role"></div>
</li>
</ul>
3. Using <details>
This one leverages the power of the <details> element. It's probably more accessible and by far easier to implement!
const websiteOwners = [
{ name: "Jane", role: "Admin" },
{ name: "Sarah", role: "Employee" },
{ name: "Hank", role: "Employee" }
];
ko.applyBindings({ websiteOwners });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<ul data-bind="foreach: websiteOwners">
<li>
<details>
<summary data-bind="text: name"></summary>
<div data-bind="text: role"></div>
</details>
</li>
</ul>

Loop throuth a filtered list in vue.js with parameter

How to loop in vue.js through a filtered list with an input parameter given?
Pls. take into consideration that filtering like <li v-for="x in todos_filtered" v-if="x.person == 'Mike'"> should be avoided with many records.
Here the code which returns an empty list instead of 2 elements.
<html>
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14/dist/vue.js"></script>
<body>
<div id="app">
<ul>
<li v-for="x in todos_filtered('Mike')">
{{ x.text }}
</li>
</ul>
</div>
<script>
myObject = new Vue({
el: '#app',
data: {
todos: [
{ person: 'Mike', text: 'Learn JavaScript' },
{ person: 'Luke', text: 'Learn Vue.js' },
{ person: 'Mike', text: 'Build Something Awesome' }
]
},
computed: {
todos_filtered(person) {
return this.todos.filter((x) => x.person === person);
}
},
})
</script>
</body>
</html>
The computed property should return a function that take the person as parameter:
computed: {
todos_filtered() {
return (person)=> this.todos.filter((x) => x.person === person);
}
},

What method to use to access multiple objects in an array?

I'm new to javascript and still learning them.
So I was building a project where I want to display a multiple object, which I put it in an array, to the DOM.
I am not sure what method to use to access the object inside the array.
<div class="container">
<div class="hero">
<h2>List of Names</h2>
</div>
<ul class="name-list"></ul>
</div>
This is my js file:
const nameList = document.querySelector('.name-list');
//List of Names
const john = {
name: 'john',
car: 'fiat',
address: 'new york'
}
const mike = {
name: 'mike',
car: 'toyota',
address: 'sydney'
}
const greg = {
name: 'greg',
car: 'nissan',
address: 'melbourne'
}
//Store list of names in an array
const allNames = [
john,
mike,
greg
]
function displayName (){
//Not sure what methods to use to
return `
<li>
<p>Name: ${allNames.name}</p>
<p>Car: ${allNames.car}</p>
<p>Address: ${allNames.address}</p>
</li>
`
}
So I kind of want to display all the objects in the DOM.
Is it necessary to put the objects in the array first? What methods do I use to return a list in the file? Or do you know any easier methods to display all the objects in the DOM?
Thank you so much for the help.
Maybe you can try something like this :
function showNameList() {
const allNames = [
{
name: 'john',
car: 'fiat',
address: 'new york'
},
{
name: 'mike',
car: 'toyota',
address: 'sydney'
},
{
name: 'greg',
car: 'nissan',
address: 'melbourne'
}
]
var namelist = allNames.map(function (t, i) {
return `<b>Name : </b> ${t.name}<br/><b>Car : </b> ${t.car}<br/><b>Address : </b> ${t.address}<br/><br/>`;
})
document.getElementById('name-list').innerHTML =
'<li>' + namelist.join('</li><li>') + '</li>'
}
showNameList()
<div class="container">
<div class="hero">
<h2>List of Names</h2>
</div>
<ul id="name-list"></ul>
</div>
use map function to display them :
const values = allNames.map(item=>{
return(
<li>
<p>Name: ${item.name}</p>
<p>Car: ${item.car}</p>
<p>Address: ${item.address}</p>
</li>
)
})
<div class="container">
<div class="hero">
<h2>List of Names</h2>
</div>
<ul class="name-list">
{values}
</ul>
</div>

Kendo ui MVVM - How to bind an observable array and array inside array using kendo templates

Here is the my working DEMO.
I have an observable array named persons and each array element contains an another array named hobbies.
I have successfully bound the persons array using kendo template, but does anyone know how should I bind the hobbies array using an another template. Below is the code from my DEMO.
Code:
var persons = new kendo.data.ObservableArray(
[
{
name: "John Doe",
age: 28,
hobbies: [
{ id: 1, description: "Baseball", rank: 1 },
{id: 2, description: "music", rank: 3 },
{ id: 3, description: "Surfing the web", rank: 2}
]
},
{
name: "Jane Doe",
age: 24,
hobbies: [
{ id: 1, description: "Volley Ball", rank: 1 },
{id: 2, description: "Cricket", rank: 3 },
{ id: 3, description: "Hockey", rank: 2}
]
}
]
);
var viewModel = kendo.observable({
array: persons
});
kendo.bind($("#example"), viewModel);
<h2>Persons Array</h2><br/>
<div id="example" data-template="template" data-bind="source: array">
</div>
<script id="template" type="text/x-kendo-template">
<div>
Name: #=name# || Age: #=age# <br>
<ul>Hobbies (below, I want to display hobbies)</ul>
<br/>
</div>
</script>
You need to use a for loop inside the ul tag, something like:
# for (var i = 0; i < hobbies.length; i++) { #
<li>#= hobbies[i].description#</li>
# } #
Here it is the updated fiddle
Use another nested template named "hobby-template" and bind "hobbies" as source to that
<h2>Persons Array</h2><br/>
<div id="example" data-template="template" data-bind="source: array">
</div>
<script id="template" type="text/x-kendo-template">
<div>
<span data-bind="text:name"></span>
<span data-bind="text:age"></span>
<ul data-template="hobby-template" data-bind="source: hobbies"></ul>
</div>
</script>
<script id="hobby-template" type="text/x-kendo-template">
<li>
<span data-bind="text:description"></span>
<span data-bind="text:rank"></span>
<li>
</script>

Using angularjs filter and _.groupBy

Here's a simple example I created to replicate my issue. In my controller, I have an array of people:
$scope.people = [
{ name: 'fred', age: 20 },
{ name: 'bob', age: 22 },
{ name: 'jane', age: 24 },
{ name: 'mary', age: 22 },
{ name: 'ben', age: 24 },
{ name: 'sarah', age: 21 },
];
I have a filter defined:
.filter('grouped', function () {
return function (input) {
return _.groupBy(input, 'age');
}
})
You may notice that I'm using Lo-Dash to do the group by.
In my view I have a list defined:
<div class="list" ng-repeat="personGroup in people | grouped">
{{ $index }}
<div class="list" ng-repeat="person in personGroup">
{{ person.name }}
</div>
</div>
I get the result I'm after but I get a heap of errors in my developer console.
https://docs.angularjs.org/error/$rootScope/infdig?p0=10
I understand why I'm getting the error. This is explained in detail in the above link. I just don't know the proper way of achieving the result I'm after.
As you are creating new Array-objects when grouping the people, I would not use a filter at all. The easiest solution would be to do the grouping inside your controller:
...
$scope.groups = _.groupBy($scope.people, 'age');
...
Your ng-repeat attribute would then read like:
ng-repeat="personGroup in groups"
What version of angular are you using? I'm not getting any error:
http://plnkr.co/edit/bbDGjHWMGZx5GmNcggaw?p=preview

Categories

Resources