I am returning JSON data from a remote server which holds some information. (The product name and the date).
I want to modify the date output in my handlebars template. Here is a JSBin with my code.
Should I use a property in order to change the format of the date? Or there is a better way to do that? If yes, what should I do?
Thank you in advance.
Ember Helper:
The better way to do it is to use an Handlebars Helper to convert your date into a useable format. The reason a helper is better than a computed property, is it can be reused throughout your code anywhere you need to format a date in the template.
I have updated the JSBin to use this helper.
Helper:
Ember.Handlebars.helper('dateformat', function(value) {
// Use moment, or alternatively use JavaScript date and return your own custom string
return moment(value).format("DD/MM/YY"); // Change to suitable format. See http://momentjs.com/docs/
});
Usage:
{{dateformat item.date}}
Note:
This example uses the MomentJS library to convert the date into format DD/MM/YY but obviously you can change this.
It is possible to do the conversion without using this external library, using the standard JavaScript date object. The helper just needs to return the formatted string.
Computed Property:
To do it as a computed property requires a little more effort. You can see a working demo of this here. The demo uses a modified version of your localeDate function.
Ember Object with Computed Date Property:
So create a HistoryItem object that has the computed property.
App.HistoryItem = Ember.Object.extend({
localeDate: function() {
var dt = new Date(this.get('date'));
return dt.toLocaleDateString() + " " + dt.toLocaleTimeString();
}.property('date')
});
Update Model:
The model is a plain object, you need to create an instance of the HistoryItem for each item in your history.
App.IndexRoute = Ember.Route.extend({
model: function() {
// Get the history
var history = App.JsonRequestFromRemoteServer.history;
var result = [];
// Create item as history item
history.forEach(function(item){
result.push(App.HistoryItem.create(item));
});
// Return the result as the model
return { history: result };
}
});
Hope this helps.
To address why your JSBin here didn't work:
You were trying to use property("history.#each.date"), to create a computed property on each item in the collection.
Unfortunately what this does is provide what's called an Aggregated Property. You can read more about aggregated properties here, but essentially that would give you one property on your history object based on all the dates in the collection. Not a single property on each item as desired.
I do think this is an area of Ember that should be better covered in the documentation, you're not the first person to think you can add computed properties to collections that way.
Related
I'm using the Chap Links Timeline Library http://almende.github.io/chap-links-library/js/timeline/doc/ and find it very useful. However I need to stack the events in a special order.
I tried to use the customStackOrder() function without any success so far.
How can I access custom data fields in this function?
My data elements look like:
data.push({
start: new Date( msg.timestamp ),
content: msg.text,
stackOrder: msg.level
});
and
function customStackOrder(A,B) {
return A.stackOrder - B.stackOrder
}
But A.stackOrder and B.stackOrder is undefined.
How can I access custom data fields in this function?
The Timeline does just ignores custom fields, and there is no easy way to access them. Usable item properties in the function customStackOrder are the actual position and size (left, right, width, height) and timestamps (start and optionally end).
If you really want to do this, you will have to apply an inefficient hack. This is not officially supported and I don't recommend it:
From the items passed to the sorting function you will have to determine their table index: var indexA = mytimeline.items.indexOf(itemA)
Read the data of this item from your original data table: var dataA = data[indexA].
Read your custom field from this: var stackOrderA = dataA.stackOrder
Edit:
Another trick could be: if you don't use the field className, you could misuse this to hold the value of your stackOrder field. Just ensure the values cannot collide with some actual css.
I have an array of objects. Say
var
sidelist = [
{
name:"asdf",
id:1234,
types:[...]
}
];
Every object is turned into a box on the page using this construct
Template.global.side = function(){
var obj = [], m;
m = 1;
for (var i in sides){
obj.push({
index : m,
object : sides[i]
});
}
return obj;
}
The HTML:
{{#each side}}
<div class="span{{this.index}}" id={{this.object.id}}>
<div class="side-head">{{this.object.name}}</div>
</template>
There is a function that creates and pushes a new object into the array. How do I make the row of boxes reactively update on the page when the array they depend on changes?
So when I add a new object a new box should appear.
If you want to use Dependencies, it can look like this:
var sidelist = ...;
var sidelist_dep = new Deps.Dependency;
Template.global.side = function(){
sidelist_dep.depend();
// Do your stuff here;
return ...;
};
// Important: call this every time you change sidelist,
// AFTER the change is made.
sidelist_dep.changed();
See: http://docs.meteor.com/#deps
In almost all cases, you should put the objects in a Meteor Collection instead of an array that is part of a reactive object. There are many reasons for this, including the following
Adding, removing, searching, and updating will all be faster
The reactivity will be on the element level instead of the array
Meteor won't re-render the whole set of objects when something is added or deleted - just the change
You can define a sort order on the collection, making it much more flexible than a fixed sequence
Take a look at Andrew Wilcox's isolate-value smart package:
https://atmosphere.meteor.com/package/isolate-value
The README contains the exact example of selectively rerendering relevant templates when values are added/removed from an array stored in a Session varaible.
Let's say I want to store some custom value in a element, I would need:
$('div').data('k','v');
But now I need to add more data to that element: v2.
The only way I could come up with is to somehow store or reference the previous data, and append to it, but it doesn't look like the best way to do it nor the most efficient:
$('div').data('k','v');
var prevData = $('div').data('k');
$('div').data('k',prevData + ',v2');
alert($('div').data('k'));
This will alert v,v2 as it should, but is this the correct approach?
You can use lists as data attributes:
$('.selector').data('test', []);
var list = $('.selector').data('test');
list.push('foo')
It's pretty useful, also because you can fill data- attributes with JSON in the HTML at page generation time, and .data() will automatically convert them to normal JS objects.
if you are using array as data atribute, you can do this
$('.selector').data('list', []);
$('.selector').data('list').push(1);
$('.selector').data('list').push(2);
$('.selector').data('list'); // Will return [1,2]
I'm new to jQuery and just playing for fun. I have some code that I want to try to modify for my needs but the current js file is getting its data from google spreadsheets and then returning each item as objects. I don't use json to pass data from my server to jQuery so I'm wondering how I can convert json to objects.
The current way its doing it is(tabletop is the name of their js program that gets data from google docs):
Tabletop.init({
key: timelineConfig.key,
callback: setupTimeline,
wanted: [timelineConfig.sheetName],
postProcess: function(el){
//alert(el['photourl']);
el['timestamp'] = Date.parse(el['date']);
el['display_date'] = el['displaydate'];
el['read_more_url'] = el['readmoreurl'];
el['photo_url'] = el['photourl'];
}
});
I have added alerts all over the file and I think this is the area that gets the data and passes it on. I was thinking of trying to replace items in their object with objects from my json and see if it changes anything, but I'm unsure. Typrically I pass individual items via json,hashmaps, and lists, not sure how it works with objects or how to access objects(I simply call url's that I create for the requests, $("#user-history").load("/cooltimeline/{{ user.id }}");). But where do I start if I want to turn json data into objects?
If it helps, here's the demo of what I'm trying to do(but by having it use json data).
p.s. I'm really looking for the logic of how to complete what I'm trying to do and perhaps some ideas I'm missing so I can google them and learn.
Use use function JSON.parse(json) :) Or jQuery.parseJSON(json)
var json = '{"a":2}';
var object = JSON.parse(json);
alert(object.a);
You should see alert with message: 2
I don't realy know if I understand your comment, but maybe you want just do this:
postProcess: function(el){ //here el is JSON string
el = JSON.parse(el); // now el is an object
el.timestamp = Date.parse(el.date);
el.display_date = el.displaydate;
el.read_more_url = el.readmoreurl;
el.photo_url = el.photourl;
return el;
}
Btw. you do not need to use brackets on know property names without not standard names:
el['timestamp'] === el.timestamp
It will be easier if you paste your JSON
I want my renderer to run only once for each row.
So obviously my renderer should look something like
renderer: function() {
if (there_is_a_rendered_value_in_view) {
return rendered_value_in_view;
}
return 'generate some return';
}
Is it possible to do?
So how to get rendered_value_in_view?
UPD:
seems like I'm not detailed enough.
Well, the generated value after that is changed outside the grid, so the question is: How to get currently displayed value
You can always add boolean flag, and your rendered_value_in_view to the grid itself. And then in the renderer function check grid property and return it.
Update: from the Sencha docs here are list of parameters your renderer function will get:
value : Object
metaData : Object
record : Ext.data.Model
rowIndex : Number
colIndex : Number
store : Ext.data.Store
view : Ext.view.View
I think the last one will be your grid object.
http://docs.sencha.com/ext-js/4-0/#!/api/Ext.grid.column.Column-cfg-renderer
It'd be fairly difficult to try and capture the rendered value. I think the better way would be to add another model to your field that contains this new value. You can use a convert method so that when the original value changes, the display value can also change.
Maybe -
...
there_is_a_rendered_value_in_view:false,
renderer:function() {
if (!this.there_is_a_rendered_value_in_view) {
this.there_is_a_rendered_value_in_view=true;
return rendered_value_in_view;
}
return 'generate some return';
}