Original model is changing when changing the copied data in SAPUI5 - javascript

I am fetching some data (JSONArray) through an API and saving it in two models and an array variable. I am manipulating data in the array but it is changing the values in the models also. Below are my some code snippets :
onInit : function(){
this.addmodel = new sap.ui.model.json.JSONModel();
this.addmodel.setDefaultBindingMode(sap.ui.model.BindingMode.OneWay);
this.getView().setModel(this.addmodel, "Model");
this.originalModel = new sap.ui.model.json.JSONModel();
this.originalModel.setDefaultBindingMode(sap.ui.model.BindingMode.OneWay);
this.getView().setModel(this.originalModel, "OrgModel");
this.router.attachRoutePatternMatched(this._handleRouteMatched, this);
},
Controller.js :
_handleRouteMatched: function (evt) {
if (evt.getParameter("name") !== "BookMeal") {
return;
}
$.ajax({
url: "/Non_sap_create_requests/odata/MealSelfLocMealType",
method: "GET",
dataType: "json",
success: function (data) {
that.mCopiedArray = $.extend([], data.value);
that.originalModel.setData(data);
that.addmodel.setData(data);
},
error: function (err) {
}
});
onFromDateSelect: function (oEvent) {
if (Date.parse(fromDate) === Date.parse(currentDate)) {
var tempVal = that.mCopiedArray;
tempVal = formatter.mealContsraints(tempVal, currentDate, fromDate, currentTime, "null");
that.addmodel.setProperty("/value", tempVal);
} else {
that.addmodel.setProperty("/value", that.originalModel.getProperty("/value"));
}
},
});
});
In the above code I am saving data in the array mCopiedArray and in 2 models - addmodel and originalModel. I am manipulating the data in formatter.js. Changing the data in mCopiedArray is also changing the data in addmodel and originalModel.
formatter.js :
mealContsraints: function (value, currentDate, fromDate, currentTime, meal) {
if (fromdate === currentdate) {
while (ln--) {
if (value[ln].MealField === "Breakfast") {
value.splice(ln, 1);
break;
}
}
ln = value.length;
if (currentTime > '11:30:00') {
while (ln--) {
if (value[ln].MealField === "Lunch") {
value.splice(ln, 1);
break;
}
}
}
ln = value.length;
if (currentTime > '16:30:00') {
while (ln--) {
if (value[ln].MealField === "Eve Snacks") {
value.splice(ln, 1);
break;
}
}
}
if (currentTime > '18:00:00') {
while (ln--) {
if (value[ln].MealField === "Dinner") {
value.splice(ln, 1);
break;
}
}
}
}

$.extend([], data.value); does not create a deep copy. So if you modify an item in your array (e.g. change MealField from Dinner to Midnight Snack) it will also be changed in the model.
But if you modify the array itself (e.g. remove an item from the array) that should not affect the model.
I did a small snippet for this:
const mData = {value: [
{ MealField: "Dinner", id: 3 },
{ MealField: "Lunch", id: 2 },
{ MealField: "Breakfast", id: 1 }
]};
const oModel = new sap.ui.model.json.JSONModel(mData);
const aCopy = $.extend([], mData.value);
aCopy.splice(1, 1);
// arrays should be different
console.log(aCopy);
console.log(oModel.getProperty("/value"));
aCopy[0].MealField = "Midnight Snack";
// single item should be the same
console.log(aCopy[0]);
console.log(oModel.getProperty("/value/0"));
So the problem shouldn't be the formatter but something else?
Btw your formatter isn't a formatter. A real formatter should return a value and not have side effects (like modifing models/data).

The objects are working reference logic so you can use jquery.extend().

One way to make a deep copy of an object is to serialize it to json and the deserialize it to a new object:
Let objString = JSON.stringfy(obj):
Let newObject = JSON.parse(objString)
PS: that will work for serializable properties and if you have
A huge object you might run into performance issues.

Related

How to remove a cookie objects array in $each loop with jquery

I have a code for cookie object. i want to delete an object which $each loop. How can i do that .
var obj =
{
'deviceid': deviceId,
'full': cookiefull,
'stacked': istoggle
};
var cookieDevObjArray = GetCookieDevObjArray();
if (cookieDevObjArray.length > 0) {
var decoded = $.parseJSON($.cookie("cookieDevObjArray"));
$.each(decoded, function (index, value) {
if (value.deviceid == deviceId) {
//how to delete object from array//
value.deviceId={expire :-1}
}
});
}
$.cookie("cookieDevObjArray", JSON.stringify(obj), { expires: 30 });

Remove Local Storage JSON Object & Rebuild Array

I'm struggling with the task of removing an item from the LocalStorage...here is my LocalStorage data JSON.
{
"1461569942024" :
{"t_id":1461569942024,"t_build_val":"PreBuild1","t_project_val":"18"},
"1461570048166" :
{"t_id":1461570048166,"t_build_val":"PreBuild2","t_project_val":"17"}
}
here is what I was trying to do:
function removeItem(array, value) {
var idx = array.indexOf(value);
if (idx !== -1) {
array.splice(idx, 1);
}
return array;
}
var newData = removeItem(localStorage['data'], '1461569942024');
I would like to remove na object based on object key eg:1461570048166 and re-save whole array again to the LocalStorage.
Thanks
Try this code
var json = {
"1461569942024": {
"t_id": 1461569942024,
"t_build_val": "PreBuild1",
"t_project_val": "18"
},
"1461570048166": {
"t_id": 1461570048166,
"t_build_val": "PreBuild2",
"t_project_val": "17"
}
};
function deleteItem(input, key) {
delete input[key];
return input
}
localStorage.setItem("localStore", JSON.stringify(json));
localStorage.setItem("localStore", JSON.stringify(deleteItem(JSON.parse(localStorage.getItem("localStore")), '1461570048166')));
JSON.parse(localStorage.getItem("localStore"));

How to do ajax Post of one complex JSON object in a knockout observable array?

Can't quite figure out how to solve my current situation. I have a complex javascript objects representing an 'organization'. In the page, a list of these organizations are loaded up via ajax get request and pushed into a ko observable array, like so:
$.getJSON(apiBaseUrl + "?userId=" + userId, function (data) {
$.each(data, function () {
var dropdownOrg = new DropdownOrg();
var org = new Organization();
$.each(this, function (k, v) {
if (k === "UserGuid") {
org.userGuid = v;
}
if (k === "OrgId") {
dropdownOrg.orgId = v;
org.orgId = v;
}
if (k === "OrgName") {
dropdownOrg.name = v;
org.orgName = v;
}
if (k === "IsHiring") {
org.isHiring = v;
}
if (k === "Blurb") {
org.blurb = v;
}
if (k === "HqLocCity") {
org.hqLocCity = v;
}
if (k === "HqLocCountry") {
org.hqLocCountry = v;
}
if (k === "NumberOfEmployees") {
org.numberOfEmployees = v;
self.selectedEmployee(v);
}
if (k === "OrgImg") {
org.orgImg = v;
}
if (k === "Ownership") {
org.ownership = v;
self.selectedOwnership(v);
}
if (k === "Website") {
org.website = v;
}
});
self.orgDdl.push(dropdownOrg);
self.orgs.push(org);
});
});
self.OrgDdl is an observable array that only holds enough data so I can switch which organization I am viewing on the page (visibility toggling). self.orgs is the ko observable array that is holding all the data i need to save via an ajax PUT. The organization object looks like this (beware, is ugly):
function Organization(userGuid,
orgId,
orgName,
orgImg,
isHiring,
blurb,
numberOfEmployees,
hqLocCity,
hqLocCountry,
website,
ownership)
{
var self = this;
self.userGuid = userGuid;
self.orgId = orgId;
self.orgName = ko.observable(orgName);
self.isHiring = ko.observable(isHiring);
self.blurb = ko.observable(blurb);
self.numberOfEmployees = ko.observable(numberOfEmployees);
self.hsLocCity = ko.observable(hqLocCity);
self.hsLocCountry = ko.observable(hqLocCountry);
self.website = ko.observable(website);
self.ownership = ko.observable(ownership);
self.orgImg = ko.observable(orgImg);
}
Told you, pretty ugly, but I don't really know how to make that cleaner, and it binds alright, so good enough for now. In the current situation I have 3 organizations being loaded into the orgs observable array. In each of the organizations views I will be placing a button (haven't done it yet, need to figure this out first) that will take the currently visible org and post it.
I have a shell of a PUT request, but I am not sure how to get the current org that I am view out of the array.
self.updateOrganization = function () {
$.ajax({
type: "PUT",
contentType: "application/json; charset=utf-8",
url: apiBaseUrl + "?userId=" + userId,
data: ko.toJSON(?????),
dataType: "json",
success: function (msg) {
alert('success');
},
error: function (err) {
alert('Error updating information, please try again!');
}
});
Any idea?
Got it:
self.selectedOrgId.subscribe(function (currentOrgId) {
//alert(currentOrgId);
var org = ko.utils.arrayFirst(self.orgs(), function (item) {
if (currentOrgId == item.orgId) {
return item;
}
});
alert(ko.toJSON(org));
}, self);
So I can subscribe to changed to my selectedOrgId which gets updated each time I change my drop down list item. Then I can take that id in, use the ko.util.arrayFirst functions to find the item in my observable array that has that id.
Credit to: this guy

Azure mobile services and Ember.js

Hello!
I learning Ember.js as Web-Client Windows Azure Mobile Services from this tutorial.
When I write:
Tothevoidjs.ApplicationRoute = Ember.Route.extend({
// admittedly, this should be in IndexRoute and not in the
// top level ApplicationRoute; we're in transition... :-)
model: function(params) {
return Tothevoidjs.Secret.findAll();
}
});
I get this error:
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = meta(this), proto = m.proto;
m.proto = this;
if (initMixins) {
// capture locally so we can clear the closed over variable
var mixins = initMixins;
initMixins = null;
this.reopen.apply(this, mixins);
}
if (initProperties) {
// capture locally so we can clear the closed over variable
var props = initProperties;
initProperties = null;
var concatenatedProperties = this.concatenatedProperties;
for (var i = 0, l = props.length; i < l; i++) {
var properties = props[i];
Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin));
for (var keyName in properties) {
if (!properties.hasOwnProperty(keyName)) { continue; }
var value = properties[keyName],
IS_BINDING = Ember.IS_BINDING;
if (IS_BINDING.test(keyName)) {
var bindings = m.bindings;
if (!bindings) {
bindings = m.bindings = {};
} else if (!m.hasOwnProperty('bindings')) {
bindings = m.bindings = o_create(m.bindings);
}
bindings[keyName] = value;
}
var desc = m.descs[keyName];
Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty));
Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
Ember.assert("`actions` must be provided at extend time, not at create time, when Ember.ActionHandler is used (i.e. views, controllers & routes).", !((keyName === 'actions') && Ember.ActionHandler.detect(this)));
if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) {
var baseValue = this[keyName];
if (baseValue) {
if ('function' === typeof baseValue.concat) {
value = baseValue.concat(value);
} else {
value = Ember.makeArray(baseValue).concat(value);
}
} else {
value = Ember.makeArray(value);
}
}
if (desc) {
desc.set(this, keyName, value);
} else {
if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
this.setUnknownProperty(keyName, value);
} else if (MANDATORY_SETTER) {
Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
} else {
this[keyName] = value;
}
}
}
}
}
finishPartial(this, m);
this.init.apply(this, arguments);
m.proto = proto;
finishChains(this);
sendEvent(this, "init");
} has no method 'findAll'
My app.js:
var Tothevoidjs = window.Tothevoidjs = Ember.Application.create();
var client = new WindowsAzure.MobileServiceClient(
"link",
"key"
);
Tothevoidjs.WAMAdapter = Ember.Object.extend({
table: null,
init: function() {
this.table = this.get('table');
},
find: function(record, id) {
var query = this.table.where({
id: id
});
return query.read().then(function(data) {
Ember.run(record, record.load, data);
});
},
findAll: function(klass, records) {
var _self = this;
return _self.table.read().then(function(data) {
Ember.run(records, records.load, klass, data);
});
},
findQuery: function(klass, records, params) {
var query = this.table.where(params);
return query.read().then(function(data) {
Ember.run(records, records.load, klass, data);
});
},
createRecord: function(record) {
return this.table.insert(record.toJSON()).then(function(data) {
Ember.run(function() {
record.load(data.id, data);
record.didCreateRecord();
});
});
}
});
/* Order and include as you please. */
require('scripts/controllers/*');
require('scripts/store');
require('scripts/models/*');
require('scripts/routes/*');
require('scripts/views/*');
require('scripts/router');
My model file:
var attribute = DS.attr;
Tothevoidjs.Secret = DS.Model.extend({
id: attribute('number'),
body: attribute('string')
});
var client = new WindowsAzure.MobileServiceClient(
"link",
"key"
);
Tothevoidjs.Secret.adapter = Tothevoidjs.WAMAdapter.create({
table: client.getTable('secret')
});
And router:
Tothevoidjs.ApplicationRoute = Ember.Route.extend({
// admittedly, this should be in IndexRoute and not in the
// top level ApplicationRoute; we're in transition... :-)
model: function(params) {
return Tothevoidjs.Secret.findAll();
}
});
I do not understand what I did wrong. :(
Please tell me how to avoid this error or what I should read to understand it.
If you need to know version of Ember.js - it's from yeoman generator - 1.0.0
P.S. I'm newbie in web-dev.
Your tutorial is using the ember-model libray. But your current code use ember-data version 1.0.0.beta.x. Both are data libraries for ember, have similar api, but are different.
I recommend you to use the ember-model libray, so you will be able to finish the tutorial.
So, import the ember-model script, the source is here, make sure it comes after the ember.js script, and change your model definition to use ember-model:
var attribute = Ember.attr;
Tothevoidjs.Secret = Ember.Model.extend({
id: attribute('number'),
body: attribute('string')
});
I hope it helps

Publish/Subscribe to notification after published

I am trying to understand the Observable/Observer design pattern.
This is the code I have so far (code from Javascript Patterns book):
var publisher = {
subscribers: {
any: [] // event type: subscribers
},
publications: {
any: []
},
subscribe: function (fn, type) {
type = type || 'any';
if (typeof this.subscribers[type] === "undefined") {
this.subscribers[type] = [];
}
this.subscribers[type].push(fn);
if(typeof this.publications[type] === "undefined"){
return;
}
var pubs = this.publications[type],
i,
max = pubs.length
for(i = 0;i<max;i++){
this.subscribers[type][i](pubs[i]);
}
},
unsubscribe: function (fn, type) {
this.visitSubscribers('unsubscribe', fn, type);
},
publish: function (publication, type) {
var pubtype = type || 'any';
this.visitSubscribers('publish', publication, type);
if(typeof this.publications[pubtype] === "undefined") {
this.publications[pubtype] = [];
}
this.publications[pubtype].push(publication);
},
visitSubscribers: function (action, arg, type) {
var pubtype = type || 'any',
subscribers = this.subscribers[pubtype],
i,
max;
if(typeof subscribers === 'undefined') {
return;
}
max = subscribers.length;
for (i = 0; i < max; i += 1) {
if (action === 'publish') {
subscribers[i](arg);
} else {
if (subscribers[i] === arg) {
subscribers.splice(i, 1);
}
}
}
}
};
function makePublisher(o) {
var i;
for (i in publisher) {
if (publisher.hasOwnProperty(i) && typeof publisher[i] === "function") {
o[i] = publisher[i];
}
}
o.subscribers = {any: []};
o.publications = {any: []};
}
var paper = {
daily: function () {
this.publish("big news today");
},
monthly: function () {
this.publish("interesting analysis", "monthly");
},
yearly: function () {
this.publish("every year","yearly");
}
};
makePublisher(paper);
var joe = {
drinkCoffee: function (paper) {
console.log('Just read ' + paper);
},
sundayPreNap: function (monthly) {
console.log('About to fall asleep reading this ' + monthly);
},
onHolidays: function(yearly) {
console.log('Holidays!'+yearly);
}
};
paper.daily();
paper.monthly();
paper.yearly();
paper.subscribe(joe.drinkCoffee);
paper.subscribe(joe.onHolidays,'yearly');
paper.subscribe(joe.sundayPreNap, 'monthly');
I wonder if it is possible somehow to allow clients to receive the notifications
even if they registered themselves after such notifications had been broadcast.
Should I modify the publisher.subscribe and make it check if undefined and if yes publish the event type?
Thanks in advance.
*EDIT 1 *
I've added a publications object to save the publications in the publish function. I also check if there are subscribers for the publication type and if not I call return. Now I need to figure out how to notify them for older publication on subscribe.
*EDIT 2 *
New version of a working script added. Is it thought correct or the best way to do it?
If you want to allow subscribers to get the notifications after they are sent, what you need to do is just
For each different publication type, keep a list of all notifications you get for it.
(When publishing, add the publication to the appropriate list)
Change the subscribe function so that it also sends all the appropriate stored notifications to the late-arriving subscriber.
2.1 Perhaps you should also create a separate send_notification_to(arg, type, subscriber) method, to avoid code duplicatio with visitSubscribers

Categories

Resources