I want to display in my template only the first element of an array. I've seen many topics including this one but it doesn't work in my case.
I have an helper like this:
Template.home.helpers({
posts() {
return Posts.find({});
}
});
I would like to do something like this in my template:
{{posts.[0].title}}
I don't want to use a findOne in this case.
Best to do this at the helper level, for example, this would add an optional index argument to the posts helper:
Template.home.helpers({
posts(index) {
if(typeof index !== "undefined" && index !== null){
var data = Posts.find();
return data[index];
}
else{
return Posts.find();
}
}
});
Then you set the data context and call it in blaze like this:
{{#with posts 0}}
{{title}}
{{/with}}
Just limit the size of the return set:
Template.home.helpers({
posts() {
return Posts.find({}, {limit: 1})
}
});
Of course you'll probably want to sort it by something sensible as well, so that the first record is definitely the one you actually want.
Related
I have a simple template that iterates over some items:
<template v-for="card in filteredCards">
filteredCards are a property I am using to filter some results by clicking a simple html link. In my Vue component I have this:
data = {
cards: '',
filteredCards: ''
}
cards is the original data coming via an ajax request - and filteredCards is what I'm actually iterating over.
The problem becomes when I do any kind of update with a filter - the template is not reflecting the filtered array. Here is how I'm filtering:
this.filteredCards = this.cards.filter(function (item)
{
return item.Data.event_type.match('something_test');
});
In devtools I can see that the array has been updated to only a single item - however the template never updates and leaves all the results showing. If I call something that actually mutates the array though like reverse - the template updates just fine. Is there something I need to do in order to force an update after filtering the array?
I've updated a bit to reflect using a custom filter. I'm still running into the same problem. In devtools I see that the filterKey is being updated from an event broadcasted from the parent instance. But nothing is being updated in the template.
var $data = {
cards: [],
filterKey: '',
loading: true
};
Vue.component('cards', {
template: '#card-template',
data: function()
{
return $data;
},
events: {
'updateFilterKey': function(key)
{
this.filterKey = key;
}
}
});
Vue.filter('onlyMatching', function(cards)
{
var $this = this;
return cards.filter(function(item)
{
return item.Data.event_type.match($this.$data.filterKey);
});
});
The code that initially gets the data is just a simple ajax call:
var getFeed = function($url)
{
$.get($url, function(response)
{
$data.loading = false;
$data.cards = response;
}).fail(function()
{
$data.loading = false;
});
};
The strange thing is with this current code - when I click back and forth between sending different keys the actual array items are being duplicated in my template over and over when I click the "all items" which sets the filterKey to an empty string.
What you're doing is absolutely backwards.
You actually want to loop the original array but apply a filter on it.
<template v-for="card in cards | filterBy onlyMatching">
Then, in your code, create a custom filter:
Vue.filter('onlyMatching', function (cards) {
return infoBlocs.filter(function(item) {
return item.Data.event_type.match('something_test');
});
})
This should totally work. Now, whenever the array is changed in any way, the filter will get triggered and the list will be re-rendered.
Come to find out the filtering was working all along. There's an issue with the conditional templating. I'll ask that in another question.
I am having trouble getting my custom filter to work.
I have a module of global filters:
angular.module('globalFilters', []).filter('dateRange', function () {
return function(item) {
console.log(item);
return true;
}
});
This is injected into my app on creation.
I am trying to apply this to an ng-repeat:
tr.pointer(ng-repeat="asset in completed | dateRange", ng-animate="'animate'", ng-click="selectAsset(asset);")
td {{asset.Name}}
However adding this filter will filter all assets out of table. To try to isolate the problem I am returning true for the function to display all assets but it is not working.
Upon logging item to the console, result appears to be an array of all assets so I guess something is wrong.
I was following this tutorial https://docs.angularjs.org/tutorial/step_09
Thanks!
You are filtering an array...so your filter function needs to return an array.
.filter('dateRange', function () {
return function(itemArray) {
if(!itemArray){
return null
}else{
return itemArray.filter(function(item){
// conditions of filter
});
}
}
});
When you define a custom filter function, the value passed into the filter is replaced by the value returned from the filter, so you are replacing item with true.
For a filter which logs the input without changing it, just return the input:
return function(item) {
console.log(item);
return item;
}
In my collection, each document has two values that look like this:
{
originalPrice: 500,
ourPrice: 420
}
As you can imagine, I'd like the users to be able to sort by how much they will save shopping from us rather than from a competitor, which for this particular good is 80.
However, this value is itself not in the database, so I can't simply do
Goods.find({}, {sort: saveAmount: 1})
or anything like that.
It would perhaps be an easy task to insert this number in the database, but unless the approach to get things working the other way is extremely complicated I'd prefer not to.
So I want a function that does this,
var saveAmount = function(originalPrice, ourPrice) {
return originalPrice - ourPrice
}
and somehow use this value to sort by!
How do I do this?
You can do it in a MongoDB query, but not with meteor. So you'll have to use a separate field as you proposed. Here's what you can do:
function setDifference (doc) {
var difference = doc.originalPrice - doc.ourPrice
Goods.update(doc._id, { $set: { difference: difference } })
}
Goods.find().observe({
added: setDifference,
updated: setDifference
})
If you are using simple-schema, you could use autoValue.
...
difference: {
type: Number,
autoValue: function () {
return this.field('originalPrice').value - this.field('ourPrice').value
}
}
...
Anyway, now you can just sort by difference:
Goods.find({}, {sort: {difference: -1}})
Here's an example of how you could display the goods in sorted order on the client:
js
Template.myTemplate.helpers({
sortedGoods: function() {
return _.sortBy(Goods.find().fetch(), function(good) {
return good.originalPrice - good.ourPrice;
});
}
});
html
<template name="myTemplate">
{{#each sortedGoods}}
<div class="good">{{name}}: ${{ourPrice}}</div>
{{/each}}
</template>
I find this weird that whenever I am declaring variables in my controller and I want to return a filtered value from my model I have to use a function. But when I try to call it using {{#each}} it gives me an error.
Uncaught Error: Assertion Failed: The value that #each loops over must be an Array. You passed function () {
return this.store.filter('entry', function(entry) {
return (entry.get('entry_week') == moment().week());
});
}
My Controller
App.CurrentWeekController = Ember.ArrayController.extend({
current_week: function() {
return this.store.filter('entry', function(entry) {
return (entry.get('entry_week') == moment().week());
});
}
});
EDIT:
Route
App.CurrentWeekRoute = App.AuthenticatedRoute.extend({
model: function() {
return this.store.find('entry');
},
setupController: function(controller, entries) {
controller.set('model', entries);
}
});
handlebars
{{#each current_week}}
{{description}}
{{/each}}
So what's a better approach? I essentially have a lots of model instances and I need to filter them by certain conditions and display them separately.
In any case, how can I have a controller variable contain a filtered set of my model instances.
You can empty your controller and then update your route to this:
App.CurrentWeekRoute = App.AuthenticatedRoute.extend({
model: function() {
var current_week = moment().week();
return this.store.filter('entry', function(entry) {
return entry.get('entry_week') == current_week;
});
}
});
Now, just because you have this code here does not mean that something will show up. Your filter may not be working as you expect.
You may want to do this in your hbs file:
{{#each current_week}}
{{description}}
{{else}}
<p>No entries this week.</p>
{{/each}}
You need to declare those functions as Computed Properties of the properties you are using to filter the items.
Additionally, you shouldn't have to use DS.Store#filter, but instead use Ember.Enumerable#filter on the App.CurrentWeekController model. The model property already contains a live collection of all your App.Entry records.
App.CurrentWeekController = Ember.ArrayController.extend({
current_week: function() {
return this.get('model').filter(function(entry) {
return (entry.get('entry_week') == moment().week());
});
}.property('#each.entry_week')
});
One caveat is that the current_week array won't be updated on moment().week() change. You need some way of making sure current_week is recomputed when the week changes. Have a look at http://emberjs.com/guides/cookbook/working_with_objects/continuous_redrawing_of_views/ for some inspiration.
Alternative approach
Assuming your model is App.Entry, define an isCurrentWeek computed property in your model definition, and use it in your template to show only those.
App.Entry = DS.Model.extend({
// ...
isCurrentWeek: function() {
return this.get('entry_week') == moment().week();
}.property('entry_week')
});
And in your template:
{{#each}}
{{#if isCurrentWeek}}
{{description}}
{{/if}}
{{/each}}
I wanna jump on the bandwagon of adding a million good answers :) I recommend using filterBy, quick and clean.
App.CurrentWeekController = Ember.ArrayController.extend({
current_week: function() {
return this.filterBy('entry', moment().week());
}.property('#each.entry_week')
});
http://emberjs.jsbin.com/OxIDiVU/702/edit
I want to return the child elements (notes) of a given draft (parent element) as a computed property from my Ember.DocumentController.
In this case I want to return all the notes that belong to the editableDraft property.
Or is there a better way of doing it?
App.DocumentController = Ember.ObjectController.extend({
editableDraft: function() {
var editDrafts = this.get('model.drafts').filterBy("editable", true);
var draft = editDrafts.length ? editDrafts[0] : null;
return draft;
}.property('model.drafts.#each.editable'),
editableNotes: function() {
var eDraft = this.get("editableDraft"); // want to return notes of editableDraft
return eDraft.get("notes");
}.property('model.drafts.#each.editable')
});
See test app in the jsbin!
Two problems. One, in the document template, here:
{{render 'editableDraftNotes' notes}}
Render does some weird thing with replacing your controller and model with the provided arguments. Not what you need in this case. Try this:
{{partial 'editableDraftNotes'}}
Two, in the editableNotes property. You should listen to changes on editableDraft. Also, because Ember.Data returns promises, you must chain your gets (this.get("A.B") instead of this.get("A").get("B")). Try this:
editableNotes: function() {
return this.get("editableDraft.notes");
}.property('editableDraft')
Working jsbin here.