Thursday, 22 March 2018

Breeze.js- Basics

Breeze.js is a client side JavaScript library that manages rich data. It is used for data management on client side using javascript.
If you store data in a database, query and save those data as complex object graphs, and share these graphs across multiple screens of your JavaScript client, Breeze is for you.
A Breeze client can communicate to almost any server technology including Node.js running Express with a MongoDB database. See Breeze-MongoDB integration in action
Client side Requirements:

Standard Breeze requirements:
  1. Breeze.debug.js or Breeze.min.js
  2. Q.js
  3. Ajax library - usually jQuery
Additional MongoDB requirement:
  1. Breeze.dataservice.mongo.js - a Breeze dataservice adapter that handles all of the MongoDB -specific client-side work involved in communicating with the MongoDB–backed service. 
Server side requirements

1- breeze-mongodb from npm - a node package that handles all of the server-side details of communicating between a Breeze client and a MongoDB-backed service.

npm install breeze-mongodb

Basics
You will write Breeze specific code in your Node server application to handle the following types of operations.
  • Querying
  • Saving
Breeze/MongoDB - Server side processing
The examples assume you’ll launch in node a JavaScript file containing standard node/express boilerplate that looks something like this:

server.js
var express = require('express');
var app = express();
var routes = require('./routes');  //  refs a routes.js file where most of our code will be written.
app.use(express.bodyParser());
app.use(app.router);
app.use(errorHandler);
Almost all of the Breeze/MongoDB code shown in these examples is assumed to be part of a “routes.js” file. Below is the beginning of such a file that opens a MongoDB database called “MyNorthwindDatabase”.

Assume a MongoDB server is running with access to this database.

routes.js
var mongodb = require('mongodb');             // MongoDB support package   
var breezeMongo = require('breeze-mongodb');  // Breeze MongoDB support package
var fs = require('fs');                       // Access to the local file system.

// Connect to a database.
var host = 'localhost';
var port = 27017;
var dbName = 'MyNorthwindDatabase';
var dbServer = new mongodb.Server(host, port, { auto_reconnect: true});
var db = new mongodb.Db(dbName, dbServer, { strict:true, w: 1});
db.open(function () { });

// route definitions begin here …

Querying with MongoDB
Let’s start on the breeze client which makes a query request to the server. Then we’ll see how routes.js redirects that request to the proper method for query processing.

Client side
Tell Breeze that you’re using MongoDB and everything else is standard Breeze. Put the following line somewhere in your application bootstrapping logic:

breeze.config.initializeAdapterInstance("dataService", "mongo", true);
Querying a MongoDB database from a Breeze client involves nothing more than a standard Breeze EntityQuery such as this one:

var query = EntityQuery.from("Products").where("ProductName", "startsWith", "C"); 
The real point here is that, in general, you cannot tell by looking at the client side code what backend datastore is behind any Breeze query.

Server side
In order to provide the most basic support for Breeze the minimum necessary requirement is simply that you give Breeze an endpoint and then route Breeze to this endpoint.

The routing could look something like this:
app.get('/breeze/Northwind/Products', routes.getProducts);

Thus a Breeze EntityQuery with “Products” in the EntityQuery.from clause is directed to the getProducts method in the routes.js file.

getProducts illustrates the typical implementation of a query processing method:
exports.getProducts = function(req, res, next) {
    // convert a client OData-style query string in the request to a MongoDB query
    var query = new breezeMongo.MongoQuery(req.query);

    // add custom server-side filtering to the query object here...
       query.filter["isDiscontinued"] = false;
     

    // execute the MongoDB query with a callback
    query.execute(db, "Products", processResults(res, next));
}  

// Return the query callback function
// res is the HTTP response object
// next is the Express HTTP pipeline callback
function processResults(res, next) {
    // return a function to process the results of the query
    // here we simply compose a response with the query results as content
    return function(err, results) {
        if (err) {
            next(err);
        } else {
            res.setHeader("Content-Type:", "application/json");
            res.send(results);
        }
    }
}
This is the standard template for most queries. The processResults method can be reused by all of the query methods discussed in this document.

Inside the query method
getProducts composes a query object by parsing the OData-style parameters that the Breeze client has passed in the URL query string and turning them into an equivalent MongoDB query expression. These implementation details are handled automatically by the Breeze MongoQuery class that you imported when you called “require(‘breeze-mongodb’)”.

The Breeze MongoQuery.execute receives three parameters: the database object (db), the name of a MongoDB collection in “MyNorthwindDatabase”, and a callback to process the results returned by MongoDB.

You can further constrain or augment the client query by modifying the Breeze query object before executing it. Continuing with our Products query, we may wish to ensure that no ‘discontinued’ products are ever returned. We’d specify that constraint with the query.filter property.

Saving with MongoDB
We’ll start on the client and return quickly to the server.

Client side
Saving to a MongoDB database from a Breeze client involves nothing more than a standard Breeze EntityManager.saveChanges call such as this one:

return myEntityManager.saveChanges().then(...);
Again, as with queries, in general, you cannot tell by looking at the client side code what backend datastore is behind any Breeze saveChanges call.

Server side
As with queries, in order to support Breeze’s client side EntityManager.saveChanges call, you will need to provide an endpoint and a route to this endpoint. Something like this:

app.post('/breeze/Northwind/SaveChanges', routes.saveChanges);
Here is a simple implementation for routes.saveChanges.

exports.saveChanges = function(req, res, next) {
    var saveHandler = new breezeMongo.MongoSaveHandler(db, req.body, processResults(res, next));
    saveHandler.save();
};

Validation through save interception
You authorize and validate client save-data with save “interceptors”. You can even modify the save data with interceptors.

Breeze offers two interceptor methods: MongoSaveHandler.beforeSaveEntity and MongoSaveHandler.beforeSaveEntities.

These are methods that you write and breeze calls just before saving the data to the MongoDB database.

exports.saveChanges = function(req, res, next) {
    var saveHandler = new breezeMongo.MongoSaveHandler(db, req.body, processResults(res, next));
    // write one or both of the following
    // saveHandler.beforeSaveEntity = myCustomBeforeSaveEntity;
    // saveHandler.beforeSaveEntities = myCustomBeforeSaveEntities;
    saveHandler.save();
};

You can define one or both of these methods. Breeze first calls beforeSaveEntity for every entity in the save-set and then calls beforeSaveEntities.

Each method has a distinct purpose:
  • beforeSaveEntity - review and possibly modify or reject each entity individually.
  • beforeSaveEntities - review and possibly modify the entire collection of entities to be saved (the “save-set”). You can modify or remove any of them. You can add more entities-to-save, potentially of types not included in the original save-set.
beforeSaveEntity
Breeze doesn’t define this method; you do. Breeze calls your custom implementation of the beforeSaveEntity interceptor once for each entity in the save-set.

Save Example #1:

Ensure that every new Product we add to the database has at least a $.50 surcharge.
function myCustomBeforeSaveEntity(entity) {
    var entityAspect = entity.entityAspect;
    if (entityAspect.entityTypeName === "Product" && entityAspect.entityState === "Added") {
        if (entity.surcharge < .5) entity.surcharge = .5;
    }
    return true;
}
Save Example #2:

Prevent new products from being added to “revoked suppliers” by removing such products from the save-set.
function myCustomBeforeSaveEntity(entity) {
    var entityAspect = entity.entityAspect;
    if (entityAspect.entityTypeName === "Product" && entityAspect.entityState === "Added") {
        if (revokedSupplierNames.indexOf(entity.supplierName) >= 0) return false;
    }
    return true;
}
If the method returns false, breeze will not save this entity. Breeze will continue to evaluate the remaining entities and may save them.

You can throw an exception if you want to terminate the save immediately.

beforeSaveEntities
Your beforeSaveEntities method is granted access to the entire save-set through several public properties on the MongoSaveHandler instance. The MongoSaveHandler instance is the this object within your beforeSaveEntities function.

Save Example #3:

Add 5% to the freight cost on every order saved.
function myCustomBeforeSaveEntities(callback) {
    var orderTypeName = this.qualifyTypeName("Order");
    var orders = this.saveMap[orderTypeName] || [];
    
    orders.forEach(function(order) {
       order.freightCost = order.freightCost * 1.05;    
    });
    callback();
}



1 comment:

  1. Ni Hau,


    Smokin hot stuff! You’ve trimmed my dim. I feel as bright and fresh as your prolific website and blogs!

    I am developing an angular app, I have a Stored Proc using in which i have a lot of data related to multiple pages. I want to call that SP in master page only and I will save data in multiple variables related to diff-diff pages.
    On master page i want to display some of the data and further u have some links in master page which will navigate to respective pages. On those respective pages i don't want to call separate SP, i want to utilize data which i already got on master page.

    Once again thanks for your tutorial.


    MuchasGracias,

    ReplyDelete

Topics

ADO .Net (2) Ajax (1) Angular Js (17) Angular2 (24) ASP .Net (14) Azure (1) Breeze.js (1) C# (49) CloudComputing (1) CMS (1) CSS (2) Design_Pattern (3) DI (3) Dotnet (21) Entity Framework (3) ExpressJS (4) Html (3) IIS (1) Javascript (6) Jquery (9) Lamda (3) Linq (11) Mongodb (1) MVC (48) NodeJS (7) RDLC (1) Report (1) Sql Server (29) SSIS (3) SSRS (2) UI (1) WCF (12) Web Api (10) Web Service (1) XMl (1)