Can't directly assign new array instance to existing array from method - arrays

Issue is demonstrated in this plunker:
https://plnkr.co/edit/h1fFuY9VOZDLHO5JAM3o
I know that all arrays in Typescript are passed by reference. Thus it should be possible to assign new array to this reference.
But I've got a problem. If I want to replace one array with another from inside of the method in Angular 6.1.7 (TS 2.9.2), changes are not visible from outside of the method.
private assignArray(arrayToReplace: any[], replacement: any[]) {
arrayToReplace = replacement;
}
I've found workaround to this problem. Instead of assigning source array to target array, I remove all entries from target array and push all entries of source array in it.
private replaceArray(arrayToReplace: any[], replacement: any[]) {
arrayToReplace.splice(0, arrayToReplace.length);
for(let c of replacement) {
arrayToReplace.push(c);
}
}
Actually, this plunker behaves strange because console shows that array was changed even before invocation of replaceArray() method.
So it this behavior a bug of Angular/Typescript?

You are right that array is passed by reference, but you get the copy of the reference in your method. So when you modify the reference you modify its copy. There are no modifiers like out or ref that could help you. I suggest you return the new array that you try to return via a parameter.

Related

Nested Loop inside reactjs

I am new to React JS & currently trying to iterate a certain data to present the same in react js but not able to do the same. The data which looks something like this
Now, the final output should be look something like this in tabular format
The things which I tried are:-
The error which I am getting below one is for 1 image and for second, the code is not getting parsed.
[![error][5]][5]
So, how to achieve the desired output in react js
It looks like you're trying to use map onto an object, while you should use it on a collection. Maybe try something like this :
Object.values(listData.data).map((meetingRoom) => { // your code here });
This will allow you to use the content inside your data object as an array of objects.
Edit : Sorry, I didn't understand you need to access the key as well as the value. To achieve that you can simply use Object.entries which will return the key (Meeting Room 1, Meeting Room 2 in this instance) in the first variable and the array of items in the second variable.
Here's a quick example:
Object.entries(listData.data).forEach(([key, value]) => {
console.log(key, value);
// You could use value.map() to iterate over each object in your meeting room field array.
});
Note : you can also use a for (... of ...) loop like this instead of a forEach :
for (const [key, value] of Object.entries(listData.data)) {
console.log(key, value);
};
For more information about the Object.entries method, feel free to check the MDN Webdocs page about it here.

TypeScript Variable Undefined inside array.forEach()

I will try to explain my problem. I have an array of a particular object and, from this array, I want to create a new one with the value of a particular field of each object.
In order to achieve this, I tried to use array.forEach() method but I get a variable undefined error inside the forEach() function. This is my code:
var values: number[];
measures.forEach((measure)=>{
values.push(measure.value);
});
I've tried to declare the array values as public (and access this.values) and also to declare it in the function where I make the forEach and neither way worked.
Here is the error I get in the browser (angular CLI reports no problem):
ERROR TypeError: "values is undefined"
Try to initialize your array rather than just declare it as
values: number[] = [];
You do not need the var keyword in angular if you are declaring a variable directly inside a component class. If you need to do it inside a function, use let keyword. more information HERE

Mutating object properties within an array with Polymer

I not sure how to solve this issue. I am sure someone will know this very quickly.
I have an array of objects and modifying a property. I have a firebase listener 'child_changed'. When firebase is updated need to update the array. Here is the code below.
dbRefList.on('child_changed', function(snap) {
var len = this.grocerylist.length;
for(var i=len; i--;) {
if(this.grocerylist[i].key === snap.key) {
this.set(['grocerylist', i, 'selected'], snap.val().selected);
}
}
this.notifyPath('grocerylist', this.grocerylist.slice());
}.bind(this));
When the array is modified I want the template repeat-dom to trigger. I know this.set will not trigger array mutation sub properties but again I am not sure how to solve this. I done research and tried so many solutions.
I can force a render on the template dom-repeat but I would prefer the data binding way.
So this code (just the this.set you have in there now) should cause the value of grocerylist.i.selected to update inside the dom-repeat (assuming it's bound in there so it's actually showing up).
What behavior are you seeing? Are you trying to filter or sort the list based on the selected value? In that case, you might need to add observe="selected" on the dom-repeat.
(Also—have you confirmed that the child-changed callback is being called with the this value you expect—the element—rather than window or something else?)
You should be able to force a refresh by doing this.grocerylist = this.grocerylist.slice() or this.set('grocerylist', this.grocerylist.slice()); ... notifyPath doesn't work here because notifyPath doesn't change the value, it notifies the element about a prior change (the second argument is effectively ignored).

Clearing a text entry field after processing the input

Having completed CodeSchool's AngularJS course, I'm trying to go back through and use what I've learned to build a simple to-do app. I've gotten it to the point where I can add entries to a list, but I'm not able to clear the text entry field after I've used the input. Here's my JSFiddle.
Specifically, here's my addComment() function:
card.addComment = function(newComment) {
card.comments.push(newComment.text);
newComment = {text: ""};
};
And the corresponding Angular HTML:
<form ng-submit="card.addComment(newComment)">
<input name="comment" placeholder="Add comment" ng-model="newComment.text">
</form>
At first I was passing the data in as a string (<input ng-model="newComment"> instead of newComment.text), but then I remembered that strings get passed by value, not by reference. I figured newComment was being blanked in the function but not passed back through Angular to the document. But even after I changed newComment to an object so it would be passed by reference, it made no difference--clearing the value in JavaScript has no effect on the page. What am I doing wrong?
I remembered that strings get passed by value, not by reference.
This line of reasoning is incorrect. Everything in JavaScript gets passed by object reference. See this question: Is JavaScript a pass-by-reference or pass-by-value language?
Replacing the raw string with an object does potentially buy you some affordance, though. However, whether or not it's a string or an object (and indeed, a string is an object!), newComment = someValue will never work.
Instead, you'll need to set the text property on the object. This doesn't create a new object, it just modifies a property, so the reference is preserved. Try this instead:
newComment.text = '';
Try this:
card.addComment = function(newComment) {
card.comments.push(newComment.text);
newComment.text = "";
};
What you're doing is creating a new object on the scope and breaking the binding which was set up on the original object. By updating the text property only, you leave the binding intact and achieve your goal of clearing the input field to which it is bound.
You can rest a form which may do what you require
document.getElementById("myForm").reset();

Arrays in a Backbone.js Model are essentially static?

Why are arrays in a Backbone.js Model essentially static variables?
class exports.Content extends Backbone.Model
tags: []
then if i make a few models:
contentA = new Content()
contentB = new Content()
and add one string to each models' array:
contentA.tags.push('hello')
contentB.tags.push('world')
they both end up with the same array:
contentB.tags // ['hello','world']
but if it's a string, then there is no problem:
contentA.name = "c1"
contentB.name = "c2"
contentA.name // "c1"
The short answer
When you call extends to define your object, you are passing the new object's configuration in as an object literal. Objects are passed by reference, and the extends function only passes a reference to the tags array in to the new type definition.
As noted by others, you can correct this by assigning tags to a function. This works because a function delays the evaluation of the tags until the object is instantiated. There's nothing native in JavaScript that does this, but it's Backbone itself that recognizes tags as a function or a value.
The long answer
In spite of your code being in CoffeeScript, this comes down to a combination of a few things in JavaScript:
There are no classes in JavaScript
Object literals are evaluated immediately
JavaScript objects are passed around by reference
In JavaScript, there are no classes. Period. CoffeeScript gives you the notion of a class, but in reality, it gets compiled down to JavaScript which has no classes.
You can have types and type definitions (constructor functions) but not classes. Backbone provides a class-like definition, that looks similar to Java's "extend" class-based inheritance. It's still just JavaScript, though, which has no classes.
What we have, instead, is an object literal being passed in to the extends method. It's as if you write this code:
var config = {
tags: []
}
var MyModel = Backbone.Model.extends(config);
In this code, config is an object literal, or a hash, or a key/value pair, or an associative array. Whatever name you call it, it's the same basic idea. You end up with a config object that has a tags attribute. The value of config.tags is an empty array, [], which is itself an object.
Which brings us back to the short answer:
When you call extends to define your object, you are passing the new object's configuration in as an object literal. Objects are passed by reference, and the extends function only passes a reference to the tags array in to the new type definition.
As noted by others, you can correct this by assigning tags to a function. This works because a function delays the evaluation of the tags until the object is instantiated. There's nothing native in JavaScript that does this, but it's Backbone itself that recognizes tags as a function or a value.
"tags" is being declared on the Content.prototype - which is shared across all instances of Content. You probably need to use defaults instead: http://backbonejs.org/#Model-defaults. However, it should be noted that you must use a function (instead of a hash) when defining defaults, otherwise you still run into the same problem when using types which are passed by reference (e.g. Array).
var Content = Backbone.Model.extend({
defaults: function() {
return {
tags: []
};
}
});
benpickles is correct when it comes to Backbone models, and the way to do this in coffeescript in general is to initialize instance properties in the constructor:
class Foo
constructor: ->
#bar = []
As Derick Bailey mentioned, pass by reference is the problem. I solved the problem by cloning the Array (passing-by-value essentially):
var _events = window.active_model.get('events').slice(0);
_events.push({ key: "xxx", value: "yyy" });
window.active_field.set('events', _events);

Resources