I would like to add vertical lines to graph as markers for interesting events.
The only way i can see to add multiple lines is to define multiple controller.verticalLine annotations like
controller.verticalLine({
xAnchor: "2007-09-23"
});
controller.verticalLine({
xAnchor: "2008-10-23"
});
Is it possible to do this nicer like
controller.verticalLine([{
xAnchor: "2007-09-23"
},
{
xAnchor: "2007-10-23"
},
{
xAnchor: "2007-11-23"
}]);
or better, pass it a data set using mapAs, where each value in the mapping would be a xAnchor value ?
var mapping = dataTable.mapAs({"value": 4});
controller.verticalLine(mapping);
Thanks
You can define the lines from the dataset with the custom field and get them from data with the get() method:
https://api.anychart.com/latest/anychart.data.Mapping#get
mapping.get(i, "anchor"),
Like is shown here: https://jsfiddle.net/osub60ck/
Related
So I'm using this Rest API with ngResource to do get, query, post and update requests. What I'm looking for, is a way to define the structure for each entity.
For example, assuming we have:
module.factory('app.entity.item', function($resource) {
return $resource('http://xmpl.io/items/:itemId', { itemId: '#id' });
});
I want to instantiate it in a controller like:
module.controller('AddItemCtrl', ['app.entity.item', function(Item) {
$scope.item = new Item();
});
and bind it to the respective form in my template.
The actual problem that I have run into, is that I have to deal with 1:m tables.
An example of the entity structure would be:
{
"name": "",
"categories": [],
"list": [
{
"value": "",
"list": [
{
"value": "",
"list": [
{
"value": ""
}
]
}
]
}
]
}
(A more thorough example in the fiddle below)
Now the first two fields are obviously not the problem. It is the third one. The list. Each one of these lists can have a variable number of items.
I am currently using ngRepeat and an add(type, context) method, which adds a new set of fields to the scope (value field in this example and child lists for the first two levels), which will appear in UI by ngRepeat so the user can fill it up and submit it to the service.
First off, I have to define the structure, so the UI would not be empty when the page loads.
module.controller('AddItemCtrl', ['app.entity.item', function(Item) {
$scope.item = new Item({
"name": "",
"categories": [],
"list": [
{
"value": "",
"list": [
{
"value": "",
"list": [
{
"value": ""
}
]
}
]
}
]
});
});
But that is redundant. I have to do it everywhere!
Another issue is that when the item.$save is called, the model is emptied (perhaps re-instantiated?) and the fields inside the list property (managed by the ngRepeat directive) are gone.
So I'm wondering, what would you do under such circumstances.
Is there a way to define the entity (resource) structure?
SAMPLE: http://jsfiddle.net/g15sqd5s/3/
trying to give simple answer - for simple structures I would use something like
module.factory('Item', function($resource) {
var resource = $resource('http://xmpl.io/items/:itemId', { itemId: '#id' },
// you can also define transformRequest here:
{ transformRequest: function(data) {
// data can be transformed here
return angular.toJson(data);
}});
return angular.extend(resource.prototype,
{
name: null,
categories: []
});
});
but then be aware of need to 'flatten' the object.
and for the more complex model I would check restangular
similar topic is also discussed here:
How can I extend the constructor of an AngularJS resource ($resource)?
I would go ahead and revise my model structure in the backend in the first place - the models on the client side should merely follow the ones already defined, rather than being re-defined in a transform block. So, to answer your question, the "default" model structure comes from the server. What you get in your $resource objects has the structure of what your server returns.
To start off, is it really ok to invoke $save on the Item model when the user has populated some values? What we want to save are obviously the lists associated with an item, not the item itself. A separate resource defined in the backend, say items/<item_id>/list, may be a cleaner solution. It may not scale very well, as you'll have to make a separate GET request for each item to fetch its list, but that's the proper RESTful way to do it.
Extending this approach to the example in your fiddle, I imagine a routing scheme like buildings/<building_id>/floors/<floor_id>/units/<unit_id> would be a proper solution. Making a GET request to buildings/ should yield you a list of buildings; each building in the array returned should be an instance of a Building model, which has the proper URL set so the user can perform a single POST and update only the building name, instead of sending back the whole structure back to the server. Applying this recursively to the nested resources should give you a clean and concise way to deal with model changes.
Regarding the UI part - I would go ahead and define three directives for buildings, floors and units, and let each one manage an array with the respective resources, also taking care for the UI bindings to the model values.
So how could a Building model look like?
var BuildingResource = $resource('/buildings/:id', { id: '#id' });
Invoking BuildingResource.query() should yield an array of existing buildings. Adding a new building could look like this:
var newBuilding = new BuildingResource();
newBuilding.$save().then(function(building) {
$scope.buildings.push(building);
}, function(errData) {
//Handle error here...
});
It should be easy to extend this pattern for the rest of the resources - note that what the server needs to return for every building is just the name and the id; knowing the id is sufficient to construct an URL (and a $resource object, respectively) to fetch the needed child resources (in this case, floors).
I have the following code to fetch the data for my collection but with specifying what colors should come from the server:
fruits = new FruitsCollection();
fruits.fetch({
data: {color: ['red', 'green']}
});
This is what I expect:
http://localhost:8000/api/fruits/?color=red&color=green
This is what I got:
http://localhost:8000/api/fruits/?color[]=red&color[]=green
As you can see, for some unknown reason Backbone.js is appending the square brackets to the URL params, instead of having color=green I have color[]=green
I'm using django-rest-framework in the server side and I know I can do a hardcoded fix there, but I prefer to know the logic reason because it is happening and how can I solve it from my javascript.
Backbone uses jQuery.ajax under the hood for the ajax request so you need to use the traditional: true options to use "traditional" parameter serialization:
fruits = new FruitsCollection();
fruits.fetch({
traditional: true,
data: {color: ['red', 'green']}
});
var items=[{"endsAt": "2013-05-26T07:00:00Z","id": 1,"name": "Niuniu1"},
{"endsAt": "2013-05-26T07:00:00Z","id": 2,"name": "Niuniu2"}]
ItemModel=Backbone.Model.extend({});
ItemCollection=Backbone.Collection.extend({
model:ItemModel,
url: '...',
parse: function(response) {
return response.items;
}
})
If I have a series of data like items, when I build model, for each model, it's endAt will be "2013-05-26T07:00:00Z". Where can I modify the model or data process so it will actually be "2013-05-26"?
I could do a foreach loop inside collection to process the date, but I'm wondering if there is a better pracitce like to do a parse inside the model?
Thanks!
The practice I use is the one you said you've thought about - implementing a custom parse on the model. As the documentation states, it will be called for you after a sync. See here: http://backbonejs.org/#Model-parse
ItemModel = Backbone.Model.extend({
parse: function(response,options) {
//perform your work on 'response',
// return the attributes this model should have.
};
})
As far as I know, you have 2 options here
Implement a custom parse method inside your model
Implement the initialize method inside your model
Both of them don't have any problems, I did 2 ways in several projects, and they work well
In my Backbone app, on my collection I have numerous sorting methods, when rendering the views based on the collection I am currently using a global var set via the route (I do it with a global as other actions add to the collection and I want the last ordering to be used). For example
routes : {
"" : "index",
'/ordering/:order' : 'ordering'
},
ordering : function(theorder) {
ordering = theorder;
listView.render();
},
then in my view
if (typeof ordering === 'undefined') {
d = this.collection.ordered();
}
else if(ordering == 'owners') {
d = this.collection.owners();
}
_.each(d, function(model){
model.set({request : self.model.toJSON()});
var view = new TB_BB.OfferItemView({model : model});
els.push(view.render().el);
});
Where ordered and owners are the 2 ordering methods.
So my first question is, based on routes could someone advice a better way of implementing above? This view gets rendered in multiple places hence me using a global rather than passing a ordered var to the method?
Second question is - I would like to also add some filtering, so lets say I want to sort by 'price' but also do some filtering (lets say by multiple categories id). How could I add a flexible 'route' to deal with filtering.
I guess I could do
routes : {
"" : "index",
'/ordering/:order/:filter1/:filter2' : 'ordering'
},
So the filter1 and filter2 would be the subsequent filtering, but if the filters could be 0 or 100 this will not work. Could anyone offer a solution?
Well, first you should be using Backbone's built-in ability to auto-sort collections. You can take advantage of this by defining a comparator function on your collection. This gives you all kinds of wins right out of the box — for example, the collection will re-sort itself every time you add or remove something from it, based on your comparator. If you want to define multiple sort functions, just define them all as functions and then update comparator when you need to. Then you can ditch that ugly global var.
For your second question, I'm not totally sure what you mean by "if the filters could be 0 or 100 this will not work." If you mean that you'll run into trouble if you don't specifiy all of the filters, then that's true. But you can use a wildcard to fix that. Here's what that might look like:
// your routes look like this:
routes : {
'/ordering/:order/filters/*filters' : 'ordering' // your routes will look like: /ordering/price/filters/filter_one/filter_two/filter_three
},
ordering: function (order, filters) {
filters = filters.split('/'); // creates an array of filters: ['filter_one', 'filter_two', 'filter_three']
listView.render(filters); // pass your filters to the view
}
// listView.render() looks like this:
render: function(filters) {
collection = this.collection;
_.each(filters, function (filter) {
collection = collection.filter(function () {
// your actual filtering code based on what the filter is
});
});
}
In Sencha Touch, I often need to have an Ext.DataView panel that contains a small sub-set records or even a single record from the collection in the store.
For example I might have a Model for Car which has thousands of car records in it's app.stores.cars store but I want to show a smaller subset of these items (say; just sports cars) in my listOfSportsCars DataView while also showing the larger complete set of cars in my listOfCars DataView.
My first thought was to use multiple stores. So I'd have one main store for the big list of all cars, and a second store with a filter for my subset of sportscars. However, now updating a model from one store does not automatically update the record in the other store, so this defeats the purpose of using a DataView as the changes are not updated everywhere in the page when updating records.
My second attempt was to overwrite the collectData method on the DataView, which sounded exactly like what I was after:
var card = new Ext.DataView({
store: app.stores.cars,
collectData: function(records, startIndex){
// map over the records and collect just the ones we want
var r = [];
for( var i=0; i<records.length; i++ )
if( records[i].data.is_sports_car )
r.push( this.prepareData(records[i].data, 0, records[i]) );
return r;
},
tpl: new Ext.XTemplate([
'<tpl for=".">',
'<div class="car">{name}</div>',
'</tpl>'
]),
itemSelector: 'div.car'
});
A full example can be found here.
But, although it's documented that I can/should override this method, Sencha Touch really doesn't like it when you mess around with the length of the array returned by collectData so this was a dead-end.
How do others deal with displaying/updating multiple collections of the same records?
UPDATE There was a bug preventing collectData from working as expected. The bug has since been fixed in Sencha Touch 1.1.0.
As written in the comment:
I've used your democode with the last Sencha Touch release and opened all with Google Chrome. In the current version the error is fixed. (Version 1.1)
you could use Filters in order to get a subset of the data asociated to that store.
yourstore.filter('name', 'Joseph');
Also you should define 'root' as a function so it will always return an array. Readers in sencha touch asume you're always going to get an array as response, but it's not true if you are having a JSON with a single entry, try something like this:
root: function(data) {
if (data) {
if (data instanceof Array) {
return data;
} else {
return [data];
}
}
The full code for the store could be like this:
YourApp.ViewName = new Ext.data.Store({
model: 'YourApp.models.something',
proxy: {
type: 'scripttag',
url: 'http://somerandomurl/service/json',
extraParams: {
param1: 'hello'
},
reader: {
type: 'json',
root: function(data) {
if (data) {
if (data instanceof Array) {
return data;
} else {
return [data];
}
}
}
}
},
});
Hope it helps.
I use the "filter" features in the Store. Not modifying the DataView (I use a List).
Here's a snippet where I will fiter out Programs with a catagory that fit's a regex. (I have Programs with a catagory field)
MyApp.stores.Programs.filter(function(object) {
var regex = new RegExp(filterValue, 'i');
return object.data.category.search(regex) >= 0; // found match
});
You can clear the filter like this:
MyApp.stores.Programs.clearFilter(false);
This will update the DataView (I use a List) immediately (it's amazing).
So within your filter you could just filter out sports cars, or cars of a certain price, or whatever.
Hope that helps...
For my understanding of Sencha Touch this is not the best approach.
If it can be still good for performance you shoud use a second "slave" store, with inline data (http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Store) that you can populate automatically from main store with subset of information you want to show when an event occours on the master store, i.e. load event.
If you want to deal with just one store a solution I can imagine is to use an xtemplate with "tpl if" tag in the dataview where you want to show just some information
http://docs.sencha.com/touch/1-1/#!/api/Ext. to write empty records. Maybe, also better solution, could be to use a custom filter function inside xtemplate, in order to put a css with visibility hidden on the items you don't want to see.