How can I implement MVVM with offline storage and Knockout.js? - html

I can implement Mvvm with Knockout.js. But I want to use it with cross browser(FF and Chrome) supported Html 5 offline storage.
I want to bind html objects to offline storage.

I haven't tried it, but there is a knockout.localStorage project on GitHub, that seems to be what you are looking for.
With that plugin, you should be able to pass an object as a second argument, when you create your observable, which saves the observable into localStorage.
From the documentation:
var viewModel = {
name: ko.observable('James', {persist: 'name'})
}
ko.applyBindings(viewModel);

You can use a library such as amplify.js which can serialize objects to localStorage (cross browser). It falls back to older storage tools for older browsers too. First, unwrap the observables to a JSON object, then use amplify.store to serialize the object and store it. Then you can pull it back out and map it back to an observable object when you want to fetch it.
http://amplifyjs.com/api/store/

http://craigcav.wordpress.com/2012/05/16/simple-client-storage-for-view-models-with-amplifyjs-and-knockout/
His solution works!

I worked out a solution bases on the subscribe feature of KnockoutJS. It takes a model and persist all the observable properties.
ko.persistChanges = function (vm, prefix) {
if (prefix === undefined) {
prefix = '';
}
for (var n in vm) {
var observable = vm[n];
var key = prefix + n;
if (ko.isObservable(observable) && !ko.isComputed(observable)) {
//track change of observable
ko.trackChange(observable, key);
//force load
observable();
}
}
};
Check http://keestalkstech.com/2014/02/automatic-knockout-model-persistence-offline-with-amplify/ for code and JSFiddle example.

Related

How to pull down an schema-less JSON document from CosmosDB in Xamarin App

I am creating an app in Xamarin and am having issues querying a general JSON document from my CosmosDB. I am able to query my DB with a known structure (very similar to what we see in the Xamarin ToDo Example):
public async static Task<List<ToDoItem>> GetToDoItems()
{
var todos = new List<ToDoItem>();
if (!await Initialize())
return todos;
**var todoQuery = docClient.CreateDocumentQuery<ToDoItem>(
UriFactory.CreateDocumentCollectionUri(databaseName, collectionName),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(todo => todo.Completed == false)
.AsDocumentQuery();**
while (todoQuery.HasMoreResults)
{
var queryResults = await todoQuery.ExecuteNextAsync<ToDoItem>();
todos.AddRange(queryResults);
}
return todos;
}
The problem I see with this "code fixed scheme" approach is that if the scheme of your JSON file changes throughout development, older versions of code will overwrite the newer scheme in CosmosDB since writes to the DB are on a document level and not a property level. Instead, it would be helpful for older versions of the code to be able to pull down the latest scheme and work with the properties that it knows about without having to force the user to update.
Does anyone know how to query a schema-less JSON document out of CosmosDB? Thanks!
Use JObject as the type to query a schema-less JSON document out of CosmosDB, and use following code to pull the raw JSON data into your object:
JsonConvert.DeserializeObject<Class>(JSONString);

Angular2 HTTP Providers, get a string from JSON for Amcharts

This is a slightly messy questions. Although it appears I'm asking question about amCharts, I really just trying to figure how to extract an array from HTTP request and then turn it into a variable and place it in to 3-party javacript.
It all starts here, with this question, which was kindly answered by AmCharts support.
As one can see from the plnker. The chart is working. Data for the chart is hard coded:
`var chartData = [{date: new Date(2015,2,31,0,0,0, 0),value:372.10,volume:2506100},{date: new Date(2015,3,1,0, 0, 0, 0),value:370.26,volume:2458100},{date: new Date(2015,3,2,0, 0, 0, 0),value:372.25,volume:1875300},{date: new Date(2015,3,6,0, 0, 0, 0),value:377.04,volume:3050700}];`
So we know the amCharts part works. Know where the problem is changing hard coded data to a json request so it can be dynamic. I don't think this should be tremendously difficult, but for the life of me I can't seem figure it out.
The first issue is I can't find any documentation on .map, .subscribe, or .observable.
So here is a plunker that looks very similar to the first one, however it has an http providers and injectable. It's broken, because I can't figure out how to pull the data from the service an place it into the AmCharts function. I know how pull data from a http provider and display it in template using NgFor, but I don't need it in the template (view). As you can see, I'm successful in transferring the data from the service, with the getTitle() function.
this.chart_data =_dataService.getEntries();
console.log('Does this work? '+this.chart_data);
this.title = _dataService.getTitle();
console.log('This works '+this.title);
// Transfer the http request to chartData to it can go into Amcharts
// I think this should be string?
var chartData = this.chart_data;
So the ultimate question is why can't I use a service to get data, turn that data into a variable and place it into a chart. I suspect a few clues might be in options.json as the json might not be formatted correctly? Am I declaring the correct variables? Finally, it might have something to do with observable / map?
You have a few things here. First this is a class, keep it that way. By that I mean to move the functions you have inside your constructor out of it and make them methods of your class.
Second, you have this piece of code
this.chart_data =_dataService.getEntries().subscribe((data) => {
this.chart_data = data;
});
What happens inside subscribe runs asynchronously therefore this.chart_data won't exist out of it. What you're doing here is assigning the object itself, in this case what subscribe returns, not the http response. So you can simply put your library initialization inside of the subscribe and that'll work.
_dataService.getEntries().subscribe((data) => {
if (AmCharts.isReady) {
this.createStockChart(data);
} else {
AmCharts.ready(() => this.createStockChart(data));
}
});
Now, finally you have an interesting thing. In your JSON you have your date properties contain a string with new Date inside, that's nothing but a string and your library requires (for what I tested) a Date object, so you need to parse it. The problem here is that you can't parse nor stringify by default a Date object. We need to convert that string to a Date object.
Look at this snippet code, I used eval (PLEASE DON'T DO IT YOURSELF, IS JUST FOR SHOWING PURPOSES!)
let chartData = [];
for(let i = 0; i < data[0].chart_data.length; i++) {
chartData.push({
// FOR SHOWING PURPOSES ONLY, DON'T TRY IT AT HOME
// This will parse the string to an actual Date object
date : eval(data[0].chart_data[i].date);
value : data[0].chart_data[i].value;
volume : data[0].chart_data[i].volume;
});
}
Here what I'm doing is reconstructing the array so the values are as required.
For the latter case you'll have to construct your json using (new Date('YOUR DATE')).toJSON() and you can parse it to a Date object using new Date(yourJSON) (referece Date.prototype.toJSON() - MDN). This is something you should resolve in your server side. Assuming you already solved that, your code should look as follows
// The date property in your json file should be stringified using new Date(...).toJSON()
date : new Date(data[0].chart_data[i].date);
Here's a plnkr with the evil eval. Remember, you have to send the date as a JSON from the server to your client and in your client you have to parse it to a Date.
I hope this helps you a little bit.
If the getEntries method of DataService returns an observable, you need to subscribe on it to get data:
_dataService.getEntries().subscribe(
(data) => {
this.chart_data = data;
});
Don't forget that data are received asynchronously from an HTTP call. The http.get method returns an observable (something "similar" to promise) will receive the data in the future. But when the getEntries method returns the data aren't there yet...
The getTitle is a synchronous method so you can call it the way you did.

Best way to get users folder using as-user in new Box Java SDK

Per Box example easy way to get user's root folder using below code
http://opensource.box.com/box-java-sdk/
BoxAPIConnection api = new BoxAPIConnection("your-developer-token");
BoxFolder rootFolder = BoxFolder.getRootFolder(api);
for (BoxItem.Info itemInfo : rootFolder) {
System.out.format("[%d] %s\n", itemInfo.getID(), itemInfo.getName());
}
But if i need to access someone else info using As-user, I'm unable to use BOX SDK classes (BoxFolder, BoxFile, BoxUser...) and need to get the data only from JSON directly like below.
If i do so, i'm loosing the latest features added in the new SDK. Is it the best way? How about the performance? Is there any alternative way available?
url= new URL("https://api.box.com/2.0/folders/0");
BoxAPIRequest request = new BoxAPIRequest(api,url,"GET");
request.addHeader("As-User", "12345678");
BoxJSONResponse response = (BoxJSONResponse) request.send();
JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
Later get the folder properties using JsonObject / JsonArray. If i need the folder items, i need to loop the JsonArray like below
JsonArray entries = responseJSON.get("entries").asArray();
for (JsonValue entry : entries)
{ ....}
Unfortunately, the new Java SDK beta doesn't have built-in support for "As-User" functionality yet, which makes this kind of tricky. One workaround is to use a RequestInterceptor with your BoxAPIConnection to manually add the "As-User" header to every request.
api.setRequestInterceptor(new RequestInterceptor() {
#Override
public BoxAPIResponse onRequest(BoxAPIRequest request) {
request.addHeader("As-User", "user-id");
// Returning null means the request will be sent along with our new header.
return null;
}
}
This should let you use the rest of the SDK normally and not have to worry about doing the API requests manually. I also created an issue for adding "As-User" support.

Knockout: nested Elements as observable

I'm trying to build an complexe view with knockout.js and have a few problems..
The content data for the view model is loaded over ajax as JSON. The JSON is quite complex and has multiple nested objects from which some should be observable and others not.
Here is a little example (real one is some levels deeper)
{
BaseData:{
Title:'BaseDataTitle',
DataArray:[{Title:'obs1'}],//this should be observable
SecondArray:[{Title:'notobs1'}],//this should not be observable
},
DataArray:[{Title:'obs1'}]
}
http://jsfiddle.net/wPs7e/
is there any possibility to do that with knockout?
thanks for your help!
You'll have to do some work with your objects...you could use the mapping plug-in, however you'll still need to create some objects to receive your data and conditionally apply the bindings...
var KOObject = function(rawdata) {
var self = this;
self.BaseData = new BaseDataObject(rawdata.BaseData);
self.DataArray = rawdata.DataArray;
}
var BaseDataObject = function(baseData) {
var self = this;
self.Title = baseData.Title;
// you could keep chaining the creation of objects with this concept
self.DataArray = ko.observableArray(baseData.DataArray);
self.SecondArray = ko.observableArray(baseData.SecondArray);
}
before you apply your bindings, new up this object
ko.applyBindings(new KOObject(rawdata));
The mapping plugin is a great idea for automating both the serialization and deserialization of data with knockout...it's just not always necessary if you have the option of initializing your data sets manually...it's good practice anyways.

how to send the data in Json structure

I have a rest service for which I am sending the Json data as ["1","2","3"](list of strings) which is working fine in firefox rest client plugin, but while sending the data in application the structure is {"0":"1","1":"2","2":"3"} format, and I am not able to pass the data, how to convert the {"0":"1","1":"2","2":"3"} to ["1","2","3"] so that I can send the data through application, any help would be greatly appreciated.
If the format of the json is { "index" : "value" }, is what I'm seeing in {"0":"1","1":"2","2":"3"}, then we can take advantage of that information and you can do this:
var myObj = {"0":"1","1":"2","2":"3"};
var convertToList = function(object) {
var i = 0;
var list = [];
while(object.hasOwnProperty(i)) { // check if value exists for index i
list.push(object[i]); // add value into list
i++; // increment index
}
return list;
};
var result = convertToList(myObj); // result: ["1", "2", "3"]
See fiddle: http://jsfiddle.net/amyamy86/NzudC/
Use a fake index to "iterate" through the list. Keep in mind that this won't work if there is a break in the indices, can't be this: {"0":"1","2":"3"}
You need to parse out the json back into a javascript object. There are parsing tools in the later iterations of dojo as one of the other contributors already pointed out, however most browsers support JSON.parse(), which is defined in ECMA-262 5th Edition (the specification that JS is based on). Its usage is:
var str = your_incoming_json_string,
// here is the line ...
obj = JSON.parse(string);
// DEBUG: pump it out to console to see what it looks like
a.forEach(function(entry) {
console.log(entry);
});
For the browsers that don't support JSON.parse() you can implement it using json2.js, but since you are actually using dojo, then dojo.fromJson() is your way to go. Dojo takes care of browser independence for you.
var str = your_incoming_json_string,
// here is the line ...
obj = dojo.fromJson(str);
// DEBUG: pump it out to console to see what it looks like
a.forEach(function(entry) {
console.log(entry);
});
If you're using an AMD version of Dojo then you will need to go back to the Dojo documentation and look at dojo/_base/json examples on the dojo.fromJson page.