fetchEntityByKey not making any requests to the server - breeze

I'm trying to create a filemanager using Angularjs and I recently discovered Breezejs and was interested in trying to use it to communicate with my backend and manage my model relations. The backend is a rest api over which I have full control.
I am however running into a problem. I know the id of the file, so I would like to make a request to url of the form backend_url/files/:fileId where :fileId is the url of the file base64 encoded. According to the documentation I should use EntityManager.fetchEntityByKey() for this purpose. This is the function that i use to create the Angularjs service:
var FilestoreService = function () {
var dataService, manager;
dataService = new breeze.DataService({
serviceName: "../VFS-Symfony-Backend/web/app_dev.php/filesystems/local/",
hasServerMetadata: false
});
manager = new breeze.EntityManager({
dataService: dataService
});
manager.metadataStore.addEntityType(fileEntityType);
return {
findOneById: function (id) {
/* I have tried to leave the 3th argument empty or changing it to false with the same results */
return manager.fetchEntityByKey("File", id, true).then(function(){console.log("success");}).fail(function(){console.log("failure");});
/* I have also tried the following approach with the same result:
var type, key, query;
type = manager.metadataStore.getEntityType("File");
key = new breeze.EntityKey(type, id);
query = breeze.EntityQuery.fromEntityKey(key);
return manager.executeQuery(query);
*/
}
};
};
where fileEntityType is defined as:
var FileEntityType = new breeze.EntityType({
shortName: "File"
});
FileEntityType.addProperty(new breeze.DataProperty({
name: "uri",
dataType: breeze.DataType.String,
isNullable: false
}));
FileEntityType.addProperty(new breeze.DataProperty({
name: "mTime",
dataType: breeze.DataType.Int16,
isNullable: false
}));
FileEntityType.addProperty(new breeze.DataProperty({
name: "type",
dataType: breeze.DataType.String,
isNullable: false
}));
FileEntityType.addProperty(new breeze.DataProperty({
name: "size",
dataType: breeze.DataType.int16,
isNullable: true
}));
However, when I call findOneById no request is made to the server and 2 lines are logged to the console:
Should be empty:[] by q.js
failure (as per the fail() callback function)
A 'normal' query (manager.executeQuery(new breeze.EntityQuery().from("Files"));) does result in a request to the server as expected.
I am really quite lost here. I have searched for a solution all weekend and finaly decided to post on SO hoping someone might be able to help me.
Thanks very much for reading.

At least one problem is that you haven't identified the key ('id'?) in your metadata description of the "File" entity type. You have defined every other property except the 'id' property.
You know this but let me tell other readers of this question who may not understand that you are defining the metadata on the JS client rather than retrieving it from the server.
I'm also curious why you are using int16? Is that really the right type? Do you really need to shave off the two bytes?
Finally, Breeze's fetchEntityByKey won't make a request URL in the form "backend_url/files /:fileId". Instead, the request URL will be "backend_url/files /?id=:fileId" as befits an OData query. See the network traffic for Id queries in the queryTests.cs of the DocCode sample.
You can still get that file if the server expects the URL you specified. You won't be using the Breeze query syntax. You'll just hit the service with the URL it expects, e.g.,
function findOne(id) {
// check cache first
var file = manager.getEntityByKey('File', id);
if (file) {
console.log("got it from cache");
return Q(file); // got it; return in resolved promise
}
// not in cache; get from server
return breeze.EntityQuery
.from('Files/'+id) // constructs the URL you want
.using(manager).execute()
.then(function(data){
console.log("success");}
// return the file in a resolved promise
return data.results[0] || null;
})
.fail(function(error){
console.log("failure: "+ error.message);
});
}
This is a variation on the getByIdCacheOrRemote method in queryTest.cs in the DocCode sample.
Yes, it's longer. You're reimplementing fetchEntityByKey so you can hit the remote service the way it expects.
The method begins by looking for the file in cache. The getEntityByKey is a synchronous search of the cache. The findOne method has async syntax ... returns a promise ... because it might have to go to the server. So if we find the file in cache (synchronously), we wrap the result in a resolved Q.js promise for the benefit of the caller which is expecting findOne to be asynchronous.
If the file is not found in cache, you go to the server with a "query" that is really just a request on the URL of your choice.
I haven't tried this a long time. I'm sure you'll tell me if it works ... or does not. If it doesn't, it should and we'll fix it so it does.

As Ward suggested the EnityType I created did need one of its field to be marked as the key field. Besides that the EntityType also needs to have a defaultResourceName to be supplied to its constructor. I think this explains why the 'normal' query did work (the resourceName is supplied by from()) and why fetchEntityByKey did not work. The updated FileEntityType now looks like this:
var FileEntityType = new breeze.EntityType({
shortName: "File",
defaultResourceName: "files"
});
FileEntityType.addProperty(new breeze.DataProperty({
name: "id",
dataType: breeze.DataType.String,
isNullable: false,
isPartOfKey: true
}));
// These properties were not altered
FileEntityType.addProperty(new breeze.DataProperty({
name: "uri",
dataType: breeze.DataType.String,
isNullable: false
}));
FileEntityType.addProperty(new breeze.DataProperty({
name: "mTime",
dataType: breeze.DataType.Int16,
isNullable: false
}));
FileEntityType.addProperty(new breeze.DataProperty({
name: "type",
dataType: breeze.DataType.String,
isNullable: false
}));
FileEntityType.addProperty(new breeze.DataProperty({
name: "size",
dataType: breeze.DataType.int16,
isNullable: true
}));

Related

cannot get extjs grid to populate with proxy data call

I have gone from incorporating extjs in my original asp.net application which worked when hardcoding any data stores and binding them to the charts/grids. When I tried proxy url calls or even fetching the data from code behind and wrapping in json I still do not get the data into the grid. So I gave up and went with extjs and nodejs and still using mongodb; this worked perfectly but I still have to learn to create a better UI using express/jade etc which is a different project now. But then I came across using MVC with extjs and with a sample project tried the same thing (the sample had hardcoded data) and I cannot for the life of me get it to display the data.
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.util.*',
'Ext.state.*'
]);
Ext.onReady(function () {
Ext.QuickTips.init();
// setup the state provider, all state information will be saved to a cookie
Ext.state.Manager.setProvider(Ext.create('Ext.state.CookieProvider'));
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{ name: 'username', type: 'string' }
]
});
Ext.define('UserStore', {
extend: 'Ext.data.Store',
model: 'User',
autoload: true,
proxy: {
type: 'ajax',
url: '/dashboard.aspx/getDBData',
reader: {
type: 'json',
root: 'users'
},
listeners:
{
exception: function (proxy, response, operation) {
Ext.MessageBox.show(
{
title: 'REMOTE EXCEPTION',
msg: operation.getError(), icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK
});
}
}
}
});
var myStore = Ext.getStore('UserStore');
the url I am including here is the codebehind function that I initially tried which accesses the mongodb and returns json result. Not working.
Now from the extjs node.js application I have results coming into localhost:3000/userlist which returns a list from mongodb and displays it as follows:
extends layout
block content
h1.
User List
u1
each user, i in userlist
li
a(href="mailto:#{user.email}")= user.username
Now would it be possible to use the same server and call the base url and then change the route.js file to return the mongodb json result or call the mongodb localhost:27017 and get a result. Really confused here
exports.index = function(db) {
return function(req, res) {
var collection = db.get('usercollection');
collection.find({},{}, function(e,docs){
res.render('userlist', {
"userlist" : docs
});
});
};
};
EDIT:
First thing I realized from asp.net perspective was that I was not calling a webservice just a codebehind method. Any comments will still be appreciated.
EDIT 2:
{"connTime":null,"userName":"101591196589145","clientName":null,
"feedUrl":null,"dconnTime":null,"errMessage":null,"ip":null}
You have identified a root in your store as 'users'
reader: {
type: 'json',
root: 'users'
},
But there is no root in your returned json such as:
{"users":[{"connTime":null,"userName":"101591196589145","clientName":null,
"feedUrl":null,"dconnTime":null,"errMessage":null,"ip":null}]}

Custom resource method's URL not respected

I'm trying to make a list of persons. My HTTP server is able to give a JSON representation of a person on URLs like /person/[id].json, and a list of all persons at /person.infinity.json.
So I have the controller, where I have defined the Person resource:
var app = angular.module('myApp', ["ngResource"])
.controller('PersonController', function PersonController($scope, $http, $resource) {
var Person = $resource("/person/:personId.json",
{ personId: '#id' },
{ list: {method: "GET", url: "/person.infinity.json", isArray: true}}
);
$scope.persons = Person.list();
});
However, when Person.list() is called, my custom URL for the list method (/person.infinity.json) is not used. Chrome´s network inspector reveals that a request is fired to /person/.json, that is, the default resource URL with no parameter set.
Obviously, I would like Person.list() to result in a request to /person.infinity.json.
What am I doing wrong?
I was using angular-resource 1.0.7, in which URL override was not implemented. It was implemented in this pull request: https://github.com/angular/angular.js/pull/1890
Switching to version 1.1.5 solved the problem.

Select2 with createSearchChoice uses newly created choice for keyboard entry even given a match, bug or am I missing something?

I'm using Select2 (version 3.4.0) to populate a tag list. The tags are matched against existing ones via ajax call, and I'm using createSearchChoice to allow creating new tags. The code works so far, and looks something like this:
$(mytags).select2({
multiple: true,
placeholder: "Please enter tags",
tokenSeparators: [ "," ],
ajax: {
multiple: true,
url: myurl,
dataType: "json",
data: function(term, page) {
return {
q: term
};
},
results: function(data, page) {
return data;
}
},
createSearchChoice: function(term) {
return {
id: term,
text: term + ' (new)'
};
},
});
All pretty standard, except note the appended (new) in createSearchChoice. I need users to know that this is not a preexisting tag.
It works as expected: if I start typing "new-tag", I get "new-tag (new)" tag suggested at the top of the list, and if I pick it, the tag list contains "new-tag (new)", as expected. If the tag already exists, Select2 detects the match, and no "(new)" choice is created. Pressing return or clicking on the match works as expected.
The problem appears when I type a comma (my single tokenSeparators entry) while there is a match. Select2 closes that token, and adds the tag to the list, but with the "(new)" label appended, i.e. it uses the return value from createSeachChoice even if it does not have to.
Is this a bug in Select2, or am I using it wrong (and what should I do instead)?
I 'm not sure if this is a bug or not -- in any case, there is no open issue referring to this behavior at the GitHub issue tracker at this moment.
You can mostly fix the behavior yourself though. The idea is that the createSearchChoice callback must be able to tell if term refers to a search result or not. But createSearchChoice does not have direct access to the search results, so how can we enable that? Well, by saving the latest batch of search results from inside the results callback.
var lastResults = [];
$(...).select2({
ajax: {
multiple: true,
url: "/echo/json/",
dataType: "json",
type: "POST",
data: function (term, page) {
return {
json: JSON.stringify({results: [{id: "foo", text:"foo"},{id:"bar", text:"bar"}]}),
q: term
};
},
results: function (data, page) {
lastResults = data.results;
return data;
}
},
createSearchChoice: function (term) {
if(lastResults.some(function(r) { return r.text == term })) {
return { id: term, text: term };
}
else {
return { id: term, text: term + " (new)" };
}
}
});
This code uses Array.some so you need something better than IE8 (which is the select2 minimum requirement) to run it, but of course it is possible to emulate the behavior.
See it in action.
There is, however, a fly in the ointment: this code works correctly only if the search results corresponding to the current search term have been already received.
This should be obvious: if you type very fast and create a search term that corresponds to an existing tag but hit comma before the search results that include that tag have arrived, createSearchChoice will be testing for the tag's presence among the previously received search results. If those results do not include the tag, then the tag will be displayed as "new" even though it is not.
Unfortunately I don't believe there is anything you can do to prevent this from happening.
Instead of tweeking the result, I think it is better to work on the server side.
If the server doesn't find a tag make it return a json answer with the new tag
{"more":false,"results":[{"id":"whatever","text":"new-tag (new)"}]}
There is another parameter for the 'createSearchChoice' - 'page', it lists all the choices, you can easily find dupes with it.
createSearchChoice = function (term, page) {
if( page.some(function(item) {
return item.text.toLowerCase() === term.toLowerCase();
}) ){
return { val: term, name: term + '*' };
}
}

Sencha touch XML response to JSON

I have recently started a project in Sencha touch with existing Web-services.
Being very new to the technology, I am facing certain issues in accomplishing some functionality.
Problem
I have to call login service and the request goes like:
http://domain.sub.com/Service.asmx/LoginService?body={"Username":"maj#smaj.com","Password":"p12345","Token":122112321123212123,"Method":"Login","LabId":"(null)","Hash":"fr3f33f3334348u8yy8hfuhdu8bdy7y89u89x8998c89789c87d78r9","DeviceType":"iPhone Simulator","DeviceId":"91BF3299-A94C-5AD3-9C35-A5C9BBBB6AA8","ApplicationType":"iPhone","Id":"998390494"}
but the response is coming in XML format as:
RESPONSE:
<?xml version="1.0" encoding="utf-8"?>
<string xmlns="http://domain2.sub2.com/MobileWebService/">{"Id":"null","Message":"Logged In.","Status":true,"LoggedIn":true}</string>
I have to parse this xml to json to get : {"Id":"null","Message":"Logged In.","Status":true,"LoggedIn":true} out of the response.
then use the Status, LoggedIn and Id to verify the login.
My Idea
I am not sure whether its right, I am trying to create two stores, xmlStore and JsonStore.
??
How will I store the xml response inside a string.
How Will I pass this string to Json Store (at the place of url?)
I may sound very naive to this, but this is my problem ;)
Please guide.
EDIT:
I realized tha I am diving cross domain request.
is that what is causing problems or confusion. How to deal with it suppose I did not had cross domain requests?
If you are doing cross domain requests use scripttag in your proxy in place of ajax. Here is a json example
ex.
mApp.stores.onlineStore = new Ext.data.Store({
model: 'XPosts',
proxy: {
type: 'scripttag',
url : 'http://domain.com/data.json',
reader: new Ext.data.JsonReader({
root: 'pages'
}),
timeout: 3000,
listeners: {
exception:function () {
console.log("onlineStore: failed");
},
success: function(){
console.log("onlineStore: success");
}
}
},
autoLoad: true
});
Offline store:
mApp.stores.offlineStore = new Ext.data.Store({
model: 'XPosts',
proxy: {
type: 'localstorage',
id: 'appdata',
reader: new Ext.data.JsonReader({
root: 'pages'
})
},
autoLoad: true
});
Then, in your launch :
this.stores.onlineStore.addListener('load', function () {
console.log("onlineStore: online");
mApp.stores.offlineStore.proxy.clear();
console.log("offlineStore: cleared");
this.each(function (record) {
console.log("offlineStore: adding record");
mApp.stores.offlineStore.add(record.data)[0];
});
mApp.stores.offlineStore.sync();
console.log("offlineStore: synced");
mApp.stores.offlineStore.load();
});
this.stores.onlineStore.load();
May have some bugs so beforewarned!

$.ajax post to MVC controller returns Internal Server Error: Parameters dictionary contains null entry

The parameters dictionary contains a null entry for parameter 'appId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ContentResult CheckForInstaller(Int32)' in 'HLIT_TicketingMVC.Controllers.TicketController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
function SubmitAjax(url, message, successFunc, errorFunc) {
$.ajax({
type:'POST',
url:url,
data:message,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success:successFunc,
error:errorFunc
});
};
The data object is built as follows:
var message={"appId":application.val()};
I have also tried a jsonified string:
var message="{'appId':"+application.val()+"}";
and
var message="{'appId':'"+application.val()+"'}";
I validated that the message is coming through with a proper int value before it tries to post. The mouse over debugger most recently showed: {appId="6"}
The method signature on the controller is:
public ContentResult CheckForInstaller(int appId)
When I remove the parameter from the method signature, it does hit the breakpoint inside, so it's either the signature needing attributes of some kind, or the message isn't built properly I believe.
Remove this:
contentType: 'application/json; charset=utf-8',
MVC isn't going to parse the JSON into an int. You want the default value of application/x-www-form-urlencoded.
I think it maybe that you are sending json to the controller try this
function SubmitAjax(url, message, successFunc, errorFunc) {
$.ajax({
type:'POST',
url:url,
data:"appId=" + application.val(),//not sure where you get the value from in your current code
dataType: 'json',
success:successFunc,
error:errorFunc
});
};

Resources