breezejs how to using as datasource - breeze

i'm using breezejs for some time now, and i'm happy about it and it's rich functionality , but the problem with breezejs is that i cant use it as datasource almost for anything.
there is no grid that you could show data and not losing functionality, and you cant use your array of entities as normal array. (so you cant use it as datasource for dropdown ...)
so for showing data in UI i end up converting data to normal array and losing Breeze functionality (like track change) and before save converting them back.
some code like this one for converting to normal array:
if(data.length>0)
{
var names = Object.getOwnPropertyNames(data[0]._backingStore);
var columns: string[] = [];
for (var i = 0; i < names.length; i++)
{
columns.push(names[i]); //Getting columns name
}
var newArray = [];
data.forEach((item, index, array) => {
newArray.push(item._backingStore);
});
}
my question is how do you show your data in UI using breezejs properly?
(i'm using angualr (hottowel))

Assuming you're trying to solve issues like this:
The grid doesn't like the entityType and entityAspect properties on Breeze entities.
This grid doesn't know how to handle Knockout style "properties" that are functions.
Creating a POCO object using the Breeze entity's property values disconnects you from the change tracking goodness.
You could try creating your POCO object using Object.defineProperty, using the Knockout observable as the property's getter and setter functions. Here's a simple example:
Typescript + Knockout:
class PocoBreezeEntity {
constructor(private entity: breeze.Entity) {
entity.entityType.dataProperties.forEach(
dataProperty => {
Object.defineProperty(
this,
dataProperty.name,
{
get: entity[dataProperty.name],
set: entity[dataProperty.name],
enumerable: true,
configurable: true
});
});
}
}
Typescript + Angular:
class PocoBreezeEntity {
constructor(private entity: breeze.Entity) {
entity.entityType.dataProperties.forEach(
dataProperty => {
Object.defineProperty(
this,
dataProperty.name,
{
get: function() { return entity[dataProperty.name]; },
set: function(newValue) { entity[dataProperty.name] = newValue; },
enumerable: true,
configurable: true
});
});
}
}
With this kind of approach you have the benefits of POCO entities without losing the Breeze change tracking.

Related

BreezeJS - Using expand

I am querying the server to get an entity with expand
function _loadIncidents() {
var deffered = Q.defer(),
queryObj = new breeze.EntityQuery().from('Incidents').expand(['Deployments', 'IncidentComments', 'DTasks', 'ExtendedProperties', 'IncidentEvents']);
dataRepository.fetchEntitiesByQuery(queryObj, true).then(function (incidents) {
var query = breeze.EntityQuery.from("DTasks"),
incidentIds = dataRepository.getEntitiesByQuerySync(query);
deffered.resolve();
}, function(err) {
deffered.reject(err);
});
return deffered.promise;
};
I am getting the results and all is fine, how ever when I query breeze cache to get the entities - I am getting empty collection. So when using expand does the expanded entities are added to the cache?
Yes the related entities identified in the expand should be in cache ... if the query is "correct" and the server interpreted your request as you intended.
Look at the payload of the response from the first request. Are the related entities present? If not, perhaps the query was not well received on the server. As a general rule, you want to make sure the data are coming over the wire before wondering whether Breeze is doing the right thing with those data.
I do find myself wondering about the spelling of the items in your expand list. They are all in PascalCase. Are they these the names of navigation properties of the Incident type? Or are they the names of the related EntityTypes? They need to be former (nav property names), not the latter.
I Had problem with the navigation property - as I am not using OData webapi not using EF , there is problem with the navigation properties so for the current time i just wrote
Object.defineProperty(this, 'Deployments', {
get: function () {
return (this.entityAspect && this.entityAspect.entityManager) ?
this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery("Deployments").
where('IncidentID', 'eq', this.IncidentID)) :
[];
},
set: function (value) { //used only when loading incidents from the server
if (!value.results) {
return;
}
var i = 0,
dataRepository = require('sharedServices/dataRepository');
for (i; i < value.results.length; i++) {
dataRepository.addUnchangedEntity('Deployment', value.results[i]);
}
},
enumerable: true
});

use specific field as a backbone's model data

I'm retrieving a json and want to use a field(don't know the right term) in the json to initialize backbone model after fetch.
It seems I have to implement parse function, but how?
1) You need to define defaults object on your model setting up properties you want to populate from a request; 2) then you need to filter (those) necessary fields and construct new object in the model's parse method:
var model = Backbone.Model.extend({
defaults: {
swordType: null
},
parse: function(data) {
// filter what you need
var swordTypeValue = null;
if(data.hasOwnProperty('swordType')) {
swordTypeValue = data.swordType;
}
// construct attributes from it
return { swordType: swordTypeValue };
}
});

How do I set a property on a breeze entity client-side?

I've tried drilling down into the object and looking at the docs but haven't found anything. I've created an entity and I need to assign some properties manually. I see _backingStore and entityAspect on the object... and I know the property names but don't know how to set them via the breeze entity.
In case it matters, I'm creating a new object and then copying properties over from another object to facilitate cloning.
function createDocument() {
var manager = datacontext.manager;
var ds = datacontext.serviceName;
if (!manager.metadataStore.hasMetadataFor(ds)) {
manager.fetchMetadata(ds).then(function () {
return manager.createEntity("Document");
})
}
else {
return manager.createEntity("Document");
}
}
function cloneDocument(doc) {
var clonedDocument = createDocument();
// Copy Properties Here - how?
saveChanges()
.fail(cloneFailed)
.fin(cloneSucceeded);
}
Not knowing what your properties might be, here are two scenarios -
function cloneDocument(doc) {
var clonedDocument = createDocument();
clonedDocument.docId(doc.docId());
clonedDocument.description(doc.description());
saveChanges()
.fail(cloneFailed)
.fin(cloneSucceeded);
}
There are a few things to note here - I am assuming you are using Knockout and needing to set the properties. If you are not using Knockout then you can remove the parans and use equals -
clonedDocument.docId = doc.docId;
I believe this is true for if you are not using Knockout (vanilla js) and if you are using Angular, but I have not used Breeze with Angular yet so bear with me.
And here's another way that works regardless of model library (Angular or KO)
function cloneDocument(doc) {
var manager = doc.entityAspect.entityManager; // get it from the source
// Check this out! I'm using an object initializer!
var clonedDocument = manager.createEntity("Document", {
description: doc.description,
foo: doc.foo,
bar: doc.bar,
baz: doc.baz
});
return clonedDocument;
}
But beware of this:
clonedDocument.docId = doc.docId; // Probably won't work!
Two entities of the same type in the same manager cannot have the same key.
Extra credit: write a utility that copies the properties of one entity to another without copying entityAspect or the key (the id) and optionally clones the entities of a dependent navigation (e.g., the order line items of an order).

breeze.js navigation properties not available to custom initializer when importing

When retrieving entities and metadata from the server my custom initializer functions are able to access thier navigation properties as expected. When exporting metadata and entities to localstorage and reimporting them the navigation properties are not available. I have ensured that the ctors and initializers are registered to the metadatastore before import as the methods are called. I took a look at the breeze source and it appears that the initialize functions are called prior to the item being fully populated and attached.
targetEntity = entityType._createEntityCore();
updateTargetFromRaw(targetEntity, rawEntity, dataProps, true);
if (newTempKeyValue !== undefined) {
// fixup pk
targetEntity.setProperty(entityType.keyProperties[0].name, newTempKeyValue);
// fixup foreign keys
if (newAspect.tempNavPropNames) {
newAspect.tempNavPropNames.forEach(function (npName) {
var np = entityType.getNavigationProperty(npName);
var fkPropName = np.relatedDataProperties[0].name;
var oldFkValue = targetEntity.getProperty(fkPropName);
var fk = new EntityKey(np.entityType, [oldFkValue]);
var newFkValue = tempKeyMap[fk.toString()];
targetEntity.setProperty(fkPropName, newFkValue);
});
}
}
*targetEntity.entityAspect._postInitialize();*
targetEntity = entityGroup.attachEntity(targetEntity, entityState);
if (entityChanged) {
entityChanged.publish({ entityAction: EntityAction.AttachOnImport, entity: targetEntity });
if (!entityState.isUnchanged()) {
entityGroup.entityManager._notifyStateChange(targetEntity, true);
}
}
}
if (targetEntity) {
targetEntity.entityAspect.entityState = entityState;
if (entityState.isModified()) {
targetEntity.entityAspect.originalValuesMap = newAspect.originalValues;
}
entityGroup.entityManager._linkRelatedEntities( targetEntity);
}
If I move the post initialize call below the ._linkRelatedEntities call it appears to work exactly as the query materialization
if (targetEntity) {
targetEntity.entityAspect.entityState = entityState;
if (entityState.isModified()) {
targetEntity.entityAspect.originalValuesMap = newAspect.originalValues;
}
entityGroup.entityManager._linkRelatedEntities( targetEntity);
*targetEntity.entityAspect._postInitialize();*
}
Am I missing something here? Is this the designed functionality? I have found a work around by passing in the EntityManager, setting the entityAspect manager and calling loadNavigationProperties however this seems redundant. Any insight fromt he Breeze folks would be greatly appreciated.
Thanks,
Brad
Not sure but this issue might be related to a bug that we just fixed in Breeze 1.4.1 available now.

Breeze querying local cache with EF and Web API

Problem
I have a view with 6 drop downs. Each of which is being populated by a Web API call. I want
to use breeze to run the query locally once it has populated from the remote server
The code runs fine when the data call is against the server. The issue is when trying to query the local cache. I never get any results returned. Is my approach flawed or am I doing something wrong ?
SERVER SIDE
View model
class genericDropDown()
{
public int value{get;set;}
public string option{get;set;}
}
The WebAPI [A single sample method]
[HttpGet]
// GET api/<controller>
public object GetSomeVals()
{
return _context.getClinician();
}
The Repository [A single sample method]
public IEnumerable<genericDropDown> getDropDownVal()
{
return context.somemodel(a=>new{a.id,a.firstname,a.lastname}).ToList().
Select(x => new GenericDropDown
{ value = x.id, option = x.firstname+ " " + x.lastname});}
}
CLIENT SIDE
Datacontext.js
var _manager = new breeze.EntityManager("EndPoint");
//Being called from my view model
var getDropDownBindings = function(KO1, KO2) {
//First add the entity to the local metadatastore then populate the entity
$.when(
addDD('clinicianDropDown', webAPIMethod),
getData(KO1, webAPIMethod, null, 'clinicianDropDown'),
addDD('docTypeDropDown', webAPIMethod);
getData(KO2, webAPIMethod, null, 'docTypeDropDown'),
).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
logger.log('Got drop down vals', "", 'dataContext', true);
}
};
//Add the entity to local store. First param is typename and second is
resource name (Web API method)
var addDD = function(shortName,resName) {
_manager.metadataStore.addEntityType({
shortName: shortName,
namespace: "Namespace",
autoGeneratedKeyType: breeze.AutoGeneratedKeyType.Identity,
defaultResourceName:resName,
dataProperties: {
value: { dataType: DataType.Int32,
isNullable: false, isPartOfKey: true },
option: { dataType: DataType.String, isNullable: false }
}
});
return _manager.metadataStore.registerEntityTypeCtor(shortName, null, null);
};
//Get the data
var getData = function(observableArray, dataEndPoint, parameters, mapto) {
if (observableArray != null)
observableArray([]);
//TO DO: Incorporate logic for server or local call depending on
// whether this method is accessed for the first time
var query = breeze.EntityQuery.from(dataEndPoint);
if (mapto != null && mapto != "")
query = query.toType(mapto);
if (parameters != null)
query = query.withParameters(parameters);
//This approach doesnt work on local querying as Jquery complains
//there is no 'then' method. Not sure how to implement promises
//when querying locally
/* return _manager.executeQuery(query).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
if (observableArray != null)
observableArray(data.results);
}
*/
//The array length from this query is always 0
var data = _manager.executeQueryLocally(query);
observableArray(data.results);
return;
};
//Generic error handler
function queryFailed(error) {
logger.log(error.message, null, 'dataContext', true);
}
viewmodel.js
//In Durandal's activate method populate the observable arrays
dataContext.getDropDownBindings (KO1,KO2);
Viewmodel.html
<select class="dropdown" data-bind="options: KO1, optionsText: 'option', value: 'value', optionsCaption: 'Clinicians'"></select>
<select class="dropdown" data-bind="options: KO2 optionsText: 'option', value: 'value', optionsCaption: 'Document Types'"></select>
You can only execute local queries against types that are described by metadata.
Without more information I can't be sure, but my guess is that your GetSomeVals method is not returning 'entities' but just loose data. In other words, the types of objects returned from the GetSomeVals method must be entities (or contain entities within a projection) in order for breeze to be able to perform a local query. This is because Breeze knows how to cache and query entities but has no ideas how to cache 'arbitrary' query results.
Note that you can return an anonymous type containing entities of different types from the server, (in order to populate mostly static small datasets), but the individual items must be 'entities'. In this case, Breeze will take apart the anon result and pick out any entities to include in the EntityManager cache.
Per you question of how to perform an local query with promises, use the FetchStrategy.FromLocalCache with the using method.
i.e. this query
var results = em.executeQueryLocally(query)
can also be expressed as:
query = query.using(FetchStrategy.FromLocalCache);
return em.executeQuery(query).then(data) {
var results = data.results;
}
The local query is still executed synchonously but is made to look async.

Resources