I'm running the following using Breeze.js with Asp.Net WebAPI. I can query my service using OData protocol and I can see JSON data in the response, but the .then() and .fail() callbacks aren't firing so the view model never gets the data. Is that because I'm missing the metadata?
/// <reference path="..\breeze.debug.js" />
(function (root) {
var dataService = new breeze.DataService({
serviceName: 'api/breezesample',
hasServerMetadata: false
});
var altMs = new breeze.MetadataStore({
namingConvention: breeze.NamingConvention.camelCase
});
var manager = new breeze.EntityManager({
dataService: dataService,
metadataStore: altMs
});
// define the viewmodel
var vm = {
todos: ko.observableArray(),
includeDone: ko.observable(false),
show: ko.observable(false)
};
// start fetching Todos
getTodos();
// re-query when "includeDone" checkbox changes
//vm.includeDone.subscribe(getTodos);
// bind view to the viewmodel
ko.applyBindings(vm);
/* Private functions */
// get Todos asynchronously
// returning a promise to wait for
function getTodos() {
var query = breeze.entityModel.EntityQuery.from("todos");
if (!vm.includeDone()) {
query = query.where("IsDone", "==", false);
}
return manager
.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
};
function querySucceeded(data) {
vm.todos.removeAll();
var todos = data.results;
todos.forEach(function (todo) {
vm.todos.push(todo);
});
vm.show(true); // show the view
}
function queryFailed(error) {
alert("Query failed: " + error.message);
}
}(window));
Try this code. You will also need to add your metadata to the 'altMs' via addEntityType method calls. Make sure you get the latest version of breeze as well, v.0.78.x. The 'breeze.entityModel' in your code is no longer needed.
var DataService = breeze.DataService;
var MetadataStore = breeze.MetadataStore;
var EntityManager = breeze.EntityManager;
var dataService = new DataService({
serviceName: altServiceName,
hasServerMetadata: false
});
var altMs = new MetadataStore({
namingConvention: NamingConvention.camelCase
});
return new EntityManager({
dataService: dataService,
metadataStore: altMs
});
Related
I need to set the property to DataSet during onInit, to change the visiblity of some controls in my View. I could solve this problem with setting the visibility dynamically in controller. But I want to use the expression binding and the visible property in the View to set visibilites.
I'm getting an error in the function OnStartSetVisibilites. self.getView().getBindingContext() returns UNDEFINED. Without the sPath, I can't use setProperty. And without setProperty, my View-Controls don't know the visible-value.
How to fix this?
View:
<uxap:ObjectPageSubSection visible="{= ${Responsible} === '1'}" id="leader">
</uxap:ObjectPageSubSection>
OnInit in View-Controller:
onInit: function () {
var startupParameters = this.getOwnerComponent().getComponentData().startupParameters;
var sWorkitem = startupParameters.TASK[0];
this.setModel(this.getOwnerComponent().getModel());
this.getModel().metadataLoaded().then(function () {
var sObjectPath = this.getModel().createKey("DataSet", {
Workitem: sWorkitem
});
this.getView().bindElement({
path: "/" + sObjectPath
});
}.bind(this));
var self = this;
var model = this.getOwnerComponent().getModel();
this.getModel().read("/CharSet", {
success: function (response) {
$.sap.Chars = response.results;
self.onStartSetVisibilities(model, self);
}
});
// self.getView().attachAfterRendering(function () {
// self.onStartSetVisibilities(model, self);
// });
}
OnStartSetVisibilities:
onStartSetVisibilities: function (model, self) {
var char = model.getProperty(ā€˛GeneralData/Char");
if (char !== "" || char !== null) {
model.setProperty(self.getView().getBindingContext().sPath + "/Responsible",
this.getResponsibleForChar(char));
}
}
I put together some code which might solve your issue (it's untested so it may contain syntax errors!).
I introduced the concept of Promises which simplifies the asynchronous behavior of JS. I also replaced some of the inner functions with Arrow functions so you don't have to deal with that or self. Arrow functions basically use the this of the scope they are defined within.
Your app should now have a proper flow.
First you bind the view.
After the view is bound you read the CharSet.
After the data is read you set the visibility stuff
onInit: function () {
const startupParameters = this.getOwnerComponent().getComponentData().startupParameters;
const sWorkitem = startupParameters.TASK[0];
this._bindView(sWorkitem)
.then(() => this._readCharSet())
.then(() => this._setVisibilities())
},
_bindView: function (sWorkitem) {
return new Promise((resolve) => {
const oModel = this.getOwnerComponent().getModel();
oModel.metadataLoaded().then(() => {
const sPath = "/" + oModel.createKey("DataSet", {
Workitem: sWorkitem
});
this.getView().bindElement({
path: sPath,
events: {
change: resolve,
dataReceived: resolve
}
});
});
});
},
_readCharSet: function () {
return new Promise((resolve) => {
const oModel = this.getOwnerComponent().getModel();
oModel.read("/CharSet", {
success: resolve
});
});
},
_setVisibilities: function () {
const oModel = this.getOwnerComponent().getModel();
const sChar = oModel.getProperty("GeneralData/Char");
if (sChar) {
// ...
}
}
I am using SocketIO and redis based on the following code,
var sub = redis.createClient();
var pub = redis.createClient();
sub.subscribe('chat');
io.use(socketHandshake({store: sessionStore, key:'jsessionid', secret:'secret', parser:cookieParser()}));
io.on('connection', function (socket) {
socket.on('chat', function (message) {
// io.emit('chat', "hello world");
pub.publish('chat', "hello world");
});
sub.on('message', function (channel, message) {
io.emit(channel, message);
});
});
This is the base code. I have modified the code so that if any user goes offline, in server side I am storing the messages in RSMQ(Redis Simple Message Queue) and when user comes online, the message is fetched from queue and emits to the user.I have used the following code to achieve this.I have stored the user status in an array.
var fs = require('fs')
, http = require('http')
, socketio = require('socket.io');
var redis = require('redis');
var store = redis.createClient();
var pub = redis.createClient();
var sub = redis.createClient();
RedisSMQ = require("rsmq");
rsmq = new RedisSMQ( {host: "127.0.0.1", port: 6379, ns: "rsmq"} );
var active_users=[];
var inactive_users=[];
var user_status=[];
var channel_users=[];
var users_queue=[];
var socket_ids=[];
var cname,qn;
var clients=[];
var server = http.createServer(function(req, res) {
res.writeHead(200, { 'Content-type': 'text/html'});
res.end(fs.readFileSync(__dirname + '/index.html'));
}).listen(9000, function() {
console.log('Listening at: http://localhost:9000');
});
socketio.listen(server).on('connection', function (socket) {
socket.on('login', function(data){
console.log('a user ' + data.userId + ' connected'+socket.id);
//saving userId to array with socket ID
active_users[socket.id] = data.userId;
socket_ids[data.userId]=socket.id;
clients[socket.id] = socket;
user_status[data.userId]="online";
});
socket.on('message', function (msg) {
console.log('Message Received: ', msg);
socket.broadcast.emit('message', msg);
});
socket.on('json', function (msg) {
if(msg.channel_name=='UserState'){
rsmq.listQueues(function(err,resp){
//console.log("QUEUES LIST"+resp);
});
if(msg.user_state=='active'){
store.hmset("active_users."+msg.sender_id,{"user":"online"});
user_status[msg.sender_id]="online";console.log(user_status);
if(users_queue[msg.sender_id]!=undefined && users_queue[msg.sender_id].length>0){
console.log("USERS QUEUE:"+users_queue[msg.sender_id]['0']);
for(var i=0;i<users_queue[msg.sender_id].length;i++){
cname=users_queue[msg.sender_id][i].split('_')[0];//get channel name from queue name
qn=users_queue[msg.sender_id][i];
rsmq.getQueueAttributes({qname:users_queue[msg.sender_id][i]},function(err,resp){
console.log("RESP:"+resp.msgs);
if(resp.msgs>0){ //if there are messages in queue......
for(var j=0;j<resp.msgs;j++){
rsmq.popMessage({qname:qn},function(err,resp){
console.log(resp);
var sid=socket_ids[msg.sender_id]; console.log("SOCKETID:"+sid); //get socket.id for the user
pub.publish(cname,resp.message);
});
}
}
});
}
}
}
else{
store.hmset("active_users."+msg.sender_id,{"status":"offline"});
user_status[msg.sender_id]="offline";
}
}
if(msg.channel_name=='ShareConversation'){
var channel=msg.conversations_data.conversation_id;//have to change to conversation_id or whatever channel.....
sub.subscribe(channel);
channel_users[channel]=[];
var m=msg.conversations_data.users.split(',');
for(var i=0;i<m.length;i++){
channel_users[channel].push(m[i]);
}
for(var i=0;i<channel_users[channel].length;i++){
var q=channel_users[channel][i].split('#')[0].replace(/(^\s+|\s+$)/g, '');
var queue_name=channel+"_"+q;console.log(queue_name);
var uname=channel_users[channel][i].replace(/(^\s+|\s+$)/g, '');
users_queue[uname]=[];
users_queue[uname].push(queue_name);
rsmq.createQueue({qname:queue_name}, function (err, resp) {
console.log(err);
console.log(queue_name);
if (resp===1) {
console.log("queue created");
}
});
}
}
socket.broadcast.emit('json', msg);
});
sub.on('message', function (channel, message) {
console.log("Message: " + message);
for(var i=0;i<channel_users[channel].length;i++){
var c=channel_users[channel][i].replace(/(^\s+|\s+$)/g, '');console.log("channel_users:"+channel_users[channel][i]);console.log("USER STATE :"+ user_status[c]);
if(user_status[c]=='offline'){
//send notification.........
//put messages in queue.......
var q=channel_users[channel][i].split('#')[0].replace(/(^\s+|\s+$)/g, '');
var queue_name=channel+"_"+q;console.log(queue_name);
rsmq.sendMessage({qname:queue_name, message:message}, function (err, resp) {
console.log(err);
if (resp) {
console.log("Message sent. ID:", resp);
}
});
}
}
socket.emit(channel, message);
});
});
This is my entire code. Here the issue is when the user goes offline, the message gets saved in queue multiple times and when the user comes online, the messages are received multiple times as there are duplicate messages saved in queue. How to overcome this.Please help....
I'm testing sample CRUD using Angular and Breeze following sample ToDo project.
But for some reason I get error Cannot call method 'getAll' of undefined.
(my odata is hosted on another localhost server, and CORS is enabled, I've tested it)
Here is my code:
Main.js:
var app = {};
app.adminMuscleGroup = angular.module('WebApp', []);
DataService:
app.adminMuscleGroup.dataService = (function (breeze, logger) {
breeze.config.initializeAdapterInstances({ dataService: "OData" });
var servicename = 'http://localhost:23758/odata/';
var manager = new breeze.EntityManager(servicename);
manager.enableSaveQueuing(true);
var dataService = {
getAll: getAll,
};
return dataService;
function getAll() {
var query = breeze.EntityQuery.from("MuscleGroup").orderBy("Name");
return manager.executeQuery(query);
}
})(breeze, app.logger);
Controller:
app.adminMuscleGroup.controller('AdminMuscleGroupCtrl', function($scope) {
var dataService = window.app.dataService;
var logger = window.app.logger;
$scope.items = [];
$scope.getAllMuscleGroups = function () {
dataService.getAll()
.then(querySucceeded)
.fail(queryFailed);
};
$scope.getAllMuscleGroups();
function querySucceeded(data) {
$scope.items = [];
data.results.forEach(function (item) {
$scope.items.push(item);
});
$scope.apply();
logger.info("Fetched all Muscle Groups");
}
function queryFailed(error) {
logger.error(error.message, "Query failed");
}
}
And here is whole error:
ypeError: Cannot call method 'getAll' of undefined
at Object.$scope.getAllMuscleGroups (http://localhost:7122/Scripts/app/AdminMuscleGroup/MuscleGroupController.js:10:21)
at new <anonymous> (http://localhost:7122/Scripts/app/AdminMuscleGroup/MuscleGroupController.js:15:12)
at invoke (http://localhost:7122/Scripts/angular/angular.js:2902:28)
at Object.instantiate (http://localhost:7122/Scripts/angular/angular.js:2914:23)
at http://localhost:7122/Scripts/angular/angular.js:4805:24
at http://localhost:7122/Scripts/angular/angular.js:4384:17
at forEach (http://localhost:7122/Scripts/angular/angular.js:137:20)
at nodeLinkFn (http://localhost:7122/Scripts/angular/angular.js:4369:11)
at compositeLinkFn (http://localhost:7122/Scripts/angular/angular.js:4015:15)
at publicLinkFn (http://localhost:7122/Scripts/angular/angular.js:3920:30) angular.js:5754
(anonymous function)
Try to inject the dataService directly to the controller
app.adminMuscleGroup.controller('AdminMuscleGroupCtrl', function($scope, dataService) {
var dataService = window.app.dataService; // -> delete this
It was stupid mistake, I've changed this line:
var dataService = window.app.dataService;
into this:
var dataService = window.app.adminMuscleGroup.dataService;
Now it works
SOLVED! It was a Knockout issue (wrong binding). But maybe someone likes to argue or comment about the code in general (dataservice, viewmodel, etc).
I tried to build a Breeze sample, where I get one database record (with fetchEntityByKey), display it for updating, then with a save button, write the changes back to the database. I could not figure out how to get it to work.
I was trying to have a dataservice ('class') and a viewmodel ('class'), binding the viewmodel with Knockout to the view.
I very much appreciated if someone could provide a sample or provide some hints.
Thankx, Harry
var dataservice = (function () {
var serviceName = "/api/amms/";
breeze.NamingConvention.camelCase.setAsDefault();
var entityManager = new breeze.EntityManager(serviceName);
var dataservice = {
serviceName: serviceName,
entityManager: entityManager,
init: init,
saveChanges: saveChanges,
getLocation: getLocation
};
return dataservice;
function init() {
return getMetadataStore();
}
function getMetadataStore() {
return entityManager.fetchMetadata()
.then(function (result) { return dataservice; })
.fail(function () { window.alert("fetchMetadata:fail"); })
.fin(function () { });
}
function saveChanges() {
return entityManager.saveChanges()
.then(function (result) { return result; })
.fail(function () { window.alert("fetchEntityByKey:fail"); })
.fin(function () { });
}
function getLocation() {
return entityManager.fetchEntityByKey("LgtLocation", 1001, false)
.then(function (result) { return result.entity; })
.fail(function () { window.alert("fetchEntityByKey:fail"); })
.fin(function () { });
}
})();
var viewmodel = (function () {
var viewmodel = {
location: null,
error: ko.observable(""),
init: init,
saveChanges: null
};
return viewmodel;
function init() {
return dataservice.init().then(function () {
viewmodel.saveChanges = dataservice.saveChanges;
return getLocation();
})
}
function getLocation() {
return dataservice.getLocation().then(function (result) {
return viewmodel.location = result;
})
}
})();
viewmodel.init().then(function () {
ko.applyBindings(viewmodel);
});
Glad you solved it. Can't help noticing that you added a great number of do-nothing callbacks. I can't think of a reason to do that. You also asked for metadata explicitly. But your call to fetchEntityByKey will do that implicitly for you because, as you called it, it will always go to the server.
Also, it is a good idea to re-throw the error in the fail callback within a dataservice so that a caller (e.g., the ViewModel) can add its own fail handler. Without re-throw, the caller's fail callback would not hear it (Q promise machinery acts as if the first fail handler "solved" the problem).
Therefore, your dataservice could be reduced to:
var dataservice = (function () {
breeze.NamingConvention.camelCase.setAsDefault();
var serviceName = "/api/amms/";
var entityManager = new breeze.EntityManager(serviceName);
var dataservice = {
serviceName: serviceName, // why are you exporting this?
entityManager: entityManager,
saveChanges: saveChanges,
getLocation: getLocation
};
return dataservice;
function saveChanges() {
return entityManager.saveChanges()
.fail(function () {
window.alert("saveChanges failed: " + error.message);
throw error; // re-throw so caller can hear it
})
}
function getLocation() {
return entityManager.fetchEntityByKey("LgtLocation", 1001, false)
.then(function (result) { return result.entity; })
.fail(function () {
window.alert("fetchEntityByKey failed: " + error.message);
throw error; // re-throw so caller can hear it
})
}
})();
I don't want to make too much of this. Maybe you're giving us the stripped down version of something more substantial. But, in case you (or a reader) think those methods are always necessary, I wanted to make clear that they are not.
When using Meteor, with the new authentication system, how can I get facebook access token, when logged with accounts-facebook package?
get it either from a Meteor.method:
// server
Meteor.methods({
getAccessToken : function() {
try {
return Meteor.user().services.facebook.accessToken;
} catch(e) {
return null;
}
}
});
// client
Meteor.call("getAccessToken", function(error, accessToken){
console.log(accessToken);
})
or publish it:
//server
Meteor.publish("currentUserAccessToken", function(){
var self = this;
if (this.userId()){
handle = Meteor.users.find(this.userId()).observe({
added: function(user){
self.set("currentUserAccessToken", user._id, {value: user.services.facebook.accessToken});
self.flush();
},
changed: function(user){
self.set("currentUserAccessToken", user._id, {value: user.services.facebook.accessToken});
self.flush();
}
});
this.onStop(function() {
handle.stop();
});
}
});
//client
var AccessToken = new Meteor.Collection("currentUserAccessToken");
Meteor.subscribe("currentUserAccessToken");
//access the value
var accessToken = AccessToken.findOne().value;
Updating and simplifying Lloyd's answer, we get this:
server
Meteor.publish("currentAccessToken", function(){
return Meteor.users.find(this.userId, {fields: {'services.facebook.accessToken': 1}});
});
client
Meteor.autosubscribe(function(){
var newUser = Meteor.user();
Meteor.subscribe('currentAccessToken');
});
It updates every time your user changes status, and access token can be accessed (when exists) by Meteor.user().services.facebook.accessToken