What is the benefit of angular.copy? - javascript

I use angular.copy in some cases, like copying model default values to the form model, like this:
var customerModel = {
name: '',
point: 0
};
$scope.customer = angular.copy(customerModel);
function save() {
//... Reset $scope.customer after success submit
$scope.customer = angular.copy(customerModel);
}
... to prevent the default customerModel changed.
But why copy empty object or array? {} or []
I found in some codes used angular.copy on empty object or array. Why they don't assign empty object directly to the variable?
$scope.customer = {}; // or [] for array
If you use copy on empty object or array, could you explain the benefit?
What about ajax response ($http)?
And one more question, what did you do to ajax response? copy or assign it directly to a variable?
$http
.get('example.com/api/v1/get/customer/1')
.success(function(response) {
$scope.customer = angular.copy(response.data);
// or do you use $scope.customer = response.data ?
})
;
And if you used copy, what do you think happened to the response object?
Is it remain in the memory? or deleted automatically?

You copy an object to prevent other code from modifying it. (original object might change, but your copy won't see the changes)
If you were to do this:
$scope.customer = customerModel
... and some callback/service/whatnot changed customerModel, your scope would reflect this change. This is not always desirable, hence the need for deep copying.
Copying empty literal object
$scope.customer = angular.copy({})
// or
$scope.customer = {}
This doesn't make any difference. It's a new empty object every time. Note that it's very different from this:
this.customerModel = {};
$scope.customer = angular.copy(this.customerModel)
Copying ajax response data
Same rules apply. If you want to ensure that this object doesn't change from underneath you (which may happen if you also passed it to somewhere else, for example), you should copy it.

angular.copy creates deep copy of variable so that it would hold the reference of another variable
sometimes it happens when user dont want to use call by reference then deep copy comes in action According to your question
var customerModel = {
name: '',
point: 0
};
If you will use $scope.customer = angular.copy(customerModel); it will create deep copy of customerModel
As far as $http Service is concerned .Data is coming from response and If you you will assign It directly then there would be no effect of call by reference because data is coming from another source.
So I would rather assign it directly in case of $http.get()

Related

How do I update localStorage items?

I'm having a problem where the cached object doesn't resemble the correct data so I figured it I can push up the most uptodate version to the browser cache it will solve my problem.
How do you update your localStorage with a new object? So if I had a controller with that had an assessment updated. How can I push that assessment object up to the localStorage?
To do that with native JavaScript, you would do something like this:
localStorage.setItem('itemKey', JSON.stringify(yourObject));
var item = JSON.parse(localStorage.getItem('itemKey'));
Within the context of angular, you should make a localStorage service as a wrapper around localStorage and inject it into your service or controller. Or you could inject $window and use it directly off of that like: $window.localStorage
A response specifically for the asker of this duplicate question:
LocalStorage can only store strings, which is why you're stringifying your object before storing it. To manipulate the stored string as an object, you can pass it to JSON.parse (assuming it's properly JSON-formatted). Then to store the modified version, you need to convert it back into a string.
// Creates JSON-formatted object, converts it to a string and stores the string as "ship"
const ship = { name: "black pearl", captain: "Jack Sparrow" };
const originalStringifiedForStorgage = JSON.stringify(ship);
localStorage.setItem("ship", JSON.stringify(ship));
// Retrieves the string and converts it to a JavaScript object
const retrievedString = localStorage.getItem("ship");
const parsedObject = JSON.parse(retrievedString);
// Modifies the object, converts it to a string and replaces the existing `ship` in LocalStorage
parsedObject.name = "newName";
const modifiedndstrigifiedForStorage = JSON.stringify(parsedObject);
localStorage.setItem("ship", strigifiedForStorage);
If the object is in JSON format (not sure if Angular uses a different format) you could probably use the setItem() and getItem() methods to update and retrieve local storage items!
For example taken from the following post:
http://thejackalofjavascript.com/storing-objects-html5-local-storage/
var me = {name:'myname',age:99,gender:'myGender'};
localStorage.setItem("user",me);
//fetch object
console.log(localStorage.getItem("user")); // will return "[object Object]"
You can use full featured Angular module angular-local-storage
An AngularJS module that gives you access to the browsers local
storage with cookie fallback
set
myApp.controller('MainCtrl', function($scope, localStorageService) {
//...
function submit(key, val) {
return localStorageService.set(key, val);
}
//...
});
get
myApp.controller('MainCtrl', function($scope, localStorageService) {
//...
function getItem(key) {
return localStorageService.get(key);
}
//...
});
setItem wont work instead it will create another item in localStorage with the same name
Instead directly use
localStorage.item = (what ever the change that you want in the item)

How to make a DOM element update after an object it depends on changes in meteor?

I have an object in an array
var
sidelist = [
{
name:"MURICA",
types:[...]
}
];
I have a box that displays the object's name. Then I have a text field and a button. On button press the object's name gets set to text field value. But I don't know how to make the name in the box change accordingly.
As I understand putting the object in a session variable is not an option since I will not be able to modify properties of objects inside of it without resetting the whole session var. I tried it and failed.
html
<template name="asdf">
{{#with object}}
<div>{{name}}</div>
{{/with}}
</template>
js
Template.asdf.object = function() {
return Objects.findOne(...);
};
EDIT
I think I've got your question wrong, sorry. If you have a value in memory that you'd like to change and have the DOM updated, use dependencies:
html
<template name="asdf">
{{property}}
</template>
js
var property;
// Create new dependency object that will manage refreshing property value:
var _dep = new Deps.Dependency;
updateProperty = function(value) {
property = value;
// Whenever you change value of the property, call changed() function:
_dep.changed();
};
Template.asdf.value = function() {
// Within reactive function, call depend() to rerun the function
// each time the value is changed:
_dep.depend();
return value;
};
How about a different and in my opinion simpler solution - using a local collection for your data.
I am not sure exactly why do you keep that sort of data into an array, but if it is because you only need it on the client then you can instead create a local collection and have all the reactivity benefits without writing all that code for making the array reactive. The data stored in a local collection is never sent to the server, so no communication or storage overhead.
You'd do it like that:
Sidelist = new Meteor.Collection(null);
[EDIT] Put the above line in your client-side-only part of the code.
Notice the null parameter. This will give you a collection that is only stored on the client and is a regular Meteor reactive source. Then you go about using it in your code and html just as you would a normal collection.
Hope that helps.

Use $scope as json data with the $http service

I want to post everything that's on angular's scope service, its not much in my case but I don't want to create another object:
var model = angular.toJson($scope);
$http.post('/myUrl', model)
.success(function(data) {
});
However, it looks like $scope is a circular structure, because you can tell via: Converting circular structure to JSON when I use JSON.stringify or the string literal $Scope when I use the sample above.
Is there anyway to capture all of this data off of $scope?
This is my current hack, using underscore and underscore.string:
var toJs = function(item) {
var obj = {};
_.each(item, function(val, key) {
if (!_s.startsWith(key,'$')) {
obj[key] = val;
}
});
return obj;
};
then just passing:
toJs($scope)
You don't want to create new object, so a possible dirty solutions is to remove the circular reference and every other property you don't want to POST from $scope, call toJson, then put all previously deleted properties back to $scope.
Nest a child property on $scope and call toJson on that.
<input ng-model='email'>
becomes
<input ng-model='user.email'>
so that
$http.post('/url', angular.toJson($scope.user));

Can you bind a simple javascript array to your ember.js template?

I'm using ember.js RC1 + ember-data rev 11 (but I also need some plain ajax for configuration like models). I want to loop over a simple objects list and display the records (note -here I create just a basic array)
The content I have bound has the following custom find method defined
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
return this.records;
},
find: function() {
var self = this;
$.getJSON('/api/foo/', function(response) {
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
});
return this.records;
}
});
My other model uses this directly
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property()
});
Now inside my template
{{#each foo in related.listings}}
{{foo.name}}<br />
{{/each}}
The list loads up with whatever I put in the array by default (say I add a simple object using createRecord like so)
add: function(record) {
this.records.addObject(App.Foo.createRecord(record));
},
and when the template is rendered I see anything listed here... but as I put in the comments above, if I decide to remove records or null out the list that is bound it doesn't seem to reflect this in any way.
Is it possible to bind a simple array as I have and yet remove items from it using something basic such as splice? or even a drastic self.records = []; ?
self.records.splice(i, 1);
Even when I query the client manually after the splice or empty work it returns 0
console.log(App.Foo.all().get('length'));
Initially I see records, but then I see they are gone (yet the html doesn't change)
I understood your question this way, that the following remark is the point your are struggling with:
response.forEach(function(data) {
//say I want to kill everything in the array here for some strange reason...
self.records = [];
//the template still shows the record ... not an empty list ?
}, this);
You are wondering, why your template is showing no empty list? It's because you did not tell Ember when to update the template. You can tell Ember this way:
App.Related = DS.Model.extend({
listings: function() {
return App.Foo.find();
}.property("App.Foo.records.#each")
});
Now Ember knows, whenever something is added or removed from your array, it should update the listings property of your model. And therefore it knows that your view needs rerendering.
One additional remark to the orignal question regarding "simple javascript arrays". When you use Ember, you actually do not instantiate simple js arrays. When you declare:
var a = []; // is the same as -> var a = Ember.A();
Ember does some magic and wraps in an enhanced ember version of an array (Ember.NativeArray), which enables you to use such property dependency declarations mentioned above. This enables Ember to use ArrayObservers on those arrays, although they may feel like a plain JS Array.
You need to use the set method when you modify properties and get when you return them, or else Ember won't be able to do its magic and update the template.
In your case, there is an additional problem, which is that in find(), you return a reference to records before your asynchronous getJSON call replaces it with a new empty array. The calling method will never see the new array of records. You probably want to use clear() instead.
Your model should look something like this:
App.Foo = DS.Model.extend({
name: DS.attr('string')
}).reopenClass({
records: [],
all: function() {
// can't use 'this.get(...)' within a class method
return Ember.get(this, 'records');
},
findAll: function() {
var records = Ember.get(this, 'records');
$.getJSON('/api/foo/', function(response) {
records.clear();
// in this case my json has a 'foos' root
response.foos.forEach(function(json) {
this.add(json);
}, this);
}, this);
// this gets updated asynchronously
return records;
},
add: function(json) {
// in order to access the store within a
// class method, I cached it at App.store
var store = App.get('store');
store.load(App.Foo, json);
var records = Ember.get(this, 'records');
records.addObject(App.Foo.find(json.id));
}
});
Note that the addObject() method respects observers, so the template updates as expected. removeObject() is the corresponding binding-aware method to remove an element.
Here's a working jsfiddle.

How to initialize a Knockout viewmodel when initial viewmodel load is empty

I am using Knockout to implement a course list selection tool. I am using the approach below to populate the data (MVC3/Razor), so that when the viewmodel is initially populated, I have no issues working with each KO array (i.e. CourseList, ScheduleList). However, when the initial load from the server returns zero rows, meaning that the viewmodel 'ScheduleList' property is empty, then it's not possible to call any methods such as .push() or .removeAll(). Presumably this means that the observable array was never created since there was nothing to fill it with. When the model is filled, the ScheduleList property is populated with a List. What is the best way to instantiate it when the MVC action returns it as empty? There is a jsFiddle that seems to address it, but when I try to use the 'create' option, it renders my entire model blank. I am not sure what the syntax is of the 'create' option. The jsFiddle is here: http://jsfiddle.net/rniemeyer/WQGVC/
// Get the data from the server
var DataFromServer = #Html.Raw(Json.Encode(Model));
// Data property in viewmodel
var data = {
"CourseList": DataFromServer.CourseList ,
"ScheduleList": DataFromServer.ScheduleList
};
$(function() {
// Populate Data property
viewModel.Data = ko.mapping.fromJS(data);
// ko.applyBindings(viewModel, mappingOptions);
ko.applyBindings(viewModel);
});
When the initial page load does not populate ScheduleList, then the following code throws an error. If the initial page load contained data, then you could call .removeAll() and .push() etc.
var oneA= 'abc';
// push not working
this.Data.ScheduleList.push( oneA );
Set up your mapping parameters to make it so on creation, you give it a certain structure. Then it will do the updates for you.
What is most likely happening is that your DataFromServer doesn't actually contain a ScheduleList property at all. So when it is mapped, a corresponding property is never made. The mapper will only map existing properties to observables.
You need to set in your create options for the view model to add empty arrays when either array is not set. That way, your view model will end up with corresponding observable arrays in place.
By ensuring that CourseList or ScheduleList is an array, the mapped view model will map them as observableArray objects so your code will work as you expected.
var DataFromServer = {
'CourseList': [1,2,3]
//, 'ScheduleList': []
};
var dataMappingOptions = {
'create': function (options) {
var data = options.data;
data.CourseList = data.CourseList || [];
data.ScheduleList = data.ScheduleList || [];
return ko.mapping.fromJS(data);
}
};
viewModel.Data = ko.mapping.fromJS(DataFromServer, dataMappingOptions);
var data = {
CourseList: DataFromServer.CourseList || ko.observableArray([]) ,
ScheduleList: DataFromServer.ScheduleList || ko.observableArray([])
};

Categories