Vue: Array is only updated on console, not on screen - arrays

I'm still learning the basics of vue. I have different elements in one component, that have to be assigned in a second component. When I use console.log the array is shown correctly, but when i want to show the array in dynamically in template it is still the default value. What am I doing wrong?
Component one:
<template>
<div>
{{nr}}
{{containertyp}}
<button #click="click(0)"></button>
</div>
</template>
I have many more buttons with different parameters, just to make it shorter here.
export default: {
data: function {
return {
nr: [],
containertyp: [],
}
}
methods: {
click(number) {
for (var i = 0; i < 27; i++) {
this.nr[i] = false;
if (number == i) {
this.nr[i] = true;
}
};
EventBus.$emit('containerclicked');
}
},
created: function() {
let i;
//default
for (i = 0; i < 27; i++) {
this.nr[i] = false;
}
for (var index = 0; index < 27; index++) {
this.containertyp[index] = 'bs';
}
},
mounted() {
const self = this
EventBus.$on('containertypchosen', function (containertyp) {
for (let j = 0; j < 27; j++) {
if (self.nr[j] == true) {
self.containertyp[j] = containertyp
}
}
})
Component two:
<template>
<div>
<button :disabled = "disabled == true" v-on:click="chosetype('bs')" "> bs </button> <br />
</div>
</template>
export default: {
data: function() {
return {
disabled: true
}
}
mounted () {
const self = this
EventBus.$on('containerclicked', function (){
self.disabled = false
})
},
methods: {
chosetype: function (containertyp) {
this.containertyp = containertyp
EventBus.$emit('containertypchosen', containertyp)
}
}
}

You can't update arrays using indexes, the changes won't be detected by the reactivity system.
https://v2.vuejs.org/v2/guide/list.html#Caveats
So, for example, this won't work:
this.nr[i] = true;
Instead you'd need to use Vue.set, or the alias vm.$set:
this.$set(this.nr, i, true);
An alternative would be to create a new array and then replace this.nr entirely, i.e. this.nr = newArray.
You'll need to make a similar change everywhere that you're updating an array by index. There are updates to both nr and containertyp that currently have this problem.
It's not immediately clear from your code whether nr even needs to be an array. It seems that all the values are false apart from one, so you might be better off just holding the index of the true value instead of using an array.

Related

What is the reasoning behind "The final argument passed to useCallback changed size between renders ..." warning

Opening this question here, because StackOverflow is listed as a recommended place for asking React-related questions in React docs.
I am looking for a reasoning behind throwing a The final argument passed to useCallback changed size between renders. The order and size of this array must remain constant. warning.
After looking into React code it looks like React does not properly compare prevDeps and nextDeps arrays when they have different lengths.
The comparison function looks like this (some checks were omitted for brevity):
function areHookInputsEqual(prevDeps,nextDeps) {
for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
if (Object.is(nextDeps[i], prevDeps[i])) {
continue;
}
return false;
}
return true;
}
Which means:
areHookInputsEqual( ['a','b'], ['c','d'] ) === false - correct
areHookInputsEqual( ['a','b','c'], ['a','b'] ) === true - wrong
areHookInputsEqual( ['a','b'], ['a','b','c'] ) === true - wrong
areHookInputsEqual( [], ['a'] ) === true - wrong
areHookInputsEqual( ['a'], [] ) === true - wrong
Why not to write this function as following and remove warning from the codebase?
function areHookInputsEqual(prevDeps,nextDeps) {
if (prevDeps.length !== nextDeps.length) {
return false;
}
for (let i = 0; i < prevDeps.length; i++) {
if (Object.is(nextDeps[i], prevDeps[i])) {
continue;
}
return false;
}
return true;
}
The use case which led to this question
We have a custom hook like this:
function useLoadMessageKeys(messageKeys: string[]) {
return React.useCallback(
() => {
return load(messageKeys)
},
messageKeys
)
}
Because of current React implementation, load does not get called when messageKeys change from [] to ['a'].
Update (how we currently solved this)
function areArraysEqual<T>(prevDeps: T[], nextDeps: T[]): boolean {
if (prevDeps === nextDeps) {
return true
}
if (prevDeps.length !== nextDeps.length) {
return false
}
for (let i = 0; i < prevDeps.length; i++) {
if (!Object.is(nextDeps[i], prevDeps[i])) {
return false
}
}
return true
}
export function useLoadMessageKeys(messageKeys: string[]) {
const messageKeysRef = React.useRef(messageKeys)
if (!areArraysEqual(messageKeys, messageKeysRef.current)) {
messageKeysRef.current = messageKeys
}
const currentMessageKeys = messageKeysRef.current
return React.useCallback(
() => load(currentMessageKeys),
[currentMessageKeys]
)
}
If the code linked in the question properly compared 2 arrays we'd avoid having this complexity.
I think the reasoning is that the array of dependencies should always be exactly the list of variables used inside the effect. So in particular it can be statically determined and cannot change size. If it does change size, you're probably doing something more than just listing the dependencies, so it is warning you that you are not using the dependency list as intended.
You could use instead use a version of messageKeys that does not change if it is only shallow equal to the previous one (untested):
const useMemoizedArray = (array: string[]) => {
const [memoizedArray, setMemoizedArray] = React.useState(array);
React.useEffect(() => {
// Define `isShallowEqual` yourself somewhere
if (!isShallowEqual(array, memoizedArray)) {
setMemoizedArray(array);
}
}, [array, memoizedArray]);
return memoizedArray;
};
function useLoadMessageKeys(messageKeys: string[]) {
const memoizedMessageKeys = useMemoizedArray(messageKeys);
return React.useCallback(
() => {
return load(memoizedMessageKeys)
},
[memoizedMessageKeys]
)
}

Unable to Append new Child Components in react

I am new to react.
I want to add another SingleButtonComponent when a SingleButtonComponent is clicked.
I am able to increment the number of children using a state variable numChildren.
AddChild: function() {
var numChildren = (this.state.numChildren) +1;
this.setState({numChildren :numChildren})
},
But I am getting error whenever I am trying to loop inside
render: function () {
return (
for (var i = 0; i < this.state.numChildren; i += 1) {
<SingleButtonComponent AddChild={this.AddChild}/>
};
)
}
. Here is the PLUNKER to it
I have temporarily kept the for loop out of render..
Please suggest me a way to add a child-component every time it is clicked.
Thanks
Your render function doesn't return anything.
If you're using React 16, try:
render: function() {
const arr = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
arr.push(<SingleButtonComponent key={i} AddChild={this.AddChild}/>);
}
return arr;
}
Otherwise, you must return a single element:
render: function() {
const arr = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
arr.push(<SingleButtonComponent key={i} AddChild={this.AddChild}/>);
}
return (<div>{arr}</div>);
}
Regarding your second question:
React.createClass is deprecated in favor of ES6 classes. Your component should be defined as follows:
class MainBody extends React.Component {
constructor() {
this.state = { numChildren: 0 };
}
AddChild() { ... }
render() { ... }
}

how to check a value in array object angularjs

i have this array object:
$scope.datas.labels=['10','20','30']
and also i have a function return an array object like this:
response.labels=['10','20','30','50','100','80']
i created a function which recieve the last result..but what i want is to check if a value in response.labels exists in the $scope.datas.labels i dont want to insert it..to avoid duplicated data in $scope.datas.labels, how i can do that??
i tried this but i didnt work:
$scope.concatToData=function (response) {
if($scope.datas.labels=='') {
$scope.datas.labels = $scope.datas.labels.concat(response.labels);
}else {
var i;
for (i = 0; i < $scope.datas.labels.length; i++) {
alert('qa' + JSON.stringify($scope.datas.labels));
alert('res' + JSON.stringify(response.labels));
if ($scope.datas.labels[i] !== response.labels[i]) {
$scope.datas.labels = $scope.datas.labels.concat(response.labels[i]);
} else {
break;
}
}
}
$scope.datas.datasets = $scope.datas.datasets.concat(response.datasets);
}
Try this it will work as per your expectation and requirement.
var arr1=['10','20','30'];
var arr2=['10','20','30','50','100','80'];
for (var i in arr2) {
if(arr2[i] != arr1[i]) {
arr1.push(arr2[i]);
}
}
document.getElementById('result').innerHTML = arr1;
#result {
font-weight:bold;
}
<div id="result"></div>
Take a look at the lodash library, you'll find it useful, and this will be useful for you too:
let common = _.intersection($scope.datas.labels, response.labels);
if (_.size(common) && _.includes(common, 'myValue')) {
// You have a winner;
// This item (myValue) is in both;
} else {
}
Hope that helps.
You can also try that:
var response = ['foo', 'fabio'];
var labels = ['foo'];
var result = response.filter((value) => {
return labels.filter((rs) => {
return rs == value;
}).length == 0;
});
It will return only the data that does not exists on $scope.datas.labels.

Why array save last data and doesn't clears?

I have a simple AngularJs application of medical cards.
I have storage with it and display it at my home.html using dx-datagrid:
One card has many records, I get records of card from recordsArray by cardId
getVardsRecordsByCardId: function (id, recordsArray) {
if (recordsArray.length != 0) {
for (var i = 0; i < recordsArray.length; i++) {
if (recordsArray[i].cardId === id) {
cardsRecords = cardsRecords.concat(recordsArray[i]);
}
}
}
return cardsRecords;
}
Now I have records just in the third card. I added a function on button for testing it:
var jdskla = [];
var localCardId = 0;
$scope.showCardDetails = {
text: "",
type: "default",
icon: "preferences",
onClick: function () {
if ($scope.itemIdFromShowButton) {
$location.path('/carddetail/' + $scope.itemIdFromShowButton);
var jdskla =[];
var jdskla = businessLogicOfMyApp.getVardsRecordsByCardId($scope.itemIdFromShowButton, $scope.recordsArray);
console.log($scope.itemIdFromShowButton)
console.log(jdskla);
}
else {
alert("Error!!!");
}
}
};
1,3,1 is cardId's and array of records. But, why array of card records don't clears and save last data?
May be somebody know how I can resolve it? Thanks for your answers!
P.S. I'm using ng-view directive in my app and i tried to clear my array use another button:
$scope.backToGeneralPage = {
text: "Back",
onClick: function () {
jdskla = [];
$location.path('/');
}
};
but it wasn't helpful.
You should initialize cardsRecords array in function getVardsRecordsByCardId.
getVardsRecordsByCardId: function (id, recordsArray) {
var cardsRecords = []; // initialize array locally
if (recordsArray.length != 0) {
for (var i = 0; i < recordsArray.length; i++) {
if (recordsArray[i].cardId === id) {
cardsRecords.push(recordsArray[i]);
}
}
}
return cardsRecords;
}

make filter based on data from localstorage in the filter function

I'm new with the Ionic-angular.js, I hope that someone will help me to resolve this problem
First, here is the code
favorites.html
...
<ion-item ng-repeat="dish in dishes | favoriteFilter:favorites" href="#/app/menu/{{dish.id}}" class="item-thumbnail-left" on-swipe-left="deleteFavorite(dish.id)">
<img ng-src="{{baseURL+dish.image}}" on-swipe-left="deleteFavorite(dish.id)">
<h2>{{dish.name}}
<ion-delete-button class="ion-minus-circled"
ng-click="deleteFavorite(dish.id)">
</ion-delete-button>
</ion-item>
...
services.js
.factory('favoriteFactory', ['$resource', 'baseURL', function ($resource, baseURL) {
var favFac = {};
var favorites = [];
favFac.addToFavorites = function (index) {
for (var i = 0; i < favorites.length; i++) {
if (favorites[i].id == index)
return;
}
favorites.push({id: index});
};
favFac.deleteFromFavorites = function (index) {
for (var i = 0; i < favorites.length; i++) {
if (favorites[i].id == index) {
favorites.splice(i, 1);
}
}
}
favFac.getFavorites = function () {
return favorites;
};
return favFac;
}])
.factory('$localStorage', ['$window', function($window) {
return {
store: function(key, value) {
$window.localStorage[key] = value;
},
get: function(key, defaultValue) {
return $window.localStorage[key] || defaultValue;
},
storeObject: function(key, value) {
$window.localStorage[key] = JSON.stringify(value);
},
getObject: function(key,defaultValue) {
return JSON.parse($window.localStorage[key] || defaultValue);
}
//removeItem: function(key){
// $window.localStorage.removeItem(key);
//}
}
controller.js
.filter('favoriteFilter', 'localStorage', function (localStorage) {
if(localStorage.getItem('favorites')!=undefined)
{
var out = [];
return out;
}
else{
return function (dishes) {
var old_favorite = JSON.parse($localStorage.get('favorites'));
var leng = Object.keys(old_favorite).length;
console.log(leng);
var out = [];
for (var i = 0; i < leng; i++) {
for (var j = 0; j < dishes.length; j++) {
if (dishes[j].id === favorites[i].id)
out.push(dishes[j]);
}
}
return out;
}}
});
For the example, there was an array inside the localstorage like this
Key : favorites
value : [{"id":1},{"id":2},{"id":0}]
So, the logic is, I compare the ID between from database and the localstorage based on the ID with the filter function
If the ID is same, so the data from the database gonna push it into the favorites menu.
but, it couldn't show in the favorites menu, and when I checked on the console, it said that
[ng:areq] Argument 'fn' is not a function, got string
Did I make something wrong on here? Or maybe I put a wrong method on here?
Thank you in advance.
The error you present seems to be a syntax problem. You are missing the array brackets.
.filter('favoriteFilter', ['$localStorage', function (localStorage) {
if(localStorage.getItem('favorites')!=undefined)
{
var out = [];
return out;
}
else
{
return function (dishes) {
var old_favorite = JSON.parse($localStorage.get('favorites'));
var leng = Object.keys(old_favorite).length;
console.log(leng);
var out = [];
for (var i = 0; i < leng; i++) {
for (var j = 0; j < dishes.length; j++) {
if (dishes[j].id === favorites[i].id)
out.push(dishes[j]);
}
}
return out;
}
};
}]);
I didn't check your logic function, this will be the answer to solve your error.
Try a different approach:
As you already have addToFavorites and deleteFromFavorites functions, all you have to do is simply follow these 3 steps:
When defining you 'favorites' array, simply assign it as follows:
var favorites = JSON.parse(window.localStorage['favorites'] || []);
In your addToFavorites function, after you push the added item to your array, add: window.localStorage['favorites'] = JSON.stringify(favorites);
In your deleteFromFavorites function, after you splice your array, add: window.localStorage['favorites'] = JSON.stringify(favorites);
You should be good to go with these three super simple steps!

Resources