push object to array of arrays vue re-rendering failing - arrays

I have this weird issue where I can't seem to push an object to an array of arrays.
I am able to log the value of groups....but I can't update it.
here's my function to push to the groups array under a given set of conditions
calcExclusion: function(){
this.hideExclusionGroups = true //hides the exclusion groups from app before calculations
for(var i = 0; i < this.exclusionGroups.length; i++){
if(this.numberOfGroups < this.exclusionGroups.length){
alert('could not calculate, there are more exclusion groups than groups')
return
}
for(var j = 0; j < this.exclusionGroups[i].students.length; j++){
if(this.exclusionGroups[i].students.length == 1){
alert ('could not calculate, groups must be bigger than 1')
return
}
//console.log('group number', g,'student to add',this.exclusionGroups[i].students[j])
if(j < this.numberOfGroups){
this.groups[i].push(this.exclusionGroups[i].students[j].first_name)
console.log(this.groups[i])
}
}
}
},
here is where I render the data
<div v-for="(group, index) in groups" class="d-inline-block bg-white p-2 m-2 border rounded">
<span>Group {{ index + 1 }}</span>
<ul>
<li class="groupItems" v-for="student in group">
{{ student.first_name }}
<input type="hidden" :name="'group['+index+']'" :value="student.id">
</li>
</ul>
</div>
I am able to edit 'groups' to some extent but groups is referencing the computed prop here
computed: {
groups: function () {
if(! Number.isInteger(this.numberOfGroups)) {
console.log('mal');
return [];
}
const array = [];
for (let j = 0; j < this.numberOfGroups; j++) {
array[j] = [];
}
let i = 0;
this.students.forEach((student) => {
const x = i % this.numberOfGroups;
if(student.include === false){
array[x].push(student);
}
i++;
});
return array;
},
},

You are updating the results of a computed property. The result is not reactive, that's why you see that your code is updating the groups array, but you don't see any changed in the DOM.
You need to move the logic from calcExclusion inside the computed method for groups.

Related

rendering the array values only works in the initialization in vue2?

I have worked on vue2 with a simple demo, there is an array with values, by clicking the button, the array values will be shuffled. However rendering the array in html doesn't change at all after the shuffling.
<template>
<div>
<div #click="random()" > random </div>
{{ selected11.length }}
<div class="flex flex-grow " v-for="(item,index) in selected11" :key="index" >
{{ item }} {{ index }}
</div>
</div>
</template>
<script>
export default {
name: 'Choice',
data() {
return {
selected11:[],
}
},
created() {
this.selected11 = ['A', 'B', 'C', 'D','E'];
},
methods: {
random(){
console.log( 'random',this.selected11 );
this.selected11 = this.shuffle( this.selected11 );
console.log( this.selected11 );
},
shuffle(a) {
var j, x, i;
for (i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = a[i];
a[i] = a[j];
a[j] = x;
}
return a;
}
}
}
</script>
The issue is in your shuffle method where reassignment of keys with values from key positions of its own referenced self is self-destructive and is causing a silent reactive fail.
The only way I can think of describing this is like a Wormhole is traveling into its self, in this circular event that warps its self out of existence...
You need to shuffle a clone of the original object and return this shuffled clone so it replaces the original object, otherwise the array eats its self.
shuffle(a) {
let j, x, i;
let shuffle = structuredClone(a)
for (i = shuffle.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
x = shuffle[i];
shuffle[i] = shuffle[j];
shuffle[j] = x;
}
return shuffle;
}

How to use conditional to generate element on the page

for (var k = 0; k < 10; k++) {
if (k % 2 === 0) {
weatherText = <div className="in_break">
}
weatherText += <div className="eachD" key={k}>
<div>
{
countIt === 0 ? (currDate.getHours() > 12 ? "Tonight" : "Today") : dayOfWeek[weekDay]
}
</div>
<div>
{
getDate
}
</div>
<div>
{
<ReturnIcon />
}
</div>
</div>
if (k % 2 === 0) {
weatherText += </div>
}
}
What I am looking to do is group all the eachD by two inside the `in_break' div
But I keep getting:
Parsing error: Unexpected token 'weatherText = </div>'
This is the layout:
in_break
eachD
eachD
in_break
eachD
eachD
in_break
eachD
eachD
...
Please help me resolve my issue
UPDATED
I hope this find it's way to your demand:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
renderInBreak = () => {
const items = this.setWeatherTextItems();
const inBreakItems = [];
let breakBlock = [];
let newBreak = false;
items.forEach((textItem, index) => { //1
if(!newBreak) {
breakBlock.push(textItem);
if(index + 1 === items.length){
inBreakItems.push(breakBlock);
}
} else {
inBreakItems.push(breakBlock);
breakBlock = [];
breakBlock.push(textItem);
//without this condition check, the last element will be left out of an odd array length
if(index + 1 === items.length) {
inBreakItems.push(breakBlock)
}
}
if(index % 2) newBreak = true; //false
else newBreak = false; //false
});
return inBreakItems.map(twoTextWeatherItems => (
<div className="in_break">
{twoTextWeatherItems}
</div>
))
}
render(){
<div>
{this.renderInBreak()}
</div>
}
OLD
React is supposed to handle things differently, maybe this will work:
Define a method in your component that will set your items:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
in your render method, or where you are willing to render these items:
render(){
<div className="in_break">{this.setWeatherTextItems()}</div>
}
Read more about how to render things in a loop.
You can add the conditions you want inside the for loop, or where it makes sense to you.
Not sure if the logic would work in a react environment but as far as I can see from your plain code when you are going to add the 'in_break' div aren't you just assigning the whole whetherText again instead of joining text to it?
Shouldn't this:
if (k % 2 === 0) {
weatherText = </div>
}
be written like this?
if (k % 2 === 0) {
weatherText += </div>
}
Edit following the typo correction:
I tried to run your code on codepen to have a quicker and easier understanding on how to find a solution.
I created an helper function with your code then I returned
<div className="Container" dangerouslySetInnerHTML={{__html: weatherText}}></div>
This enables you to have the result you are looking for. Only the even elements have the 'in_break' class.
Hope this helped and let me know if this is not correct.
Codepen: https://codepen.io/dpgian/pen/EBzRmX

ng-repeat not displaying data from localStorage

In my Controller:
By clicking on an element, the information is given to tabNameStore.
The data is displayed in html by name
$scope.openTab = function($event){
//Checks for doubles
if($scope.tabNameStore.length>0){
for(var i=0; i<$scope.tabNameStore.length; i++){
if($scope.tabNameStore[i].id == $event.currentTarget.id){
console.log("Already open");
return;
}
}
}
//Saves name, ID and number
$scope.tabNameStore[count] = ({name: $event.currentTarget.innerHTML,
id: $event.currentTarget.id, num: count++});
//Tries to get item by id
var local = localStorage.getItem($event.currentTarget.id);
//If not stored in local, it does so
if (local == null){
localStorage.setItem($event.currentTarget.innerHTML,
$scope.tabNameStore[count-1].name);
localStorage.setItem($event.currentTarget.id,
$scope.tabNameStore[count-1].id);
console.log("null");
} else{
console.log("not null");
return;
}
}
Save the data from localStorage back in tabNameStore
$window.onload = function() {
var archive = [];
archive = allStorage();
count = 0;
for(var i=0; i<archive.length/2; i++){
if(archive[i] != null){
$scope.tabNameStore[count] = ({name: archive[i], id:
archive[i+archive.length/2], num: count++});
}
}
}
Getting all data from localStorage
function allStorage() {
var keys = Object.keys(localStorage); // Gibt alle Schlüssel zurück
var archive = [];
for (var i=0; i< keys.length; i++) {
archive.push(keys[i]);
}
return archive;
}
The HTML is:
<article id="default">
<div id="heading">
<a href="###" class="tab" id="desktop" >Desktop</a>
<a href="###" class="tab" id="" data-ng-repeat="y in
tabNameStore" data-ng-click="" >{{y.name}}</a>
</div>
<div class="gridly toggle" id="Desktop">
<div class="brick small" draggable="true" data-letters="
{{x.name[0]}}" id="{{x.id}}" data-ng-repeat="x in storage |
orderBy: '-name'" data-ng-click="openTab($event)">
{{x.name}}
</div>
</div>
</article>
After reloading the page and getting the data from localStorage it's still not displayed, even if the array is filled. Just if I click again on one of my elements (openTab function), all elements stored in localStorage are displayed at once.
I solved it:
By adding a $scope.$apply around the part that reloads the array:
....
$scope.$apply(function(){
for(var i=0; i<archive.length/2; i++){
if(archive[i] != null){
$scope.tabNameStore[count] = ({name: archive[i], id:
archive[i+archive.length/2], num: count++});
}
}
});
....
Like this the updated array is set up for angularjs after reloading.

Make React Properties not Tied to Specific Variables

I'm trying to create a group of react elements through a for loop. However, it seems that, rather than each element getting its own copy of the variables per for loop, they are tied to whatever the variable is at the end of the for loop (where i = 3). How can I prevent this. Thanks.
makeCheckboxes() {
var checkboxes = [];
for(var i = 0; i < this.props.flagNames.length; i++) {
console.log("Making checkbox. i = " + i);
checkboxes.push((
<React.Fragment>
<label><input type="checkbox" name="reportFlags" value="fraud" checked={this.state.reportFlags[i]} onClick={() => this.handleCheck(i)}/>{this.props.flagNames[i]} </label><br />
</React.Fragment>
));
}
return checkboxes;
};
just replace
for(var i = 0; i < this.props.flagNames.length; i++) {
by
for(let i = 0; i < this.props.flagNames.length; i++) {
actually it will create a closure, so it should sove the problem
if you still want to use var you can change your code like this using immediately-invoked function expression iife
for(var i = 0; i < this.props.flagNames.length; i++) {
console.log("Making checkbox. i = " + i);
checkboxes.push((
<React.Fragment>
<label>
<input
type="checkbox"
name="reportFlags"
value="fraud"
checked={this.state.reportFlags[i]}
onClick={((j) => () => this.handleCheck(j))(i)} //here is the iife
/>
{this.props.flagNames[i]}
</label><br />
</React.Fragment>
));
}
return checkboxes;

How to dynamically use object property as width in AngularJS (in ng-repeat)?

I cannot get the object's property to be read in ng-style(shape.radius || shape.length). I can't even get 1 to work at the moment, but would like to have an or statement included. Similar to my ng-class.
There is a button to generate shapes, and the shapes were created with a random size. Here is my code:
html:
<div ng-controller='ShapeController as sc'>
<div>
<p><input type="submit" value="Generate Random Shapes" ng-click="sc.generateShapes()"/></p>
<div ng-repeat="shape in sc.shapes">
<div class="shape" ng-class="{circle: shape.radius, square: shape.length}" ng-style="{'width': shape.length}"></div>
</div>
</div>
</div>
script:
var app = angular.module('app', []);
app.controller('ShapeController', function(){
var vm = this;
vm.shapes = [];
vm.randomShapes = [];
vm.width = 30;
function createCircle(radius) {
let circle = new Circle(radius);
vm.shapes.push(circle);
} // end createCircle
function createSquare(length) {
let square = new Square(length);
vm.shapes.push(square);
} // end createSquare
vm.generateShapes = function() {
let times = 50
for (let i = 0; i < times; i++) {
createCircle(getRandomNumber());
}
for (let i = 0; i < times; i++) {
createSquare(getRandomNumber());
}
sort(vm.shapes);
console.log(vm.shapes);
}; // end generateShapes
}); // end controller
function sort(arr) {
arr.sort(function(a,b){
return b.getArea() - a.getArea();
});
} // end sort function
function getRandomNumber() {
return Math.random() * (100-1) + 1;
}
width should be either in px(some unit like em, pt, etc) or %
ng-style="{'width': shape.length + 'px'}"

Resources