I am trying to create a page that shows a list of products with filtering and multiple ways of sorting the list. Rather than repeating the filtering code for each sort, I wanted to start each of the sorting methods with a call to the filtering method. I can currently filter by name:
filterText: '',
filteredResults: function() {
var filterText = this.get('filterText');
var regexp = new RegExp(filterText,'gi');
var causes = this.get('model.products').filter(function(name){
return name.get('name').match(regexp);
});
return causes;
}.property('filterText'),
However I now want to use this function in the sort methods. I've tried using Ember.computed.sort :
priceAscSorting: ['price'],
sortPriceAsc: Ember.computed.sort('filteredResults', 'priceAscSorting'),
but it seems like it treats the first input as null if I stick it in a template. If I try calling the filter method inside the sorting method:
priceAscSorting: ['views'],
sortPriceAsc: function() {
var products = filteredResults();
}
Ember throws a compiler error saying 'filteredResults' is not defined. How can I access my filter methods to use in my sorting methods?
Your code is correct, it will take filteredResults computed property and return sorted result in ascending order by using price key.
priceAscSorting: ['price'],
sortPriceAsc: Ember.computed.sort('filteredResults', 'priceAscSorting'),
but it seems like it treats the first input as null if I stick it in a
template
that means you need to check filteredResults computed property
Related
I'm trying to render a table using data from an array of objects. These have been created with a constructor which has a number of prototype methods. All the methods work correctly outside of the specific problem I'm having.
The table needs to be rendered dynamically, eventually with user input selecting which columns they want rendered and in which order. As such I'm trying to use a separate array to house the selected fields (filled in manually for now).
Below I have an example of the object, the prototype, the array, and the array of objects forEach method containing a simple console.log.
const jsonResultTotal = {
new_stats: [
{
revenue: 'US$200.00',
cost: 'US$4.09',
}]
};
jsonResultTotal.prototype = {
...jsonResultTotal.prototype,
roas: function() {
return (Number(this.revenue.replace(/[^0-9.]/g, '')) / Number(this.cost.replace(/[^0-9.]/g,''))).toFixed(
2);
},
const rowCellContent = [
'revenue',
'cost',
roas(),
'roas()'
roas
'roas'
];
jsonResultTotal.new_stats.forEach(function(e) {
for (i=0; i<=5;i++){
console.log(e[rowCellContent[i]])
}
}
The result of the above code is
'US$200.00'
'US$4.09'
undefined
Well, actually that specific rowCellContent array will result in roas() is not defined, but the point is that none of the methods will work.
If I console.log(e.roas()) then it works, so accessing the method seems to be fine. I've tried arrow functions in the rowCellContent array, as well as full functions with a return of this.roas(), I've tried to used bind on the e[rowCellContent[i]]
There doesn't seem to be any issues calling the properties through the array, or calling the prototype methods directly, I just can't get the methods via the array to work.
Any help would be appreciated.
We have a nested dataLayer variable on our booking platform. Users can make one or multiple variables are we want to pull out a string containing each of the product types contained within the array. I am hitting a error when debugging this however.
The location of the variable I would like to collect is:
dataLayer.booking.products[i].travelType
try{
var productList = {};
for(i=0;i<dataLayer.booking.products.length;i++){
productList[dataLayer.booking.products[i].travelType];
}
return productList.join('|');
}
catch(err){}
I am naive with JS so I apologies for a basic question.
M
Your code shows that you're setting a new property of the object productList, but you're not defining a value, e.g. {foo: } instead of {foo: "bar"}. It looks like what you want is an array that you can add strings to. For example:
var productList = dataLayer.booking.products.map(function(product) {
return product.travelType;
});
return productList.join('|');
Note that this is using the Array's map method as opposed to your for loop. You could also define productList as an array in a previous line, and then use the forEach method on the products Array to loop through every item, but I think this is cleaner and still legible. You can reduce the code further with ES6 syntax, but for your question it's probably better to show code that is more clearly defined.
I have an object that represents a restaurant order:
function order () {
this.customer_name = ''
this.menu = // menu object
}
extended with some object methods for business logic, like:
order.prototype.value = function() {
var total = 0;
for (var i = 0; i < this.menu.length; i++) {
// calculate the order value
}
return total;
}
In the angular controller orders get pushed onto an array when submitted (via ng-click from a button in the view):
var ref = new Firebase('https://myfirebase.firebaseio.com');
$scope.orders = [];
angularFire(ref, $scope, 'orders');
$scope.currentOrder = orderService;
$scope.submitOrder = function() {
$scope.orders.push($scope.currentOrder);
};
Once orders are pushed into the array, properties like orders[0].customer_name work, but methods like orders[0].value() don't.
It seems reasonable that Firebase/Angularfire would only be syncing JSON, but is there an approach that would allow me to keep order-related logic included with the order object, i.e without having to write $scope.getOrderValue(orders[0])?
There isn't a great way to do exactly what you want, since according to the Firebase FAQ:
At a low-level, we support basically the same data types as JSON: Strings, Numbers, Booleans, and Objects (which in turn contain Strings, Numbers, Booleans, and more Objects).
Which means you can store data but not functions. It seems like a clean way to accomplish the same thing would be to store the latest order value as a property of your order object, and have a method as part of your orderService that updates it whenever menu items are added or removed. Alternatively, do what you suggested and have a getOrderValue somewhere, but it probably still makes sense to put that in a service.
I actually had the same issue.
I wanted to add a method to my firebase object.
After looking in the latest angularfire docs I found that $extend can do just that
I didn't test it yet, but I think this is the way to go about it.
What is the basic way to keep models any views in order using Backbone? I have some idea but it's not totally clear.
I want to keep them in order by a field called "created_at". I know there is the ability to provier a comparator function in the collection but I'm not sure how it works.
I also want this order in the collection to be reflected by the views at all times (in a list). I'm not exactly sure where I tie into the model though. I'm guessing i look for change in an index attribute and then update a to match?
Thanks very much for any help or explanation!
When you define your collection, you also define comparators.
I did it this way recently:
comparators: {
id: function(animal) {
return Number(animal.get("id"));
},
d_id: function(animal) {
return -Number(animal.get("id")); // descending
},
name: function(animal) {
return animal.get("name");
},
d_name: function(animal) {
return String.fromCharCode.apply(String, _.map(animal.get("name").split(""), function (c) {
return 0xffff - c.charCodeAt();
})
);
},
}
these I defined within my collection code.
Then, in rendering my collection views, I just did this
(this was within my view that renders the whole collection, in initialize():
this.collection = new MyCollection();
this.collection.comparator = Collection.comparators[// here I put 'id' or 'd_id' etc. ];
this.collection.sort();
Since this code is in your views's initialize, you can define your comparator
when you initialize your view, and pass it a name of a comparator like this:
var directory = new pageView("d_id");
and than thru initialize(comparator_id) you could pass this to your code in initialize:
this.collection = new MyCollection();
this.collection.comparator = Collection.comparators[comparator_id];
this.collection.sort();
And then I can use the collection in rendering and re-rendering the view/page
Edited:
Here is Backbone's collection.comparator documentation,
And right below it is an explanation of sort()
Basically, comparator can be a property of a model or a function that returns a property,
Or a negative property, if it's numeric, for descending order,
or a string or its reverse value for descending order
like in the example I gave you here.
So, comparator returns a property like "id" or "name", or "-id" , "-name" etc etc.
(for string you can't just make it "negative", you need to apply more complex function,
as I wrote.)
I've done some google-fu but all I can find about AngularJS filters is simple examples about simple filters (mostly on a single field's value).
What I'm after thoguh is somewhat more complex, and I kinda look for help on how to tackle my situation.
Imagine you have an array of the following JSON objects:
{
"id":"1",
"title":"Title",
"categories":[
{"id":"14","name":"DIY"}
],
"topics":[
{"id":"12","name":"Junk Food"}
]
},
{
"id":"2",
"title":"Title 2",
"categories":[
{"id":"4","name":"Test"},
{"id":"14","name":"DIY"},
],
"topics":[
{"id":"2","name":"Food"}
]
}
[...]
so basically each object can have ANY number of "categories" and / or "topics".
Now, my goal is to create a frontend interface that allows me to cumulatively apply various kinds of filters to those JSON objects.
For example, I'd like to say: show only the entries that have category.id = 14 AND topic.id = 2 [etc] and still support deep-linking for the filtered results.
So here's where I'm stuck:
1) what's the best way to use routes for this (ie how would you structure the URLs to support ANY number of filter (based on different values)
2) how should i keep track of the filters added? (ie, how many and which filters have been selected by the user)
Looking at the documentation for the AngularJS filters I'll obviously use the 2nd example for the filtering parameter:
Object: A pattern object can be used to filter specific properties on objects contained by array. For example {name:"M", phone:"1"} predicate will return an array of items which have property name containing "M" and property phone containing "1". A special property name $ can be used (as in {$:"text"}) to accept a match against any property of the object. That's equivalent to the simple substring match with a string as described above.
But I'm not so sure on how to make sure i'm checking the right field (ie topic.id for topics vs category.id for categories)...
Simply put, I'd love to see an example for such a less-trivial filtering scenario.
I think you need something like this instead. See his 'other simple alternative'. I do complex filtering in a service that's injected into my controller, and expose the filtered list on my $scope to the View. I only use Angular filters for relatively simple tasks.
Re: the question about how to expose this on the URL, you'll need some way of representing those filters as strings, and can use $location and $routeParams to populate them into your controller.
This can work if you write a custom filter:
var module = angular.module('app', []);
module.filter("property", ["$filter", function($filter){
var parseString = function(input){
return input.split(".");
}
function getValue(element, propertyArray) {
var value = element;
angular.forEach(propertyArray, function(property) {
value = value[property];
});
return value;
}
return function (array, propertyString, target) {
var properties = parseString(propertyString);
return $filter('filter')(array, function(item){
return getValue(item, properties) == target;
});
}
}]);
HTML part can look like this:
<ul>
<li ng-repeat="data in items | property:'categories.id':<id_of_a_category_we_want>">
{{ data }}
</li>
</ul>
Credit: OnOFF-Switch blog