change tracking in manager after saveChanges BreezeJs - angularjs

I'm using Breeze and Angular on client and NHibernate on server.For business logic reasons I need to know if some object has changes after it has been saved.For that reasons I'm calling
manager.hasChanges(['ArrayOfTypes']);
In case when object is loaded from db it works as expected(returns true/false),
but after object is saved through
manager.saveChanges(['ArrayOfTypes']);
I get this error:
TypeError: Cannot read property 'hasChanges' of undefined
The error gets thrown here in breeze.js file:
proto._hasChangesCore = function(entityTypes) {
entityTypes = checkEntityTypes(this, entityTypes);
var entityGroups = getEntityGroups(this, entityTypes);
return entityGroups.some(function (eg) {
return eg.hasChanges();
});
};
I'm expecting 32 entityGroups to return and there are indeed 32 elements in array, but 25 of them are undefined.For now I've made a temp fix which does not feel right at all:
proto._hasChangesCore = function(entityTypes) {
entityTypes = checkEntityTypes(this, entityTypes);
var entityGroups = getEntityGroups(this, entityTypes);
return entityGroups.some(function (eg) {
if (eg !== undefined) {
return eg.hasChanges();
}
else {
return false;
}
});
};
Is there a way to deal with problem in other way?
Thanks

This happens when the one or more of the entity types is not represented by an EntityGroup in the entity manager. But you should be able to pass in any type name and not have it blow up.
This is a bug in Breeze, which is finally fixed. The fix will be in the next release.

Related

modifying object in componentDidMount() [duplicate]

I’ll start with the code:
var s = ["hi"];
console.log(s);
s[0] = "bye";
console.log(s);
Simple, right? In response to this, the Firefox console says:
[ "hi" ]
[ "bye" ]
Wonderful, but Chrome’s JavaScript console (7.0.517.41 beta) says:
[ "bye" ]
[ "bye" ]
Have I done something wrong, or is Chrome’s JavaScript console being exceptionally lazy about evaluating my array?
Thanks for the comment, tec. I was able to find an existing unconfirmed Webkit bug that explains this issue: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: now fixed!)
There appears to be some debate regarding just how much of a bug it is and whether it's fixable. It does seem like bad behavior to me. It was especially troubling to me because, in Chrome at least, it occurs when the code resides in scripts that are executed immediately (before the page is loaded), even when the console is open, whenever the page is refreshed. Calling console.log when the console is not yet active only results in a reference to the object being queued, not the output the console will contain. Therefore, the array (or any object), will not be evaluated until the console is ready. It really is a case of lazy evaluation.
However, there is a simple way to avoid this in your code:
var s = ["hi"];
console.log(s.toString());
s[0] = "bye";
console.log(s.toString());
By calling toString, you create a representation in memory that will not be altered by following statements, which the console will read when it is ready. The console output is slightly different from passing the object directly, but it seems acceptable:
hi
bye
From Eric's explanation, it is due to console.log() being queued up, and it prints a later value of the array (or object).
There can be 5 solutions:
1. arr.toString() // not well for [1,[2,3]] as it shows 1,2,3
2. arr.join() // same as above
3. arr.slice(0) // a new array is created, but if arr is [1, 2, arr2, 3]
// and arr2 changes, then later value might be shown
4. arr.concat() // a new array is created, but same issue as slice(0)
5. JSON.stringify(arr) // works well as it takes a snapshot of the whole array
// or object, and the format shows the exact structure
You can clone an array with Array#slice:
console.log(s); // ["bye"], i.e. incorrect
console.log(s.slice()); // ["hi"], i.e. correct
A function that you can use instead of console.log that doesn't have this problem is as follows:
console.logShallowCopy = function () {
function slicedIfArray(arg) {
return Array.isArray(arg) ? arg.slice() : arg;
}
var argsSnapshot = Array.prototype.map.call(arguments, slicedIfArray);
return console.log.apply(console, argsSnapshot);
};
For the case of objects, unfortunately, the best method appears to be to debug first with a non-WebKit browser, or to write a complicated function to clone. If you are only working with simple objects, where order of keys doesn't matter and there are no functions, you could always do:
console.logSanitizedCopy = function () {
var args = Array.prototype.slice.call(arguments);
var sanitizedArgs = JSON.parse(JSON.stringify(args));
return console.log.apply(console, sanitizedArgs);
};
All of these methods are obviously very slow, so even more so than with normal console.logs, you have to strip them off after you're done debugging.
This has been patched in Webkit, however when using the React framework this happens for me in some circumstances, if you have such problems just use as others suggest:
console.log(JSON.stringify(the_array));
Looks like Chrome is replacing in its "pre compile" phase any instance of "s" with pointer to the actual array.
One way around is by cloning the array, logging fresh copy instead:
var s = ["hi"];
console.log(CloneArray(s));
s[0] = "bye";
console.log(CloneArray(s));
function CloneArray(array)
{
var clone = new Array();
for (var i = 0; i < array.length; i++)
clone[clone.length] = array[i];
return clone;
}
the shortest solution so far is to use array or object spread syntax to get a clone of values to be preserved as in time of logging, ie:
console.log({...myObject});
console.log([...myArray]);
however be warned as it does a shallow copy, so any deep nested non-primitive values will not be cloned and thus shown in their modified state in the console
This is already answered, but I'll drop my answer anyway. I implemented a simple console wrapper which doesn't suffer from this issue. Requires jQuery.
It implements only log, warn and error methods, you will have to add some more in order for it to be interchangeable with a regular console.
var fixedConsole;
(function($) {
var _freezeOne = function(arg) {
if (typeof arg === 'object') {
return $.extend(true, {}, arg);
} else {
return arg;
}
};
var _freezeAll = function(args) {
var frozen = [];
for (var i=0; i<args.length; i++) {
frozen.push(_freezeOne(args[i]));
}
return frozen;
};
fixedConsole = {
log: function() { console.log.apply(console, _freezeAll(arguments)); },
warn: function() { console.warn.apply(console, _freezeAll(arguments)); },
error: function() { console.error.apply(console, _freezeAll(arguments)); }
};
})(jQuery);

Angular - Objects seem bound to eachother but arent

I have an edit page where the user can edit a file in the system, and then save it. When loading the file, I make two objects out of the result, one is bound to the view and the other I wish to keep (in its original state) until "save" is clicked, and then use it to compare vs the view-bound object, to see if any changes have been made.
So, when the page loads, this is being run
$http.get('/api/files/' + $stateParams.id)
.then(function (result) {
vm.fileTemp = result.data;
vm.fileTempCopy = result.data;
The fileTempCopy is not being touched or referenced by anything in the view or elsewhere in the controller, except in the save-method, where i check if they are alike or not. But somehow, both of them are updated when i make changes to the input fields (as if they were both used as ng-model for the inputs).
if(vm.fileTemp === vm.fileTempCopy)//in save-function
is always true, and their fields are exactly the same.
Why does this happen and how can I solve it?
Using the assignment operator, you are actually just referencing the original array. Arrays are reference types. That means, that they don't actually store values, they only store references to those values. What you where doing is copying a reference to a memory location, meaning that any changes to the memory at that location (including removing elements) will be reflected in both arrays.
So you will want to do this instead:
vm.fileTemp = angular.copy(result.data);
vm.fileTempCopy = angular.copy(result.data);
here is a very basic approach to checking an object's "value equality".
function isEquivalent(a, b) {
// Create arrays of property names
var aProps = Object.getOwnPropertyNames(a);
var bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
//After your update Outputs: false
console.log(isEquivalent(vm.fileTemp, vm.fileTempCopy));

Re-use array collection on multiple pages

I currently have 2 pages, page1.php and page2.php, each of the pages uses a controller that completes its own functions etc.
However there are tabs within the pages that are exactly the same that gets a promise from a factory within the module. The lists are exactly the same except for querying on different IDs. For example both controllers have this:
pageListFactory.getData().then(function (result) {
$scope.BasicItems = result; $scope.basicItemsList = [];
angular.forEach($scope.BasicItems, function (BasicItem) {
angular.forEach(BasicItem['SomeInnerArray'], function (BasicSomeInnerItem) {
if (BasicSomeInnerItem == VARIABLE_THAT_CHANGES) {
$scope.basicItemsList.push({
ID: BasicItem.ID, Title: BasicItem.Title
});
}
});
});
});
So this code is used, and the VARIABLE_THAT_CHANGES is just what changes each time. However as I said it is used on multiple pages, is there a way to create just one function call and then each page just can call a specific bit and send the variable with it?
I tried using $rootScope but of course this just clogs and slows, and I'm not exactly happy on constantly using $rootScope to pass the $scope.basicItemsList around as the list could get quite big.
So is there any way to reuse this code without just copying and pasting it into each controller?
Sure you can re-use it...
Convert the factory to a service, its basically a name change, create a local variable to store the data, update the data on first call, and then grab the data if it exists on the second call.
.service('myService', ... stuff ... { // I suggest using a service, as I don't know if a factory would act as a singleton
var myData = null;
return {
getData: function(){
if(myData != null)
return myData; // returns data
else {
return $http()... // ajax call returns promise
}
},
setData: function(dataToSet){
myData = dataToSet;
}
}
Then your controllers:
//controller 1
var promiseOrData = pageListFactory.getData();
if(promiseOrData instanceOf Array){ // or whatever data you expect
$scope.BasicItems = promiseOrData;
....
}
else { // should be a promise
promiseOrData.then(function (result) {
pageListFactory.setData(result); // set the data once you get it.
$scope.BasicItems = result; $scope.basicItemsList = [];
....
}
}
In controller 2 you only need to get the data as the returned data will be an array, not a promise
On top of all this, write a directive which will process the data when you pass it along, then you can pass the variableThatChanges and have the directive take care of it.
Use services and write the function in that service and pass the variable VARIABLE_THAT_CHANGES into it. By this you can reuse the code.

Iterating through parent entity to return array of child entities

I am trying to build an array of entities from a server query that exist in an object arrays
The diagram below illustrates my model:
In my datacontext, I've applied the following code:
function getByDboardConfig(dboardConfig) {
var busUnitDims = [];
var busUnitsTotalCount = dboardConfig.busUnits.length;
var buCount = 0;
dboardConfig.busUnits.forEach(function (busUnit) {
eq.from('BusUnitDimensions') // eq = breeze.EntityQuery
.where('busUnitId', '==', busUnit.id)
.using(em).execute() // em = EntityManager
.to$q(succeeded, failed); // using Angular, thus to$q
});
function succeeded(data) {
buCount++;
data.results.forEach(function (result) {
busUnitDims.push(result);
});
if (buCount === busUnitsTotalCount) {
console.log(busUnits.length);
return busUnitDims;
}
}
}
When I log to the console as show the length of the array, I get the correct entity count, but when I return the result of this call to my controller I get undefined. Not understanding why?
I've tried returning $q.when(busUnitDims) as well but I still get undefined.
The problem with your code is that the function is not returning anything, even if you relocate the return line outside the succeeded block
, it may return before filling the array is finished(notice you're executing the query asynchronously )
Neverthless, looking at your code; I take it you are getting the busUints and then query for each of their line items BusUnitDimensions separately;
that could mean many roundtrips to the server.
Anyways, you aim to fetching busUnits along with their related BusUnitDimensions (eager loading).
Although you didn't provide an idea of how your view model or your controller looks like; I assume you have one view for DboardConfig and another view for both related busUnits and BusUnitDimensions
so your workflow is:
Get a list of DboardConfigs
for each DboardConfig, load it's busUnits along with BusUnitDimensions
Hence, your function could be :
function getByDboardConfig(dboardConfig) {
return eq.from("busUnits")
.where("dboardConfigId", "==", dboardConfig.id)
.expand("BusUnitDimensions")
.using(em).execute() // em = EntityManager
Then inside your controller:
$Scope.getBusUnitsandDimentions = function () {
dataservice.getByDboardConfig($Scope.dboardConfig)
.then(succeeded)
.fail(failed);
};
function succeeded(data) {
$Scope.busUnits = [];
data.results.forEach(function (result) {
$Scope.busUnits.push(result);
});
}
}
}
This way, you can remove the coding for fetching busUnits since we've already fetched it with it's related child table.
Simply put:
busUnits to get your array of bus units for a specified DboardConfig
busUnits.BusUnitDimensions to get it's related dimensions

Variable array/object in one file changes when you change it in a callback in another file

I have two files in Node.js where one requires the other one.
variable_test.js:
TEST = require('./variable_test_external.js');
TEST.get(function(myVariable) {
var changeMeVariable;
console.log(myVariable);
changeMeVariable = myVariable.epicVariable;
changeMeVariable.evenEpicerVariable = "test3";
TEST.get(function(myVariable2) {
console.log(myVariable2);
});
});
variable_test_external.js:
var testVariable = new Array({epicVariable: {evenEpicerVariable: "test1"}}, {epicVariable: {evenEpicerVariable: "test2"}});
exports.get = function(callback) {
callback(testVariable[1]); // I know that the return is unnecessary in this example but in my real application I have return there for compactness.
}
This is the output when run in Node.js with node variable_test.js:
{ epicVariable: { evenEpicerVariable: 'test2' } }
{ epicVariable: { evenEpicerVariable: 'test3' } }
The console.log(myVariable) changes in the two TEST.get's. Why does this happen?
This is a reference copy, not a value copy. You got the object from the array, NOT a copy of them.
changeMeVariable = myVariable.epicVariable;
This would have to fix yout problem
// PSEUDO CODE, i don't know the correct syntax
changeMeVariable = {
epicVariable = myVariable.epicVariable
};
The answer in my case is the following based on the links at the bottom:
changeMeVariable = JSON.parse(JSON.stringify(myVariable.epicVariable));
But, it's much better to manually copy it like the bottom most link like this:
changeMeVariable = {
evenEpicerVariable: myVariable.epicVariable.evenEpicerVariable
}
n0m's answer is similar but if the epicVariable.evenEpicerVariable contained an object that object's reference would still be linked! (I tested it)
References:
What is the most efficient way to deep clone an object in JavaScript?
http://jsperf.com/cloning-an-object/3

Resources