Array: How to change specific values dependent on index (Rating function) - arrays

I'm sorry for the terrible title, but somehow I can't explain it better in one sentence.
What I want to do is a rating component in my Vue App. So if I click the 3rd star, the two stars before that one are set to "true" as well.
What I got:
const ratingsArray = [
{
name: 'rating1',
ratingCount: 1,
isClicked: ref(false)
},
{
name: 'rating2',
ratingCount: 2,
isClicked: ref(false)
},
{
name: 'rating3',
ratingCount: 3,
isClicked: ref(false)
},
{
name: 'rating4',
ratingCount: 4,
isClicked: ref(false)
},
{
name: 'rating5',
ratingCount: 5,
isClicked: ref(false)
},
]
I just got a toggle function to toggle isClicked:
function toggleClick(x) {
x.value = !x.value
}
This is my template
<template>
<div v-for="rating in ratingsArray"
:key="rating.name"
#click="toggleClick(rating.isClicked)"
:class="[rating.isClicked.value ? 'ratingBoxFilled' : 'ratingBox']">
</div>
</template>
How can I say, that if rating3 is clicked (so isClicked is true), rating1 and rating2 also got to be true?
It seems that I need to work with the index in my array. But somehow, I cannot create an idea. Maybe you guys can help me out. Thank you!

A simple loop would do the trick:
<template>
<div v-for="(rating, index) in ratingsArray"
:key="rating.name"
#click="toggleClick(index)"
:class="[rating.isClicked.value ? 'ratingBoxFilled' : 'ratingBox']">
</div>
</template>
function toggleClick(ratingIndex) {
for (let i = 0; i < ratingsArray.length; i++) {
// Set ratingsArray[i].isClicked to true if it's within the requested range
ratingsArray[i].isClicked.value = (i <= ratingIndex);
}
}

You guys are great. I didn't thought of an for loop.
So here is my final solution:
<div
v-for="(rating, index) in ratingsArray"
:key="rating.name"
#click="updateRating(index, rating.ratingValue)"
:class="[rating.isEnabled.value ? 'ratingBoxChecked' : 'ratingBox']">
</div>
function updateRating(ratingIndex: number, ratingValue: number) {
for (let i = ratingIndex; i < 5; i++) {
ratingsArray[i].isEnabled.value = false;
}
for (let i = 0; i <= ratingIndex; i++) {
ratingsArray[i].isEnabled.value = true;
}
console.log('Rating Value: ' + ratingValue)
}
First I clean all the enabled dots.
Then it will run until the given index and set the boolean value to true. Thats all.

Related

ReactJs - Expected an assignment or function call and instead saw an expression

I did take a research on stackoverflow and received some results which is still not helping me solving this different problem properly. (differences between function and class)
(Expected an assignment or function call and instead saw an expression no-unused-expressions.)
at the line <Product7 key= {products7.id} from that.
I am new to this subject. I really appreciate if anyone could help me ^_^ . Many thanks.
function App() {
var products7 = [
{
name: 'Hollow Knight',
id : 1,
price: '3$',
status : true,
image: 'https://yuzu-emu.org/images/game/boxart/hollow-knight.png'
},
{
name: '60 Second!',
id : 2,
price: '4$',
status : true
image: 'https://yuzu-emu.org/images/game/boxart/hollow-knight.png'
},
{
name: 'Valiant Heart',
id : 3,
price: '5$',
status : true
image: 'https://yuzu-emu.org/images/game/boxart/hollow-knight.png'
},
];
let elements = products7.map((Product7, index) => {
return
<Product7 key= {products7.id}
image={products7.image}
price={products7.price} >
{products7.name}
</Product7>
});
Your return statement is not proper. Try this
let elements = products7.map((product7, index) => { // variable name updated
return (
<Product7 key= {products7.id}
image={products7.image}
price={products7.price} >
{products7.name}
</Product7>
)
});
You need to wrap your return code inside map
let elements = products7.map((Product7, index) => {
return (
<Product7 key= {products7.id}
image={products7.image}
price={products7.price} >
{products7.name}
</Product7>)
});

How to loop over nested array in vue and display data every 5 seconds?

I'm using Vue Js and I have a nested array like this:
arrays: [
{
name: 'Max',
Info: [60, 75, 70, 85, 65],
},
{
name: 'Dave,
Info: [70, 95, 60, 65, 83],
},
]
I have a user profile box that displays name and info for each user.
<div class="outer" v-for="(item, index) in arrays" :key="item.id" v-bind:id="item.name">
I would like to display info in template:
<div class="box c" >Info<span v-for="info in item.Info">{{info}}</span></div>
When I did this it showed the correct array for the correct user but it displayed all numbers
but I would like info to show each number only for 5 seconds until the last number. So for Max it will display 60 then wait 5 seconds, then will only show 75 wait 5 seconds etc until it stops at the last number and only displays that number.
I tried creating a function :
appendInfo: function() {
for (let i = 0, l = this.arrays.length; i < l; i++) {
let info = this.arrays[i]['Info'];
let timer = 0;
for (let i = 0; i < info.length; i++) {
setTimeout(() => this.rate = info[i], timer);
timer = timer + 5000;
}
}
and passed data to rate
return {
count: null,
rate: [],
and used {{rate}} in the template
<div class="box c" >Info<span>{{rate}}</span></div>
but it only displayed data from the second user profile for both profiles.
So I'm wondering if there is either a way to do this with v-for or how I can modify my function?
Thanks!
Update
I used the RateDisplayer component like so:
<div class="outer" v-for="(item, index) in arrays" :key="item.id" v-bind:id="item.name">
<rate-displayer :rates="item.Info" />
import RateDisplayer from './RateDisplayer'
export default {
name: 'card',
props: {
rates: {
required: true,
}
},
data () {
return {
interval: 5000,
rate: false,
...
but still displayed only last arrays values.
Fixed
I removed from parent component:
props: {
rates: {
required: true,
}
},
interval: 5000,
rate: false,
and now it's working, thanks again!
I did something like that before. First, I suggest you to create a component JUST for this part:
<div class="box c" >Info<span>{{rate}}</span></div>
then, in this component you can accept rates as prop, and set rate every 5 sec to the new one. Something like:
<template>
<div class="box c" v-if="rate">Info<span>{{rate}}</span></div>
</template>
export default {
name: "RateDisplayer.vue",
props: {
rates: {
required: true,
}
},
data() {
return {
interval: 5000,
rate: false
}
},
mounted(){ this.nextOrStop(-1) },
methods: {
nextOrStop(current)
{
if(current === this.rates.length-1) { return }
setTimeout( () => {
const index = current+1;
this.rate = this.rates[index];
this.nextOrStop(index)
}, this.interval)
}
....
You can insert this component inside your parent component like this:
<div class="outer"
v-for="(item, index) in arrays"
:key="item.id"
v-bind:id="item.name">
<rate-displayer :rates="item.Info" />
</div>

How to remap array only on changes?

I have component Page which contains components Editor and Preview.
Page contains array items.
[
{
value: 0,
text: 'Item 1'
},
...
]
Array items is passed to Editor & Preview like this:
<editor [items]="items"></editor>
<preview [items]="items"></preview>
Editor can add/delete/edit/reorder items.
Issue is preview needs this array in another format.
[
{
index: 0,
label: 'Item 1'
},
...
]
If I do like this
getRadioItems(): any[] {
const items = [];
for (let i = 0; i < this.items.length; i++) {
items.push({ index: this.items[i].value,
label: this.items[i].text });
}
return items;
}
and then
<radio-list [radioItems]="getRadioItems()"></radio-list>
It refreshes radio list hundreds times per second. You can't even change value because it will be reset on every refresh.
If it were without remapping - it would work fine.
What is correct way to remap items to radioItems in such case?
Have you tried setting the ChangeDetectionStrategy of the preview component to OnPush? Then change detection should only be run when the #Input() items is updated.
It is stupid solution, but it works.
getRadioItems(): any[] {
const newJson = JSON.stringify(this.items);
if (this.json === newJson) {
return this.cachedItems;
}
this.json = newJson;
this.cachedItems = [];
for (let i = 0; i < this.items.length; i++) {
this.cachedItems.push({ index: this.items[i].value,
label: this.items[i].text });
}
return this.cachedItems;
}

How to split my JSON object in angularJS

I am trying to create pagination for my table that lists the objects returned from my DB as an object. My data structure will look something like:
$scope.myJSONObj = {
app1: {
id: 1,
appName: "appIntegrated1",
status: "Pending"
},
app2: {
id: 2,
appName: "appIntegrated2",
status: "Pending"
},
app3: {
id: 3,
appName: "appIntegrated3",
status: "Completed"
},
app4: {
id: 4,
appName: "appIntegrated4",
status: "Pending"
},
app5: {
id: 5,
appName: "appIntegrated5",
status: "Pending"
},
app6: {
id: 6,
appName: "appIntegrated6",
status: "Pending"
},
app7: {
id: 7,
appName: "appIntegrated7",
status: "Pending"
},
app8: {
id: 8,
appName: "appIntegrated8",
status: "Pending"
},
app9: {
id: 9,
appName: "appIntegrated9",
status: "Pending"
},
app10: {
id: 10,
appName: "appIntegrated10",
status: "Pending"
}
I am trying to split my structure in half, and display the first five results. I have a prev/next button, and when I click next, it should display the next 5 results (in this case the last 5). However, for everything to work, I need to be able to split my object, and so far every method I've researched involves arrays, and objects requiring some hack. I was wondering if I was missing something, or I have to create a solution to work with?
In pure JavaScript :
function getEntries(from, to) {
var entries = [];
for(var key in myJSONObj) {
// extract index after `app`
// var index = key.substring(3);
// Better way : extract index using regular expression, so it will match `something1`, `foo2`, `dummy3`
var index = parseInt(key.replace( /^\D+/g, ''));
if(index >= from && index <= to) {
entries.push(myJSONObj[key]);
}
}
return entries;
}
console.log(getEntries(0, 5));
Try _.chunk
https://lodash.com/docs/4.17.4#chunk
$scope.pages = _.chunk($scope.myJSONObj,5);
$scope.getPage = function( pageIndex ){
return $scope.pages[pageIndex];
}
It's untested - but I wrote a chunk method for you in vanilla JS since you can't use lodash.
function chunk(obj, chunkSize) {
var resultArray = [];
var resultArrayCurrentIndex = 0;
for (var key in obj) {
var item = obj[key];
if (resultArray[resultArrayCurrentIndex].length <= chunkSize) {
if (!resultArray[resultArrayCurrentIndex]) {
resultArray[resultArrayCurrentIndex] = [item];
} else {
resultArray[resultArrayCurrentIndex].push(item)
}
} else {
resultArrayCurrentIndex++
resultArray[resultArrayCurrentIndex] = [item];
}
}
return resultArray;
}
Then you can access it like this:
$scope.pages = chunk(yourObject, 5);
$scope.getPage = function(index){
return $scope.pages[index];
}
EDIT - changed it to accept an obj.
Used Object.keys, Array.prototype.slice and Array.prototype.reduce to solve your issue. Hope this helps
angular.module('app',[])
.controller('TestCtrl', function($scope){
$scope.myJSONObj = {"app1":{"id":1,"appName":"appIntegrated1","status":"Pending"},"app2":{"id":2,"appName":"appIntegrated2","status":"Pending"},"app3":{"id":3,"appName":"appIntegrated3","status":"Completed"},"app4":{"id":4,"appName":"appIntegrated4","status":"Pending"},"app5":{"id":5,"appName":"appIntegrated5","status":"Pending"},"app6":{"id":6,"appName":"appIntegrated6","status":"Pending"},"app7":{"id":7,"appName":"appIntegrated7","status":"Pending"},"app8":{"id":8,"appName":"appIntegrated8","status":"Pending"},"app9":{"id":9,"appName":"appIntegrated9","status":"Pending"},"app10":{"id":10,"appName":"appIntegrated10","status":"Pending"}};
$scope.currentPage = 0;
$scope.pageSize = 5;
$scope.totalPage = Math.ceil( Object.keys($scope.myJSONObj).length/$scope.pageSize);
//pageNumber starts from 0 here
$scope.goToPage = function(pageNumber) {
pageNumber = pageNumber>=0?pageNumber:0;
var from = pageNumber*$scope.pageSize;
var to = from + $scope.pageSize;
return Object.keys($scope.myJSONObj).slice(from,to).reduce(function(a,b){
a[b] = $scope.myJSONObj[b];
return a;
},{});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="TestCtrl">
<button ng-disabled="currentPage===0" ng-click="currentPage = currentPage - 1">prev</button>
<button ng-disabled="currentPage===totalPage-1" ng-click="currentPage = currentPage + 1">next</button>
<b>Page: {{currentPage+1}}/{{totalPage}}</b>
<pre>{{goToPage(currentPage) | json}}</pre>
</div>

NaN showing even with number (angularjs)

In the controller:
$scope.startCount = 0;
$scope.vs = function (number) {
$scope.startCount = number;
}
$scope.startTimeout = function () {
$scope.startCount = $scope.startCount + 1;
mytimeout = $timeout($scope.startTimeout, 1000);
}
$scope.startTimeout();
$scope.stopTimeout = function () {
$timeout.cancel(mytimeout);
alert("Timer Stopped");
}
$scope.meals = [
{ title: 'Abs', url:"#/app/mworkouts",id: 100, img: "img/female.jpg", vid:"vid/1.mp4",},
{ title: 'Arms', url:"#/app/browse",id: 2 , img: "img/male.jpg", vid:"vid/2.mp4"},
{ title: 'Biceps', url:"#/app/search",id: 3, img: "img/Spotify_2.jpg", vid:"vid/1.mp4" },
{ title: 'Legs', url:"#/app/search",id: 4, img: "img/Spotify_4.jpg", vid:"vid/2.mp4" },
{ title: 'Core', url:"#/app/mworkouts",id: 5, img: "img/female.jpg", vid:"vid/1.mp4" },
{ title: 'Back', url:"#/app/mworkouts",id: 6, img: "img/male.jpg", vid:"vid/2.mp4" }
];
In the html:
<div ng-repeat="m in meals">
<button ng-click='vs({{m.id}})'>Setter</button>
</div>
So how it is supposed to work is pass in a number from the array and then count up using $timeout. It works perfectly fine if I enter in a number manually but I want the number to come from the array. Also, {{m.id}} is definelty a number because I have tested it {{m.id-60}} and it works. I am have no idea what is wrong.
Sorry about the weird names...it's just an example :)
Just get rid of the {{}}
ng-click='vs(m.id)'
It's seems to work for me here! I added couple of log for you to make sure
<div ng-repeat="m in meals">
<button ng-click='vs(m.id)'>Setter</button>
</div>
http://plnkr.co/edit/tpl:rfqcl9AHEoJZEEJxyNn2?p=preview

Resources