Random background color on vuejs loop - javascript

A little help here, I'm stuck. I don't know even how to start.
I have this v-for for printing my "contracts".
I'm still not to submerged on vuejs workflow so I'm not being able to work around this.
How can I give a diferent background-color to every col div there? But not randomly, I want for them to keep the color even if the page is reloaded, my aproach was using the id of my contract and doing something with that but I don't understand much about vuejs to know how to do it.
Let's say I want to make a javascript function to give a class depending on the contractType.id, how do I execute that function with each loop? Is there a proper way to do this on vuejs?
<template>
<div class="row" v-if="contractTypes && contractTypes.length > 0">
<div class="col-md-4 c-button" v-for="(contractType, index) in contractTypes" :key="index" #click="choose($event.srcElement.innerHTML, index, contractType.id)">
<div class="col p-4 d-flex flex-column position-static text-center">
<strong class="d-inline-block mb-2 text-primary">World</strong>
<h3 class="mb-0">{{ translations && translations[contractType.id] ? translations[contractType.id].usecasetitle : contractType.usecasetitle }}</h3>
<p class="card-text mb-auto">{{ translations && translations[contractType.id] ? translations[contractType.id].description : contractType.title }}</p>
<button class="btn btn-primary">Click</button>
</div>
</div>
</div>
<div v-else>
<h1>Leider stehen für Sie derzeit keine Verträge zur Auswahl.</h1>
</div>
</template>

If you want custom styles on the different contract types, you can d a method:
/*
* #param {string} contract -- a foo bar
* #return {object} -- Bunch of boolean controlled classes
*/
getContractClasses (contract)
{
return {
'style1': contract === 'a string',
'style2': contract === 'another thing',
'style3': true, // This one is always going to be on and will have a bunch of shared styles between all of them
}
},
Then, in the template,
<h3 :class="getContractClasses(contract)">
Finally, just do the different styles in the style guide in the component housing the v-for.
Also, might I suggest an unwritten rule of sorts to remove all the logic from the template layer, and have that as a method or computed? Reading that long ternary inline is a big "No thank you", from me.

Related

How to set a specific keyword/password sent through textarea that trigger JavaScript if statement

That's my first ever question, and it's about my very first project, it's an online RPG Forum/PlayByChat game. I know it's kinda of an ambitious project.
So, i'm stuck from months into a problem that i feel i'm close to solve, premise (I'm self-taught).
The idea is to activate an if statement by a keyword/password given by an NPC, if there isn't no keyword in the message that it's supposed to be sent, then nothing should to happen.
That's what i've been able to put together and modify a little bit (it's not the entire code, just the interested parts), it works but it gets activated by any words or even by just one letter/empty messagges, i recently added the attribute "required" to the textarea so the field needs to be fill before to send messagges (even though it works only for the first click, then gives the possibility to send empty messagges which of course triggers the JoinedInTheRoom function) but anyway, that's a minor problem, the main problem remains that i cannot figured out how to make work the if statement inside the event listener under the JoinedInTheRoom function with a pre-chosen keyword/password, let's say "Mellon" (not case-sensitive if possible).
i'll explain myself better.
Let's say i'm gonna write "Mellon fellas" in the TextArea inside the Chat Log, my idea is to simply trigger the JoinedInTheRoom function by the fact that "Mellon", the keyword, has been just sent inside the messagge, i hope that my intent it's clear.
Thanks in advance.
here is the code:
HTML - CHAT LOG + TEXT AREA AND BUTTON
<!-- CHAT LOG -->
<h3>Chat Log</h3>
<div class="ChatLog">
<div class="msg left-msg">
<div class="msg-img" style="background-image: url()"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">System</div>
</div>
<div class="msg-text">Write the Keyword to join the room and unlock the NPCs and Quests</div>
</div>
</div>
</div>
<!-- TEXT AREA AND BUTTON -->
<form class="FormInputArea">
<textarea class="InputCamp" id="TextArea" placeholder="Explain your move in max 200 characters..." required></textarea>
<button class="button" type="submit" id="SendButton"> Send </button>
</form>
JAVA SCRIPT - CHAT LOG + TEXT AREA AND BUTTON
// CHAT LOG + TEXT AREA AND BUTTON
const ChatLog = get(".ChatLog");
const FormInputArea = get(".FormInputArea");
const InputCamp = get(".InputCamp");
JAVA SCRIPT - JoinedInTheRoom + NpcsUnlocked
// JoinedInTheRoom + NpcsUnlocked
function JoinedInTheRoom(side) {
const NpcsUnlocked = `
<div class="msg ${side}-msg">
<div class="msg-img" style="background-image: url(${BOT_IMG})"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">${BOT_NAME}</div>
<div class="msg-info-time">${formatDate(new Date())}</div>
</div>
<div class="msg-text">You've joined the room, feel free to choose your quest.</div>
</div>
</div>
<div class="msg ${side}-msg">
<div class="msg-img" style="background-image: url(${BOT_IMG})"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">${BOT_NAME}</div>
<div class="msg-info-time">${formatDate(new Date())}</div>
</div>
<div class="msg-text">
<p onclick="Npc1('RANDOM TASK DESCRIPTION 1')"> Npc 1. </p>
<p onclick="Npc2('RANDOM TASK DESCRIPTION 2')"> Npc 2. </p>
</div>
</div>
</div>
`;
ChatLog.insertAdjacentHTML("beforeend", NpcsUnlocked);
ChatLog.scrollTop += 500;
}
FormInputArea.addEventListener("submit", event => {
event.preventDefault();
const NpcsUnlocked = InputCamp.value;
if (!NpcsUnlocked) return;
const Delay = NpcsUnlocked.split(" ").length * 600;
setTimeout(() => {
JoinedInTheRoom("left", NpcsUnlocked);
InputCamp.value = " ";
}, Delay);
})
JAVA SCRIPT - PLAYER MESSAGE SCRIPT
// PLAYER MESSAGE SCRIPT
function AppendMessage(name, img, side, text) {
const msgHTML = `
<div class="msg ${side}-msg">
<div class="msg-img" style="background-image: url(${img})"></div>
<div class="msg-bubble">
<div class="msg-info">
<div class="msg-info-name">${name}</div>
<div class="msg-info-time">${formatDate(new Date())}</div>
</div>
<div class="msg-text">${text}</div>
</div>
</div>
`;
ChatLog.insertAdjacentHTML("beforeend", msgHTML);
ChatLog.scrollTop += 500;
}
FormInputArea.addEventListener("submit", event => {
event.preventDefault();
const msgText = InputCamp.value;
if (!msgText) return;
AppendMessage(PERSON_NAME, PERSON_IMG, "right", msgText);
InputCamp.value = "";
});
Searching on internet i found this 11 years old stackoverflow post that i think it might help me, it seems i might use "indexOf" for this job, i'm right ? Maybe you guys can help me make it a little bit more "modern" and apply it to my code ?
Link
You could use the .includes function, like below
if (msgText.includes("mellon")) {
JoinedInTheRoom();
}
It'll search the whole string and return true if "mellon" is found. However it will also return true if someone types a word containing mellon. "smellon" or "smellonies" would return true and run the JoinedInTheRoom function too

How to set a class name dependant on another attribute of the same element in Svelte?

I want to set a conditional class based on an attribute of the same element so that I can stay dry. At first I got this:
<div class="hidden md:flex items-center space-x-3">
Farm
LeaderBoard
WafBox
Buy
Info
</div>
Now I would like this:
<div class="hidden md:flex items-center space-x-3">
Farm
LeaderBoard
WafBox
Buy
Info
</div>
Then I would simply have to check the attribute of href in my function isActive. But here this does not seem to have the right informations inside. Is there a way to do it ? It would clean my code a lot
Edit: isActive() would look like this:
<script>
function isActive(element) {
return 'Active' if (currentPageName == element.attr('href'))
}
</script>
One problem with your approach is that both in
LeaderBoard
and the proposed answer using the class:directive
<a href="#Index" class:active-link={isActive('Index')}>Index</a>
is that the function will only run once, at the first render and then never re-execute, not even when currentPageName changes.
The most obvious, straight solution would be to simply ditch the function call and use the class:directive like this:
<a href="#Index" class:active-link={currentPageName == 'Index'}>Index</a>
this will make sure the classes change as currentPageName changes.
When declaring the function with the arrow syntax and making it reactive by adding $: it looks like alternatively to using the class:directive (which checks if value is truthy/falsy) the classname could be directly set inside the class="" attribute as well
A REPL
<script>
let currentPageName = 'Index'
$: isActive = (linkText) => {
if (linkText === currentPageName) return 'active-link'
// else if (...) return 'other-class-name' // possible class name switch
else return ''
}
</script>
<div class="">
Index
</div>
<div class="">
Leaderboard
</div>
<br>
<button on:click={() => currentPageName = 'Leaderboard'}>change currentPageName</button>
<style>
.active-link {
color: purple;
}
</style>

sorting computed values and updating computed values vue

In my vue app, I get some JSON from my API, I am then computing that returned data and sorting it via one of it's attributes, I am then wanting to then resort it every time that attribute is updated on the client side, I thought it would be enough to add `v-model="tag.weight" to the input but apparently not? What am I doing incorrectly?
<div class="mt-6" v-if="this.sortedTags">
<div class="w-full flex justify-between pb-2 border-white">
<div class="w-1/3 p-4 font-black">Tag</div>
<div class="p-4 font-black">Active</div>
<div class="p-4 font-black">Weight</div>
</div>
<div class="w-full flex justify-between" v-for="tag in this.sortedTags" :key="tag.id">
<div class="w-1/3 p-4">{{ tag.tag }}</div>
<div class="p-4"><input type="checkbox" /></div>
<div class="p-4"><input type="text" v-model="tag.weight" size="3" class="text-black"/></div>
</div>
</div>
computed: {
...mapState(['admin']),
sortedTags() {
return this.admin.tags.sort(function(a, b) {
a.weight - b.weight;
});
}
}
What I was hoping for what the 1) if entered a number into the weight field that is bound tag.weight it would order it based on that input, and 2) When I add new object to the data, it would resort it.
I got similar result when omitting the return, it will give you response, but re-sorting won't work.
Therefore, just use return a.weight - b.weight; instead of a simple a.weight - b.weight;
Detailed explanation : https://stackoverflow.com/a/38159151/3256489

vueJS List Transition not activating

I have a scenario in a Vue JS App where I have a list of items which randomly change order after having the user presses a button. I can get the list elements to dynamically change their position with Vue.set however when I add a list transition via the <transition-group> tag the change remains, well, transition-less, and I don't know why.
I am displaying my list of items using the v-for attribute like so:
<transition-group name="shuffle" tag="ul">
<li class="content column" v-for="card in notecards" v-bind:key="card.u_id">
<div v-bind:key="card.u_id" v-on:click="card.show=!card.show">
<transition mode="out-in" name="flip">
<span v-bind:key="card.term" v-if="card.show" class="card-pad box card card-content media">
<div class="media-content has-text-centered">
<strong>{{card.term}}</strong>
</div>
</span>
<span v-bind:key="card.def" v-else class="card card-content box media">
<div class="media-content has-text-centered">
<strong>{{card.def}}</strong>
</div>
</span>
</transition>
</div>
</li>
</transition-group>
The key card.u_id is a unique key for each element. I have also defined the css style "shuffle-move" with the following rule:
shuffle-move{
transition: all 1s;
}
Additionally I am updating the position using a shuffle method in my Vue instance which looks like so:
shuffle_cards: function() {
let entries = this.notecards.length;
while (entries){
let random_index = Math.floor(Math.random() * entries--);
let intermediate = this.notecards[random_index];
Vue.set(this.notecards, random_index, this.notecards[entries]);
Vue.set(this.notecards, entries, intermediate);
}
}
I am using Vue 2.
Where am I going wrong?
You are missing a dot in your css rule:
.shuffle-move{
transition: all 1s;
}
Live example of your code : https://codepen.io/hans-felix/pen/oNXqbKE

How can I display additional data when clicking on list item?

I managed to display some mock data (name, email and username) but as you can see I wanted to display more detailed information (city,country,...) on a User by clicking on a row
I am still new to angular and still have problems using the correct syntax.
I am stuck searching the web for hours although I know it should be quite easy...
I thank you all in advance
This is what I have so far :
(this is my first question on stack overflow, I am sorry for mistakes :P )
export class ScrollComponent {
users;
constructor() {
this.users = Array(100)
.fill(1)
.map(_ => {
return {
name: faker.name.findName(),
email: faker.internet.email(),
exMail: faker.internet.exampleEmail(),
userName: faker.internet.userName(),
url: faker.internet.url(),
ip: faker.internet.ip(),
mac: faker.internet.mac(),
pass: faker.internet.password(),
address: faker.address.streetAddress(),
zip: faker.address.zipCode(),
city: faker.address.city(),
country: faker.address.county(),
iban: faker.finance.iban(),
bic: faker.finance.bic(),
bitcoin: faker.finance.bitcoinAddress()
};
});
<cdk-virtual-scroll-viewport itemSize="100">
<li *cdkVirtualFor="let u of users" class="animated slideInUp">
<h2>{{ u.name }} </h2>
<p> {{ u.email }} {{ u.userName }} </p>
</li>
</cdk-virtual-scroll-viewport>
Welcome to SO!
First of all keep in mind, that when using libraries like Angular Material or ng-bootstrap you have components that can already do this. As you are using the CDK, you probably use Angular Material as well? Then you could use the Expansion Panel.
Otherwise as you expected you can achieve this quite easily. There are different ways to do this, depending on your specific needs (i.e. should every list-element be expandable at the same time or only one?). I will give you a hint for what I think is the easiest way and then you can adjust it to your needs.
First thing is to add a boolean to your user-type holding the state of each list-element. Let's call it detailsVisible.
Then you want to add a ClickHandler to the list items which toggles this boolean:
<li *cdkVirtualFor="let u of users" class="animated slideInUp" (click)="u.detailsVisible = !u.detailsVisible">
and then add the the details into some element whichs visibility you control using *ngIf:
<li *cdkVirtualFor="let u of users" class="animated slideInUp" (click)="u.detailsVisible = !u.detailsVisible">
<h2>{{ u.name }} </h2>
<p> {{ u.email }} {{ u.userName }} </p>
<div *ngIf="u.detailsVisible"> additional details in here </div>
</li>
If you only want one element to be allowed to be expanded at the same time you would go for something like this. The idea is to just store the index of the selected element and only show details for this.
<li *cdkVirtualFor="let u of users; let i = index" class="animated slideInUp" (click)="expandedElement = i">
<h2>{{ u.name }} </h2>
<p> {{ u.email }} {{ u.userName }} </p>
<div *ngIf="users.indexOf(u) === expandedElement"> additional details in here </div>
</li>
and then of course add the expandedElement variable to your .ts-file.

Categories