Accessing NotMapped Computed Properties Using BreezeJS and AngularJS - angularjs

I've followed the examples laid out in http://www.breezejs.com/documentation/extending-entities, and the DocCode test labeled "unmapped property can be set by server class calculated property".
When I try and access the unmapped property, it has the same value as it did when it was set in the constructor. I have verified that the server is returning the unmapped property. It is possible that I don't know the proper way to access the unmapped property when using angularjs with breezejs.
See below on the two lines I've commented as "empty string, value expected".
breeze.NamingConvention.camelCase.setAsDefault();
breeze.config.initializeAdapterInstance("modelLibrary", "backingStore", true);
var ms = new breeze.MetadataStore();
var manager = new breeze.EntityManager({
serviceName: 'breeze/Projects',
metadataStore: ms
});
manager.enableSaveQueuing(true);
manager.fetchMetadata()
.then(function () {
var Sheet = function ()
{
this.previewUrl = "";
}
ms.registerEntityTypeCtor("Sheet", Sheet);
var query = new breeze.EntityQuery("Project")
.withParameters({ id: $stateParams.projectId });
manager
.executeQuery(query)
.then(function (data)
{
var p = data.results[0]; // project
var s = p.sheets[0];
console.log(s["previewUrl"]); // empty string, value expected
console.log(s.previewUrl); // empty string, value expected
})
.fail(function (e)
{
console.log(e.message);
alert(e);
});
});
Edit: Additional Info
I've figured out that this is caused by the camelCase naming convention. I was able to fix the bug in breeze.js by altering the getPropertyFromServerRaw function to this:
function getPropertyFromServerRaw(rawEntity, dp) {
return rawEntity[dp.nameOnServer || dp.isUnmapped && rawEntity.entityType.metadataStore.namingConvention.clientPropertyNameToServer(dp.name)];
}
Hopefully this fix can make it into the next version of breeze.js

Related

How can I delete a feature after a callback on openlayers

I try to create a code who can create a feature and delete it if it dosen't respect some rules. (in this exemple I just delete it just after his creation)
Below is my code, I create a map and I allow to create a polygon on. Next I try to delete it when an event drawend is triggered.
init(){
this.message = "";
var raster = new TileLayer({
source: new XYZ({
url: 'http://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',
attributions: [
'© Google','Terms of Use.'
]
})
});
this.source = new VectorSource();
this.vector = new VectorLayer({
source: this.source
});
this.map = new Map({
layers: [raster, this.vector],
target: 'map',
view : new View({
center: [0,0],
zoom : 4
})
});
this.addInteraction();
}
addInteraction() {
var self = this;
this.draw = new Draw({
source: this.source,
type: "Polygon"
});
this.draw.on('drawend', function (e) {
self.message = "";
self.map.removeInteraction(self.draw);
self.selectedFeature = e.feature;
self.removeSelectedFeature();
// This works but I don't like this solution
//setTimeout(function(){self.removeSelectedFeature()}, 1);
self.geoJSON = self.getCoord();
});
this.map.addInteraction(this.draw);
}
removeSelectedFeature()
{
this.source.removeFeature(this.selectedFeature);
this.selectedFeature = null;
this.validated = false;
this.addInteraction();
}
but When I try to execute this code, I got this error :
core.js:14597 ERROR TypeError: Cannot read property 'forEach' of undefined
at VectorSource.removeFeatureInternal (Vector.js:951)
at VectorSource.removeFeature (Vector.js:939)
at MapComponent.push../src/app/map/map.component.ts.MapComponent.removeSelectedFeature (map.component.ts:106)
at Draw.<anonymous> (map.component.ts:97)
at Draw.boundListener (events.js:41)
at Draw.dispatchEvent (Target.js:99)
at Draw.finishDrawing (Draw.js:872)
at Draw.handleUpEvent (Draw.js:578)
at Draw.handleEvent (Pointer.js:132)
at Draw.handleEvent (Draw.js:524)
I know it's because my feature isn't exist yet, and it will created after the event will finish. hence my question, how can I delete a feature after it was created ?
I tried using a event button but it's not a good idea I would like that the user don't need to click on a button for execute checks.
I tried using a setTimeout (as we can see in my commented code) but I don't like this solution (even if it works)
Anyone can explain me how can I do the job ?
Thanks in advance ;)
have you tried something like this in your drawend, if your check failed:
source.once('addfeature', (event) => {
const feature = event.feature;
source.removeFeature(feature);
});
this listens to the 'addfeature' event once and then removes the listener.

Jaydata saveChanges() counts tracked / changed entities, but doesn't send a batch request (with OData v4 Provider and Web Api)

by working with jaydata i am adding entities to a tree structure with deep nesting of entity-objects.
I attach the upper entity to the context and edit/add/remove related child entities. At the end i use saveChanges() with a promise.
The count-value passed to the promise tells that all changed entities have been counted correctly but saveChanges() didn't execute a batch request, persisting these entities.
So it feels like nothing else happens, but counting entities.
I post a small code example. I am quite sure, that the references of the entites are set correctly. (Working with jaydata, odata v4, web api and angularjs)
Is someone else having this problem with jaydata and found the reason?
Thanks for your help. =)
Greetings Paul
// Attach upper entity
DataService.jaydata.attach(viewModel.currentSkillTree.entity);
// Generating new entities
var newSkill = new DataService.jaydata.Skills.elementType({
Id: undefined,
Name: 'New skill',
Levels: [],
IconId: 47,
SkillTreeUsage: []
});
var newSkillLevel = new DataService.jaydata.SkillLevels.elementType({
Id: undefined,
ShortTitle: 'New level',
Skill: newSkill,
SkillId: undefined,
Level: 1,
RequirementSets: []
});
var newRequirementSet = new DataService.jaydata.RequirementSets.elementType({
Id: undefined,
SkillLevel: newSkillLevel,
SkillLevelId: undefined,
SkillTree: undefined,
SkillTreeId: viewModel.currentSkillTree.entity.Id,
});
var newSkillTreeElement = new DataService.jaydata.SkillTreeElements.elementType({
Id: undefined,
SkillTree: undefined,
SkillTreeId: viewModel.currentSkillTree.entity.Id,
Skill: newSkill,
SkillId: undefined,
Position: { X: x, Y: y }
});
// Completing object-references
viewModel.currentSkillTree.entity.Elements.push(newSkillTreeElement);
newSkill.Levels.push(newSkillLevel);
newSkill.SkillTreeUsage.push(newSkillTreeElement)
newSkillLevel.RequirementSets.push(newRequirementSet);
// Saving
DataService.jaydata.saveChanges()
.then(function (cnt) {
console.log('Saved entities:', cnt);
// The cnt-result in console is 4
// But no request was executed, nothing was saved
}, function (exception) {
console.log(exception); // Also no exception was thrown
});
So to not be that unkind.
The solution to solve the problem above to me, since i tried nearly every combination with entities (adding, attaching, .save(), .saveChanges(), object-references etc, figuring out it doesn't make sense anyway, it just acted the same way and seems to be so buggy), ended up within a workaround acting with classic nested async calls.
The solution was to save entities seperately within nested promises and to turn off the batch behavior of jaydata, to avoid double requests.
You can find the option within $data.defaults
$data.defaults.OData.disableBatch = true;
As result i am dealing now with good old nasty pyramids of doom, which at least gave the possibility back to save entities in the right order, with full control, the way the api needs it.
// Saving new SkillLevelRequirement connection
if (isConnectionGiven === false) {
// The first level of source skill where the target-skill-requirement will be added
var sourceSkillLevel = Enumerable
.From(sourceSkill.Levels)
.FirstOrDefault(null, function (x) {
return x.Level === 1;
});
// The last level of the target-skill to solve
var targetSkillLevel = Enumerable
.From(targetSkill.Levels)
.FirstOrDefault(null, function (x) {
return x.Level === targetSkill.Levels.length;
});
// First set of first level from source skill (will be used to add skilllevel-requirement)
var firstRequirementSet = sourceSkillLevel.RequirementSets[0];
// New RequirementAsignment
var newRequirementAssignment = new DataService.jaydata.RequirementAssignments.elementType({
RequirementSetId: firstRequirementSet.Id,
Order: 1
});
// New Requirement
var newRequirement = new DataService.jaydata.Requirements.elementType({
Title: requirementTypes.SKILL_CONNECTION,
RequirementOfIntId: undefined,
RequirementOfBoolId: undefined,
RequirementOfSkillLevelId: 0
});
// New RequirementOfSkillLevel
var newRequirementOfSkillLevel = new DataService.jaydata.RequirementsOfSkillLevel.elementType({
SkillLevelId: targetSkillLevel.Id,
});
// Loading symbol
showBusyIndicator();
newRequirementOfSkillLevel.save()
.then(function () {
newRequirement.RequirementOfSkillLevelId = newRequirementOfSkillLevel.Id;
newRequirement.save()
.then(function () {
newRequirementAssignment.RequirementId = newRequirement.Id;
newRequirementAssignment.save()
.then(function () {
// Loading symbol will be closed after tree reloaded
reloadCurrentTree();
}, function (exception) {
showJayDataExceptionModal(exception);
});
}, function (exception) {
showJayDataExceptionModal(exception);
});
}, function (exception) {
showJayDataExceptionModal(exception);
});
}
}
#jaydata developers: Thanks for 42 new grey hairs. I'm still at the point where i think i am using your tool wrong and jaydata could do so much better. Better up your documentation, sieriously. No desserts for you today.

EXT JS store.loadData() is not appending the data

I am trying to use store.loadData(data, true) to append data to an existing store but for some reason it is clearing the store and replacing it with the new data which should only happen if the boolean is set to false which it is not. Is there something I am missing that I need to do to make sure the data is appended to the old data and not replacing it entirely?
Edit Additional code. Currently I am pulling a row from a grid and creating a new window with additional information for that object that is pulled from a database. The idea is that all the possible data for the rows is stored in one store and then when the window appears the store has a filter added so that you only see data that pertains to that particular object. At some point I iterate every single object in the grid and check to see if it has data that was edited. Which is an issue if I only have data from the last object that was edited.
editSelectedNode: function(grid, rowIndex, colIndex){
var store = Ext.getStore('EditStore');
var win = Ext.create('BOMGeneratorSencha.view.EditMenu', {});
var item = grid.getStore().getAt(rowIndex).get('original');
console.debug(item);
win.show();
var el = win.getEl();
store.clearFilter(true);
console.debug(store.getCount());
if(store.getCount() == 0){
el.mask('Loading Values');
console.debug(store.getCount());
Ext.Ajax.request({
url : 'EditPart.jsp',
timeout: 300000,
params : {
item: item
},
success: function (response, opt) {
el.unmask();
var res = Ext.JSON.decode(response.responseText);
if (res.success) {
console.debug(res.results);
store.loadData(res.results,true);
console.debug(store);
}
else {
console.debug("JSON failure");
Ext.Msg.alert('Error', 'Invalid part number');
}
},
failure: function(response,options){
console.debug("major failure");
el.unmask();
Ext.Msg.alert('Error', 'Connection failed<br>' + response.responseText);
}
});
}
}
I have a code that is similat to your one. But when i get response, I dont use
store.loadData(someData)
instead I am using following steps to load data(piece of my code placed here):
success: function(response, opts){
var obj = Ext.decode(response.responseText)
,data = obj.data
,$ = Ext.ComponentQuery;
var store = Ext.create('MyApp.store.SomeStore',{
data : data
});
$.query('SomeGrid')[0].bindStore(store);
$.query('SomeGrid')[0].refresh();
}

Nested backbone model results in infinite recursion when saving

This problem just seemed to appear while I updated to Backbone 1.1. I have a nested Backbone model:
var ProblemSet = Backbone.Model.extend({
defaults: {
name: "",
open_date: "",
due_date: ""},
parse: function (response) {
response.name = response.set_id;
response.problems = new ProblemList(response.problems);
return response;
}
});
var ProblemList = Backbone.Collection.extend({
model: Problem
});
I initially load in a ProblemSetList, which is a collection of ProblemSet models in my page. Any changes to the open_date or due_date fields of any ProblemSet, first go to the server and update that property, then returns. This fires another change event on the ProblemSet.
It appears that all subsequent returns from the server fires another change event and the changed attribute is the "problems" attribute. This results in infinite recursive calls.
The problem appears to come from the part of set method of Backbone.Model (code listed here from line 339)
// For each `set` attribute, update or delete the current value.
for (attr in attrs) {
val = attrs[attr];
if (!_.isEqual(current[attr], val)) changes.push(attr);
if (!_.isEqual(prev[attr], val)) {
this.changed[attr] = val;
} else {
delete this.changed[attr];
}
unset ? delete current[attr] : current[attr] = val;
}
// Trigger all relevant attribute changes.
if (!silent) {
if (changes.length) this._pending = true;
for (var i = 0, l = changes.length; i < l; i++) {
this.trigger('change:' + changes[i], this, current[changes[i]], options);
}
}
The comparison on the problems attribute returns false from _.isEqual() and therefore fires a change event.
My question is: is this the right way to do a nested Backbone model? I had something similar working in Backbone 1.1. Other thoughts about how to proceed to avoid this issue?
You reinstantiate your problems attribute each time your model.fetch completes, the objects are different and thus trigger a new cycle.
What I usually do to handle nested models:
use a model property outside of the attributes handled by Backbone,
instantiate it in the initialize function,
set or reset this object in the parent parse function and return a response omitting the set data
Something like this:
var ProblemSet = Backbone.Model.extend({
defaults: {
name: "",
open_date: "",
due_date: ""
},
initialize: function (opts) {
var pbs = (opts && opts.problems) ? opts.problems : [];
this.problems = new ProblemList(pbs);
},
parse: function (response) {
response.name = response.set_id;
if (response.problems)
this.problems.set(response.problems);
return _.omit(response, 'problems');
}
});
parse gets called on fetch and save (according to backbone documentation), this might cause your infinite loop. I don't think that the parse function is the right place to create the new ProblemsList sub-collection, do it in the initialize function of your model instead.

How can I set nested array values in meteor publish function?

I have two collection "contents" and "units". In the content collection is a field "unitID" which refers to the unit-collection. In the meteor publish function I want to add the unit type name of all new created contents:
Meteor.publish("contents", function () {
var self = this;
var handle = Contents.find().observe({
changed: function(contentdoc, contentid) {
var UnitName = Units.findOne({_id: contentdoc.unittypeid }, {fields: {type: 1}});
self.set("contents", contentid, {'content.0.typename': UnitName});
self.flush();
}
});
}
This works but it creates a new attribut "content.0.UnitName" instead of inserting the attribute "UnitName" in the first element of the content array:
[
{
_id:"50bba3ca8f3d1db27f000021",
'content.0.UnitName':
{
_id:"509ff643f3a6690c9ca5ee59",
type:"Drawer small"
},
content:
[
{
unitID:"509ff643f3a6690c9ca5ee59",
name: 'Content1'
}
]
}
]
What I want is the following:
[
{
_id:"50bba3ca8f3d1db27f000021",
content:
[
{
unitID:"509ff643f3a6690c9ca5ee59",
name: 'Content1',
UnitName:
{
_id:"509ff643f3a6690c9ca5ee59",
type:"Drawer small"
}
}
]
}
]
What am I doing wrong?
this.set within Meteor.publish only works on the top-level properties of an object, meaning it doesn't support Mongo-style dotted attributes. You'll have to call set with the entire new value of the contents array.
Caveat: What I am about to say is going to change in a future release of Meteor. We're currently overhauling the custom publisher API to make it easier to use, but in a way that breaks back-compatibility.
That said...
It looks like what you're trying to do is build a server-side join into the published collection "contents". Here, for reference, is the current code (as of 0.5.2) that publishes a cursor (for when your publisher returns a cursor object):
Cursor.prototype._publishCursor = function (sub) {
var self = this;
var collection = self._cursorDescription.collectionName;
var observeHandle = self._observeUnordered({
added: function (obj) {
sub.set(collection, obj._id, obj);
sub.flush();
},
changed: function (obj, oldObj) {
var set = {};
_.each(obj, function (v, k) {
if (!_.isEqual(v, oldObj[k]))
set[k] = v;
});
sub.set(collection, obj._id, set);
var deadKeys = _.difference(_.keys(oldObj), _.keys(obj));
sub.unset(collection, obj._id, deadKeys);
sub.flush();
},
removed: function (oldObj) {
sub.unset(collection, oldObj._id, _.keys(oldObj));
sub.flush();
}
});
// _observeUnordered only returns after the initial added callbacks have run.
// mark subscription as completed.
sub.complete();
sub.flush();
// register stop callback (expects lambda w/ no args).
sub.onStop(function () {observeHandle.stop();});
};
To build a custom publisher that is joined with another table, modify the added callback to:
check if the added object has the key you want to join by
do a find in the other collection for that key
call set on your subscription with the new key and value you want to be published, before you call flush.
Note that the above is only sufficient if you know the key you want will always be in the other table, and that it never changes. If it might change, you'll have to set up an observe on the second table too, and re-set the key on the sub in the changed method there.

Resources