Append element in DOM from array - javascript

I have:
<div *ngFor="let item of items">
<h2>Hello {{item.name}}</h2>
</div>
My items:
items = [
{
name: 'David',
star: 5
},
{
name: 'George',
star: 2
},
{
name: 'Michael',
star: 0
},
{
name: 'Tim',
star: 1
},
]
They render:
Hello Tim
etc.
How to add:
<i class="fa fa-star"></i>
above h2
when in array:
star: 1
etc?
Something like this:
if (this.items.star == 2) {
<i class="fa fa-star"></i>
<i class="fa fa-star"></i>
}
(It's just an example "if statement" to understand what's going on)
My PLUNKER:
https://plnkr.co/edit/lfZT6FwenhYkQf1MUx9E?p=preview

You can use the *ngIf template decorator this way:
<div *ngFor="let item of items">
<i class="fa fa-star" *ngIf="item.star === 1></i>
<h2>Hello {{item.name}}</h2>
</div>
See more examples and read more about it here
*ngIf: Conditionally includes a template based on the value of an expression.

Related

Conditional Styling in Svelte Loop

Has anyone used Svelte in their projects? I just have a small question. Let's say you are looping through an array and displaying the data in the array. And there are 4 objects in the array. If I only want to style the first iteration (bluma). How do I do it?
let items = [
{ icon: 'book', title: 'bulma', tags: ['public', 'sources'] },
{ icon: 'book', title: 'marksheet', tags: ['private'] },
{ icon: 'book', title: 'minireset.css', tags: ['public', 'sources'] },
{ icon: 'code-branch', title: 'daniellowtw/infboard', tags: ['public', 'forks'] },
{ icon: 'code-branch', title: 'mojs', tags: ['private'] }
]
{#each filteredItems as item}
<a class="panel-block {item.}">
<span class="panel-icon">
{#if item.icon === "code-branch"}
<i class="fas fa-code-branch" aria-hidden="true"></i>
{:else if item.icon ==="book"}
<i class="fas fa-book" aria-hidden="true"></i>
{/if}
Whenever a pure CSS approach is available, I would rather use that. Here you could make use of the :first-child selector:
<div class="panel-items">
{#each filteredItems as item}
<a class="panel-block">
<span class="panel-icon">
{#if item.icon === "code-branch"}
<i class="fas fa-code-branch" aria-hidden="true"></i>
{:else if item.icon ==="book"}
<i class="fas fa-book" aria-hidden="true"></i>
{/if}
</span>
</a>
{/each}
</div>
<style>
.panel-block {
/* general panel block styling */
}
.panel-block:first-child {
/* first panel block style overrides */
}
</style>
Note that the entire loop is held within its own container div in order to make sure the first item in the loop is indeed the first child of its parent container.
This can be done by using the index provided as a second argument of the #each block:
{#each filteredItems as item, i}
<span style="color:{i === 0 ? 'green' : 'red'}">
{item.title}
</span>
{/each}
Reference: Each block in svelte tutorial.

Refresh UI when object key value change - Knockout

sorry for a noob question, just started with Knockout.js. I have an array of objects and I want to update the view when object property favorite: changes but every time I click on an icon that triggers the change nothing happens. When I add a new object to an array UI gets rerendered. I would really appreciate some help with this. Thanks
<div id="container" data-bind="foreach:savedSearches">
<div class="save-search-item" data-bind="attr:{'data-name': $data.name, 'data-id':$data.id, 'favourite':$data.favorite() === 1}">
<div data-bind="text: $data.name"></div>
<div class="icons">
<a href="#" class="favourite-search">
<i class="fas fa-star" data-bind="css: {favourite: $data.favorite() === 1}"></i>
</a>
<a href="#" class="edit-search">
<i class="fas fa-edit"></i>
</a>
<a href="#" class="delete-search">
<i class="fas fa-trash-alt"></i>
</a>
</div>
</div>
</div>
var searches = [
{
activation_time: null,
activation_time_ms: null,
favourite: 1,
enabled: 1,
id: 66,
name: "adfdfafs"
},
{
activation_time: null,
activation_time_ms: null,
favourite: 0,
enabled: 1,
id: 66,
name: "adfdfafs"
}
];
ko.applyBindings(AppViewModel, $('#container'));
function AppViewModel(data) {
self.savedSearches = ko.observableArray([]);
self.favourite = ko.observable();
self.populateSavedSearches = function(data) {
data.forEach(function(search) {
search.favorite = ko.observable();
});
self.savedSearches(data);
}
}
$('.favourite-search').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
// get parent element with id
var parent = e.currentTarget.closest('.save-search-item');
var searchId;
var isFavourite = false;
if (parent) {
searchId = parseInt(parent.getAttribute('data-id'));
isFavourite = parent.getAttribute('favourite');
searches.map(function(search) {
if (search.id === searchId) {
search.favorite = 0;
ko.populateSavedSearches(search);
}
});
}
});
When using knockout, you should not add your own event listeners via jQuery.
In this case, use the click binding to react to user behavior.
I did the bare minimum to make your snippet work, but I think it gets the point across:
You already found out you have to make the favorite property observable! Great start
I added a toggle function to each of the searches that swaps the favorite observable between 1 and 0
In the view, I added a click binding that calls toggle
In the view, I moved your favourite attribute binding to be a css binding. This makes sure favorited searches get the favourite class
In CSS, I styled .favourite elements to have a yellow background.
In applyBindings, I use new to create a new viewmodel and pass the app container using [0]
You can see these changes in action in the snippet below.
var searches = [
{
activation_time: null,
activation_time_ms: null,
favourite: 1,
enabled: 1,
id: 66,
name: "adfdfafs"
},
{
activation_time: null,
activation_time_ms: null,
favourite: 0,
enabled: 1,
id: 66,
name: "adfdfafs"
}
];
ko.applyBindings(new AppViewModel(searches), $('#container')[0]);
function AppViewModel(data) {
const self = this;
self.savedSearches = ko.observableArray([]);
self.favourite = ko.observable();
self.populateSavedSearches = function() {
data.forEach(function(search) {
search.favorite = ko.observable(search.favorite);
search.toggle = function() {
search.favorite(search.favorite() ? 0 : 1);
}
});
self.savedSearches(data);
}
self.populateSavedSearches();
}
.favourite { background: yellow }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div id="container" data-bind="foreach: savedSearches">
<div class="save-search-item" data-bind="
click: toggle,
attr: {
'data-name': $data.name,
'data-id':$data.id
},
css: { 'favourite': $data.favorite() === 1 }
">
<div data-bind="text: $data.name"></div>
<div class="icons">
<a href="#" class="favourite-search">
<i class="fas fa-star" data-bind="css: {favourite: $data.favorite() === 1}"></i>
</a>
<a href="#" class="edit-search">
<i class="fas fa-edit"></i>
</a>
<a href="#" class="delete-search">
<i class="fas fa-trash-alt"></i>
</a>
</div>
</div>
</div>

how to toggle 3 or more font-awesome icons dynamically in vue.js

i am creating a rock-paper-scissors mini game with vue and i try to find a way to toggle classes like variables. for example:
<template>
<div id="human">
<div class="text-center">
<div class="h2 mb-5">Human</div>
<!-- PLEASE CHECK BELOW HERE -->
<i class="play-hand far fa-hand-{{ iconName }}"></i>
<!-- OR -->
<i class="play-hand far {{ icon }}"></i>
<div class="h3 mt-4">{{ activeHand }}</div>
<div class="row select-hand mt-4">
<div class="col-md-4">
<i class="far fa-hand-rock" #click="setHand(hands[0])"></i>
</div>
<div class="col-md-4">
<i class="far fa-hand-paper" #click="setHand(hands[1])"></i>
</div>
<div class="col-md-4">
<i class="far fa-hand-scissors" #click="setHand(hands[2])"></i>
</div>
</div>
</div>
</div>
</template>
i marked with a commentary. I am pretty sure you get what i want to do.
I don't want to use document.querySelector() for this.
<script>
export default {
data: () => {
return {
activeHand: 'Choose a Hand',
hands: [
{
name: 'Rock',
strength: 'scissor',
weakness: 'paper',
icon: 'fa-hand-rock'
},
{
name: 'Paper',
strength: 'rock',
weakness: 'scissor',
icon: 'fa-hand-paper'
},
{
name: 'Scissor',
strength: 'paper',
weakness: 'rock',
icon: 'fa-hand-scissors'
}
]
}
},
methods: {
setHand (hand) {
console.log(hand.name)
this.activeHand = hand.name
console.log(hand.icon)
let playHandSelector = document.querySelector('.play-hand')
playHandSelector.classList.remove(this.activeHand.includes(hand))
playHandSelector.classList.add(hand.icon)
}
}
}
</script>
<style lang="scss">
#human .far {
transform: rotate(90deg);
}
</style>
There must be a vue way to toggle a class dynamically by name. maybe without a true/false toggeling?
You have to use Vue's v-bind directive for class bindings and put any variable usage into that markup. Take a look at the documentation here.
Eventually your code could look like this.
<i class="play-hand far" :class="icon"></i>
Keep in mind to use proper class binding syntax in either using objects or arrays.

Filter array of objects based on the objects attributes

I would like to filter an array of objects based on the objects attributes. Let me show you what I mean with the code. First of all I have an array where I store the information of the objects:
var data = [
{
product: "Haori Jacket",
url: "haorijacket.html",
image: "img/haori-jacket.jpg",
altDesc: "Jacket",
price: "$210.00",
outwear: ""
},
{
product: "Swing Dress",
url: "swingdress.html",
image: "img/swing-dress.jpg",
altDesc: "Dress",
price: "$218.00",
dress: ""
},
{
product: "Tan Tote",
url: "",
image: "img/tan-tote.jpeg",
altDesc: "Backpack",
price: "$350.00",
sale: "$475.00",
accessory: ""
},
{
product: "Sunglasses Nº 1",
url: "",
image: "img/sunglasses-n1.jpeg",
altDesc: "Sunglasses",
price: "$125.00",
accessory: ""
},
{
product: "Sunglasses Nº 2",
url: "",
image: "img/sunglasses-n2.jpeg",
altDesc: "Sunglasses",
price: "$125.00",
accessory: ""
}
];
Then dynamically generate a string using a Template Literal with a function:
// Template literal
function clothingView(item, index) {
return (`
<a href="${item.url}" class="shop-item">
${item.sale ? `<span class="shop-item__sale">Sale</span>`: ''}
<img data-src=${item.image} alt="${item.altDesc}" class="shop-item__img">
<div class="quickview">
<span class="quickview__icon">Quick View</span>
<span class="quickview__info">${item.product}
<br>
<span class="quickview__price">${item.price}
${item.sale ? `<span class="quickview__price--sale">${item.sale}</span>`: ''}
</span>
</span>
<span class="shop-item__index">${index}</span>
</div>
</a>
`);
};
// Append template literal to html structure
for (var i = 0; i < data.length; ++i) {
$('.all-items').append(clothingView(data[i], i))
}
I have a div with the class "all-items" which contain all my objects (38 to be exact) but I also want to divide these objects in categories (accesories, dresses, outwear, etc..). I am trying to do this using the filter method. Here's some images to help clarify the situation:
Basically I'd like to append to my div with the class "accesories" only the objects with the attribute "accesory". How can I do this? Any help is much appreciated!
edit: let me also include the html structure just in case:
<section class="products-container container">
<nav class="categories">
<span class="categories__link" id="accesories">Accesories</span>
<span class="categories__link" id="bottoms">Bottoms</span>
<span class="categories__link" id="dresses">Dresses + Jumpsuits</span>
<span class="categories__link" id="outwear">Outerwear</span>
<span class="categories__link" id="tops">Tops</span>
<span class="categories__link" id="sale">— Sale</span>
</nav>
<div class="products all-items">
<!-- <a href="haorijacket.html" class="shop-item">
<span class="shop-item__sale">Sale</span>
<img src=img/haori-jacket.jpg alt="Jacket" class="shop-item__img">
<div class="quickview">
<span class="quickview__icon">Quick View</span>
<span class="quickview__info">$Haori Jacket
<br>
<span class="quickview__price">$210.00<span class="quickview__price--sale">$150.00<span></span>
</span>
<span class="clothing-index">${index}</span>
</div>
</a> -->
</div>
<div class="products accesories"></div>
<div class="products bottoms"></div>
<div class="products dresses"></div>
<div class="products outwear"></div>
<div class="products tops"></div>
<div class="products sale"></div>
</section>
You can use the in operator to check if a specific property exists within an object.
The in operator returns true if the specified property is in the specified object or its prototype chain.
var allItems = $('div.all-items');
var accesoryItems = $('div.accesories');
for (var i = 0; i < data.length; ++i) {
var clothingView = clothingView(data[i], i);
if ('accessory' in data[i]) accesoryItems.append(clothingView);
else allItems.append(clothingView);
}
Of course, you need to add the necessary if-else-if to add the views to specific divs.
You can try this:
function filterItems(itemType) {
return data.filter(function(i) {return i[itemType] !== undefined});
}
This should be okay for IE9+ and modern browsers.

Making A Meteor Subdocument Reactive

I have a collection of Items that can be marked as "read" by my users.
In order to mark a collection as "read", a subdocument is added:
"readby": [
{
"action": "read",
"owner": "w5XzMrCCJJfDxCn6d"
}
]
I then use the following helper to set up an array and push any "read" entries that match the current owner. If the array has a length bigger than 0, the helper returns "true" and we know the logged in user has read this item:
itemHasBeenRead() {
var subscribers = [];
var readItems = this.readby;
if(!readItems) {
return false;
}
var readiness = readItems.forEach(function(currentSubscriber) {
// loop over current users expenses
var newSubscriber = { owner: currentSubscriber.owner };
if (currentSubscriber.owner == Meteor.userid()) {
subscribers.push(newSubscriber);
}
});
return subscribers && subscribers.length > 0
}
This all works perfectly BUT as I understand it, subdocuments aren't reactive in Meteor, so the code doesn't pick up changes reactively. Refresh the page and it works fine.
Is there a way to do this reactively, rather than just on page load?
--
Edits as requested:
--
Template code:
{{#each playlists}}
<h2 class="playlistheader">{{playlistName}}<span class="badge badge-playlist badge-playlist-first badge-primary"><i class="fas fa-users playlist-fa"></i>{{numberOfSubscribers}} enrolled</span>{{#unless isPlaylistOwner}}{{#unless userIsSubscribed}}<span class="badge badge-playlist badge-success subscribe-unsubscribed" data-id="{{this._id}}"><i class="far fa-heart playlist-fa"></i>Enrol</span>{{/unless}}{{#if userIsSubscribed}}<span class="badge badge-playlist badge-success subscribe-subscribed" data-id="{{this._id}}"><i class="fas fa-heart playlist-fa"></i>Enrolled</span>{{/if}}{{/unless}}
<span class="badge badge-playlist badge-completion" data-id="{{this._id}}"><i class="fas fa-check-circle" style="margin-right:3px;"></i>0/6 items marked complete</span>
{{#if isPlaylistOwner}}<span class="badge badge-playlist badge-danger badge-delete" data-id={{this._id}}><i class="fas fa-ban playlist-fa"></i>Delete</span>{{/if}}</h2>
<span class="playlist-subheader">{{playlistPrivacyType}} collection by {{playlistOwnerName}}</span>
<div class="row" style="display:inherit; margin-left:0px; margin-bottom:20px;margin-top:8px;">
<div class="scrolling-wrapper-playlist">
{{#if isPlaylistOwner}}
<div class="playlist-product playlist-product-add" data-playlistid="{{playlistid}}">
<div class="clampcontainer">
<div class="add-playlist-plus" data-playlistid="{{playlistid}}">+</div>
</div>
</div>
{{/if}}
{{#each playlistItems this._id}}
<div class="playlist-product" data-id={{this._id}}>
{{#if isPlaylistItemOwner}}
<div class="playlist-product-delete">
<i class="fas fa-times-circle product-delete"></i>
</div>{{/if}}
<div class="playlist-product-overlay" id="overlay-{{this._id}}" style="opacity:0;">
<div class="playlist-product-overlay-description">
"{{itemDescription}}"</div>
<div class="playlist-product-overlay-icons">
{{#unless itemHasBeenRead}}
<i class="far fa-check-circle playlist-circle"></i>
{{/unless}}
{{#if itemHasBeenRead}}
<i class="fas fa-check-circle playlist-circle"></i>
{{/if}}
{{#if hasPrice}}
<i class="fas fa-shopping-cart playlist-cart"></i>
{{/if}}
<i class="fas fa-external-link-alt playlist-external"></i>
</div>
</div>
<div style="width:90px; float:left; margin-right:10px;"><img src="{{itemImage}}" width="90"></div>
<div class="clampcontainer">
<div class="itemTitle linkColor">{{itemTitle}}</div>
<p class="itemDescription">{{itemDescription}}...</p>
<div class="fadeout"></div>
</div>
</div>
{{/each}}
</div>
</div>
{{/each}}
Publication code:
Meteor.publish('UserPlaylists', function() {
var loggedinuser = Meteor.user();
// Reveal ALL expenses if it's an admin who's logged in
return Playlists.find({
owner: loggedinuser._id
});
});
// Publish public playlists, as long as they're both public and from the same company
Meteor.publish('PublicPlaylists', function() {
var loggedinuser = Meteor.user();
var companyid = loggedinuser.userofcompanyid;
// Reveal ALL expenses if it's an admin who's logged in
return Playlists.find({
companyid: companyid,
published: true
});
});
// Publishes absolutely all expenses for superadmins
Meteor.publish('Playlists', function() {
var loggedinuser = Meteor.user();
if (loggedinuser.issuperadmin) {
return Playlists.find({});
}
});
Subscribing to the publications in the template js:
Template.Playlists.onCreated(function playlistsOnCreated() {
var self = this;
self.autorun(function() {
self.subscribe('UserPlaylists');
self.subscribe('PublicPlaylists');
self.subscribe('UserPlaylistItems');
self.subscribe('PublicPlaylistItems');
});
});
The playlists helper:
playlists() {
return Playlists.find({}, {
sort: {
timestamp: -1
}
});
},
The playlistItems helper:
playlistItems(playlistid) {
return PlaylistItems.find({
playlistid: playlistid
}, {
sort: {
timestamp: -1
}
});
},

Categories