Why is length of array not displaying in browser? - javascript

Code:
initialize: function() {
this.todos = [
{id: 100, text: 'Rich'},
{id: 200, text: 'Dave'}
];
},
activeTodos: function() {
this.todos = this.todos.length(function() {
return this.todos;
});
this.emitChange();
}
<p>Todo's Remaining: {this.activeTodos} </p>
activeItems: function(){
this.context.executeAction(activeToDosAction);
},
Explanation:
I am trying to print out the size of the array to the browser window (this can be seen in the <p> tags within the code). So far nothing is displaying and I cant figure out why. activeTodos should be calling the length of todos.
i can post more code if people require it. i am using reactjs hence the { } brackets within HTML

There are a couple of weird things there. If you only need to display the length of this.todos you can do something like this:
<p>Todo's Remaining: {this.todos.length} </p>

I think the problem here is that you pass a function to length. That is not required.
Your code should work if you change it to:
activeTodos: function() {
return this.todos.length;
}
I'm not sure what your function emitChange should be doing, so I omitted it for now in the code example. If you need it to be called, put it before the return as that is the last thing that will run in your function.

First, to access the length of an array you just have to do
myArray = [1, 2, 3];
myArray.length; //3
Moreover you have 2 fonctions : initialize and activeTodos where you use this.todos = ...
Without further explanations I assume these variables created with the keyword 'this' inside 2 different functions are out of score.
Verify that your this.todos refers to the same thing.

You do not need active.Todos() as .length is a built in function by itself.
Just do
Todo's Remaining: {this.todos.length}
Cheers.

Related

Attribute empty after assigning

I'm trying to refactor an object into an array to fit an API that I have to use.
function buildObject(data) {
console.log(data) // correct ouput
var jsonLayers = Object.entries({...data}).map(([k, v]) => {
console.log(`value : ${v}`) // correct output
return {
label: k,
pointMap: v,
color: v.layerColor?v.layerColor:tabList[tabList.map(i => i.tabName).indexOf(k)].layerColor?tabList[tabList.map(i => i.tabName).indexOf(k)].layerColor:defaultPOILayerColor,
}}
)
console.log(jsonLayers) // incorrect output
return jsonLayers
}
I have 2 scenarios in which I call this function but with the same data (console.log(data) logs the exact same result).
In the first scenario I obtain the desired result and in the second the pointMap attribute is an empty Array.
In the case where it does not work the console.log(`value : ${v}`) logs the correct value but when I log console.log(jsonLayers) the attribute pointMap is empty.
Any ideas on why this happens ?
Edit: as mentionned below this code works with sample data so I suppose this must comes to how the function is called. Everything runs in a UWA widget with jQuery
So for the context here is an idea of how they are called in both cases :
var data = {
a: { tabName: "tab1", layerColor: 1 },
b: { tabName: "tab2", layerColor: 2 },
};
$('#button1').on('click', function() {
ExternalAPI.Publish('some-external-address', buildObject(data));
});
$('#button2').on('click', function() {
let jsonData = buildObject(data);
//some manipulations of jsonData
ExternalAPI.Publish('some-external-address', jsonData);
});
It works on button1 but not on button2, they are clicked are at a different moment but data contains the same object when both are clicked
Edit2 :
Found the issue,
In the manipulations of jsonData I accidentally use slice(1,1) instead of splice(1,1) which empties the array.
What drove me crazy is that this modification was perform after the log but the var was log with the modification.
At least I learnt that spread operator does not deepcopy
Found the issue,
In the manipulations of jsonData I accidentally use slice(1,1) instead of splice(1,1) which empties the array.
What drove me crazy is that this modification was perform after the log but the var was log with the modification

Trying to make a function that takes in variable names as strings

New to Js, sorry if this is an obvious one.
I have some strings in my code that correspond to the names of variables. I'd like to put them into a function and have the function be able to make changes to the variables that have the same names as the strings.
The best example is where this 'string' is passed through from a data tag in html, but I have some other situations where this issue appears. Open to changing my entire approach too is the premise of my question is backwards.
<html>
<div data-location="deck" onClick="moveCards(this.data-location);">
</html>
var deck = ["card"];
function moveCards(location){
location.shift();};
Thanks!
A script should not depend on the names of standalone variables; this can break certain engine optimizations and minification. Also, inline handlers are nearly universally considered to be pretty poor practice - consider adding an event listener properly using Javascript instead. This will also allow you to completely avoid the issue with dynamic variable names. For example:
const deck = ["card", "card", "card"];
document.querySelector('div[data-location="deck"]').addEventListener('click', () => {
deck.shift();
console.log('deck now has:', deck.length + ' elements');
});
<div data-location="deck">click</div>
I think this can technically be done using eval, but it is good practice to think more clearly about how you design this so that you only access objects you directly declare. One example of better design might be:
container = {
obj1: //whatever this object is
...
objn:
}
function applyMethodToObject(object_passed){
container[object_passed].my_method();
}
I'm not sure I 100% follow what you're trying to do, but rather than trying to dynamically resolve variable names you might consider using keys in an object to do the lookup:
const locations = {
deck: ['card']
}
function moveCards (location) {
// if 'deck' is passed to this function, this is
// the equivalent of locations['deck'].shift();
locations[location].shift();
};
Here's a working demo:
const locations = {
deck: ['card 1', 'card 2', 'card 3', 'card 4']
};
function move (el) {
const location = el.dataset.location;
const item = locations[location];
item.shift();
updateDisplay(item);
}
// update the display so we can see the list
function updateDisplay(item) { document.getElementById('display').innerHTML = item.join(', ');
}
// initial list
updateDisplay(locations['deck']);
#display {
font-family: monospace;
padding: 1em;
background: #eee;
margin: 2em 0;
}
<div data-location='deck' onclick="move(this)">click to shift deck</div>
<div id="display">afda</div>
When you assign a value to an object in javascript you can access with dot or array notation. IE
foo = {};
foo.bar = "bar";
console.log(foo.bar);
console.log(foo["bar"]);
Additionally, global variables are added to the window object, meaning deck is available at window["deck"] or window[location] in your case. That means your moveCards function could do:
function moveCards(location) {
// perform sanity checks since you could change data-location="foo"
// which would then call window.foo.shift()
if (window[location]) {
window[location].shift();
}
}
That said, this probably isn't a great approach, though it's hard to say without a lot more context.

Why isn't a bound variable modified?

I have a problem changing the value of a bound variable in Vue (I am not sure whether this is due to my lack of knowledge of Vue, or of JavaScript in general, probably both).
The initialization of the Vue instance is done via the code below
var vm = new Vue({
delimiters: ['<<', '>>'],
el: "#index-container",
data: {
posts: [],
tags: {},
},
methods: {
switchAll: function(what) {
console.log(what)
console.log(JSON.stringify(this.tags))
_.forEach(this.tags, function (v, k) {
console.log(k)
this.tags[k] = false
})
console.log(JSON.stringify(this.tags))
}
},
(...)
Further down the script, values of tags are set with a constuction like Vue.set(vm.tags, tag, true) to ensure reactivity. They are then used in constrictions such as
<div class="tag on" v-for="(content, name) in tags" v-show="tags[name]" v-on:click="tags[name] = false">
Clicking on this div does change the value of the relevant element of the Object tags (initialized previously).
So far everything works as expected.
The problem is in the method switchAll. When I use it with a similar v-on:click() element (switchAll(false) for instance), it gets fired up but the console output is the following:
false
{"hello":true,"world":true}
hello
world
{"hello":true,"world":true}
My problem is that the last line should be {"hello":false,"world":false}.
I tried to use this.tags[k] = false and this.tags.k = false (though both are supposed to be equivalent) and I believe that the issue I have is that (apologies for the wording, I am new to JS) the keys in this.tags are "bare" in my case, and not "quoted". What my operation does is to add a new key, this time "quoted" (but then this.tags.k = false should be fine, as k is either hello or world (without quotes).
I am lost - what is wrong in my code, and why the inline code in the first (working) example of the div is fine?
Not an expert in lodash, but it seems to be a problem with callback function creating new this. There are several ways around this problem, like:
If you're OK with ES6, use lambda functions - they won't create new this:
_.forEach(this.tags, (v, k)=>{
console.log(k)
this.tags[k] = false
})
It can be useful to hold your this inside some method-wide variable:
var vm = this;
_.forEach(vm.tags, (v, k)=>{
console.log(k)
vm.tags[k] = false
})
Use vanillaJS object loop:
for (var k in this.tags) {
vm.tags[k] = false
}

Can I access properties of a scope object within itself using "this"?

I need to get the total of the following example:
$scope.fees = {
basic: 1,
premium: 2,
total: this.basic + this.premium
}
Why won't this work? It says this is undefined. Is there a way to achieve this without having to write out total: $scope.fees.basic + $scope.fees.premium.
I'd love if there was a way to shorten it.
EDIT: I'd actually have to add the total property outside of $scope.fees. $scope.fees.total = ...
You can use function ..
Hello {{ total() }}
function FeeController($scope) {
$scope.fees = {
basic: 1,
premium: 2,
};
$scope.total = function() {
return $scope.fees.basic + $scope.fees.premium;
};
}
Why this.basic doesn't work
this is evaluated in the context of the function that contains this statement. So this doesn't refer to the $scope.fees object, but to the controller.
Why total : $scope.fees.basic + $scope.fees.premium doesn't work either
At the moment that the expression $scope.fees.basic + $scope.fees.premium is evaluated the $scope.fees object doesn't exist yet, because you're in the middle of creating it. Therefore it will result in an error like "Cannot read property basic of undefined".
How to solve this
There isn't any solution other than what you've already found that results in the behaviour you want, so unfortunately you'll have to stick with it.
You might consider using the "controller as class" pattern which lessens your dependency on the $scope. You could do something like this:
app.controller('FeeCtrl', function () {
this.basic =1;
this.premium =2;
this.total = this.basic + this.premium;
});
Then you can inject this controller right into your dom:
<div ng-controller="FeeCtrl as fee">
{{ fee.total }}
</div>
There are more detailed instructions here
http://toddmotto.com/digging-into-angulars-controller-as-syntax/

How to unit test var variables

I test Angular application. This fact should not be of high importance here though.
My function to be tested looks like this:
$scope.showItem = function (item) {
if (item.selected) {
activeItems.push(item);
} else {
var index = _.indexOf(activeItems, items);
activeItems.splice(index, 1);
}
};
Function works as desired. Array activeItems is used in another function which after some modifications assigns the result to the scope.
The behavior is that if the item is not selected it is added to the array. If the item is already selected, it will be removed from the array.
it('should remove an item from the array', function () {
var activeItems = [{id: 1, selected: false}, {id:2, selected: true}];
var item = {
id: 1,
selected: false
};
expect(activeItems.length).toEqual(2);
scope.showItem(item);
expect(activeItems.length).toEqual(1); // FAIL!
// expects do not work for var variables. would work, if activeItems is assigned to the scope
});
If I assign activeItems to the scope (scope.activeItems instead of var activeItems), it all works; however, I believe that if the variable is not to be shown in the view, it should not be assigned to the scope.
First expect will work because is defined inside it block:
expect(activeItems.length).toEqual(2);
Second will not:
expect(activeItems.length).toEqual(1);
but it would for scope variable:
expect(scope.activeItems.length).toEqual(1);
My question is how to test that var value?
I'm not positive, as it's hard to tell your intentions, and if you have any other globals, but I think your error is that your using activeItems outside of it's scope., but again its hard to tell because you may have other globals causing the problem. If it is that activeItems is outside of it's scope, then it will reach an error at expect,scope, andexpect again. Then it won't effect the array, and it will stay as it was edited during showItem
Hope this helps!

Categories