AngularJS create new scope from scope - javascript

I don't really understand why this piece of code works the way it does.
I call my service and get the expected data returned. I wish to store the pictures seperately, as I need to modify the array and parse it differently to new scopes. The thing I don't understand is, that as I now have created 2 scopes, product and productPictures - why are these still linked?
If I use Lodash to _.sort() productPictures, then the Images object inside scope.Product will also be altered, however I do not want this happen, and I don't know how to isolate the $scope.productPictures. I tried handling the scopes as simple js variables such as var product; and var productPictures, but as I set their data according to the response from the service, they are still linked.
$api.productpage.query({
id: parseInt($stateParams.productId)
}, function (data) {
$scope.product = data[0];
$scope.productPictures = data[0]['Images'];
});
Am I overlooking something super trivial? Should I rather manage the product images in a seperate service request?

Objects are passed by reference in javascript. It means that in this code
$scope.product = data[0];
$scope.productPictures = data[0]['Images'];
$scope.productPictures points to the same object (I guess this is array in your case) as data[0].Images.
To overcome this issue you need to clone object instead of just reference. You might want to check angular.copy method:
$scope.productPictures = angular.copy(data[0].Images);

You create two variables in the scope not two scope.
Object are passed by reference so if you modify one it modidy the other.
Maybe try to clone the object
edit use angular.copy() instead of jquery.extend()
function myclone ( obj){
return jQuery.extend(true, {}, obj);
}

Related

Having trouble with assigning scope to variable to manipulate

Attempting to assign a scope object to a JavaScript variable to do minor manipulation before sending to my API. However, any changes made to the JavaScript variable change the scope object.
var recruitingCallListOutput = $scope.RecrutingCallingList.Recruit;
// manipulation of recruitingCallListOutput
The manipulation actually still updates the scope object which is not desired. Feel I am not understanding something in AngularJS correctly. Is there a way to grab the data and detach it from the scope?
In your example, recruitingCallListOutput is a reference to $scope.RecrutingCallingList.Recruit (see https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0 for more detail.) You will want to make a copy of $scope.RecrutingCallingList.Recruit.
If Recruit is a shallow object, meaning no nested objects (property values are primitives only), you can simply do
var recruitingCallListOutput = Object.assign({}, $scope.RecrutingCallingList.Recruit);
If you have nested objects/arrays as property values, you'll need to deep copy. It's been a while since I have been in the angular world, but
var recruitingCallListOutput = angular.copy($scope.RecrutingCallingList.Recruit)
you could actually use angular.copy in both examples.
This is nothing to do with AngularJS. It's Javascript, and it's expected behaviour.
For example, if you open the browser console (F12->Console) right now and run this:
var foo = {x:1};
var copy=foo;
copy.x=2;
console.log(foo.x);
you will see {x:2} printed out.
This is the same behaviour you would expected for any object reference in Javascript, C#, Java, etc. Because you are making a reference and not a copy, any changes to the reference are actually changes to the original.
The simplest way to solve this problem in your case is to copy the values you are interested in from the item in question into a totally separate object and modify that copy.
e.g.
var recruitingCallListOutput = {
name: $scope.RecrutingCallingList.Recruit.name,
age:$scope.RecrutingCallingList.Recruit.age,
modifiedSomething: $scope.RecrutingCallingList.Recruit.something + 42 //or whatever modifications you need to make
...and so on.
};
There are ways to "clone" an object in Javascript but unless your object is really really complex I would be careful. And consider if you really need all of the properties of the original object anyway, perhaps you only need to send some of them to your backend.

In declaring properties for a polymer element, why is value sometimes a function that returns {}?

The part I'm curious about is why use
value: function() { return {}; }
instead of
value: {}
Here's the sample code:
<script>
Polymer({
is: 'polymer-demo',
properties: {
data: {
type: Object,
notify: true,
value: function() { return {}; }
}
},
});
</script>
This is explained in the example:
When initializing a property to an object or array value, use a function to ensure that each element gets its own copy of the value, rather than having an object or array shared across all instances of the element.
Although you can use {}, this will be the same object shared by each element, so that if the value is mutated for one element, all other elements would see the same change applied to it. This is not what you want to happen. By using a function, the function will be called for each element, and each call will produce a new, separate object. Then a mutation will only apply to that single element without affecting the others.
I feel it is the same reason that vuejs requires their data component to be rendered as a function rather than the object that it is:
In the basic examples, we declare the data directly as a plain object.
This is because we are creating only a single instance with new Vue().
However, when defining a component, data must be declared as a
function that returns the initial data object. Why? Because there will
be many instances created using the same definition. If we still use a
plain object for data, that same object will be shared by reference
across all instance created! By providing a data function, every time
a new instance is created, we can simply call it to return a fresh
copy of the initial data.
Thus, making sure that the data being given to the component is always indicative of a fresh "load"
Hope this helps!
Usually this is done in cases where you want to show, that you can not only return a fixed value but calculate a dynamic value and return this instead but you were to lazy to return something more than {}. And sometimes the things you are declaring a used in some place by the framework and are there expected to be a function returning an object, and not just a fixed value or object.
You can find out by changing the code, executing it, and watching for errors or different behaviour.

Angular JS: how to "load" an individual record from a json dataset?

I have json data, an array of 50 objects representing people. Each one has parameters like id and lastName.
I load this into my controller via a resolve, EmployeeResolve, and into a variable _this.employees
I also load via $state params from a previous page a rowNumber variable that holds the ID of the record the user clicked on: _this.rowNum = $stateParams.id;let's say the id is 5.
I would like to assign to a variable now the object number 5 (for want of a better way of explaining) so that in my HTML I can bind to it as in {{controller.lastName}}
What's the syntax for getting the 5th item out of employees?
UPDATE
After several helpful comments and answers, I've gotten this far (people are now packages):
_this.recordNum = Number($stateParams.id);
_this.packages = PackagesResolve;
_this.currentPackage = _this.packages.filter(function(pkg) {
return pkg.id === _this.recordNum;
});
$log.debug('package from filter', _this.currentPackage[0].status);
Note though, I expected after all this for _this.currentPackage to contain an object, so I could simply bind to its props in the html as in currentPackage.last_name But it does not. It's a resource and I need to use the above _this.currentPackage[0].status in the log statement to get anything. And that's not going to allow binding.
A colleague suggested modifying my resolve as such
PackagesResolve: function($log, MockDataFactory) {
return MockDataFactory.query({filename: 'packages'}).$promise.then(function(response) {
return response;
});
}
Adding the whole $promise.then part. No real difference.
To reiterate what I am trying to do:
PackagesResolve is getting a json array of 50 objects. I want to be able to get the CURRENT object when its row in a table of that json is clicked.
And no, #jdvp it's not a duplicate of that other post at all. I need to do this with Angular, not jquery or straight js.
If I'm understanding your issue correctly: the object returned by resolve is the resolved promise. The "data" of the resolved promise, which in this case would be the expected array of people info, is stored inside resolve.data. So for e.g. you have EmployeeResolve, you can reference the array and store it using:
Editing based on comments:
// Assuming you've done all error checking...
_this.employees = EmployeeResolve.data;
// Now _this.employees has the array of people info.
$scope.controller = {};
$scope.controller.variableName = _this.employees[$stateParams.id];
// Now, you can access your object in your template using controller.variableName.
Now although I wouldn't recommend writing code like that in your final version, I'm sure you get the gist. ;)
Additional notes: The reason I'm creating an empty object and storing it as controller on the scope is because your question stated it. I am assuming you have your own reasons for wanting to namespace your variable inside of controller.
Hope this helps!

Am I binding in angularfire (angular+firebase) correctly?

Hi y'all I'm trying out angular and firebase together for some cool 3 way binding action, but I'm running into some problems with binding. I don't really know how the objects ($scope and $firebase) should look like before being binded together. Right now, if I change through firebase, I am able to to see the change in my DOM almost immediately, but I need to be able to do some crud from DOM to FB for some real 3 way binding. Maybe I'm doing this completely wrong. :/
Here's what I have:
html (this creates a huge grid of 400 squares based off of my $scope.myGrid which is a $scope object referencing a $firebase object)
<div class="square" ng-repeat="(position, hex) in myGrid" style="background-color:{{hex}}" ng-click="squareClick({{position}})">
my Controller (anonymous fxn makes my $scope.myGrid object.)
$scope.paletteColor = "#f00";
//FIREBASE
var ref = new Firebase("https://MyAPP.firebaseio.com/");
//angularfire ref to the data
var sync = $firebase(ref);
//download the data into a local object
var syncObject = sync.$asObject();
console.log(syncObject); // firebase object is composed of root node with 400 child nodes with key:value like 01-01:"#f00", 01-02: "#ff0" which is exactly how my $scope.myGrid object looks like
$scope.myGrid = syncObject;
// binding Part taken from the docs which is a huge mystery to me.
// syncObject.bindTo($scope, "myGrid").then(function(){
// console.log($scope.myGrid);
// $scope.myGrid. = "baz";
// ref.$set({foo:"baz"});
// });
You do need to use the syncObject.bindTo syntax as you listed in the comment. This sets up the three-way binding. See this note from the official documentation below:
While 3-way bindings can be extremely convenient, be careful of trying to use them against deeply nested tree structures. Stick to practical uses like synchronizing key-value pairs.
If you need more functionality than basic key-value pairs you may want to look into extending Firebase factories. You can find it in the documentation at https://www.firebase.com/docs/web/libraries/angular/guide.html#section-extending-factories.

Clone of an object inherits future changes that are made to first object

Lets say you have this object:
mainObj = {
foo1: 'bar1',
foo2: 'bar2',
foo3: 'bar3'
}
Now I want to make a close of this object by doing cloneObj = mainOb. Now we have two identical objects.
When I change the value of mainObj.foo1 = 'lolcats' after I made the clone then for some reason cloneObj.foo1 = 'lolcats'
I tested this in Chrome's console on a much more complex object. I know for certain that there's nothing in my script that would keep making the two objects sync up. I even made sure of this by creating random names for the cloneObj.
Is this behavior done on purpose or am I experiencing some sort of bug? Or am I just missing something very fundamental here?
You didn't clone the initial object when you just did cloneObj = mainOb, you are actually passing a reference to mainOb that can be accessed via the cloneObj variable name. You therefore have two variable names referencing the SAME object.
when you assign/clone object such as cloneObj = mainOb you just create another reference to the same object. Both mainOb and cloneOb points to the same object thus, a change in one is reflected on another.

Categories