Aurelia get value conventer results in view - javascript

I would like to get the result of value conventer that filters an array in my view in order to display the number of results found.
<div repeat.for="d of documents|docfilter:query:categories">
<doc-template d.bind="d"></doc-template>
</div>
I neither want to move this logic to my controller (to keep it clean), nor to add crutches like returning some data from the value controller.
What I want:
So, basically I would like something like angular offers:
Like shown here:
ng-repeat="item in filteredItems = (items | filter:keyword)"
or here: ng-repeat="item in items | filter:keyword as filteredItems"
What I get:
Unfortunately, in Aurelia:
d of filteredDocuments = documents|docfilter:query:categories
actually means d of filteredDocuments = documents |docfilter:query:categories, and if I add brackets or as, it won't run (fails with a parser error).
So,
Is there a clean way of getting data out of data-filter in view?
Best regards, Alexander
UPD 1: when I spoke about returning some data from the value controller I meant this:
export class DocfilterValueConverter {
toView(docs, query, categories, objectToPassCount) {
...
objectToPassCount.count = result.length;
...
});
});
UPD 2. Actually, I was wrong about this: d of filteredDocuments = documents |docfilter:query:categories. It does not solve the issue but what this code does is :
1) filteredDocuments = documents |docfilter:query:categories on init
2) d of filteredDocuments which is a repeat over the filtered at the very beginning array

Assuming you have an outer-element, you can stuff the filtered items into an ad-hoc property like this:
<!-- assign the filtered items to the div's "items" property: -->
<div ref="myDiv" items.bind="documents | docfilter : query : categories">
<!-- use the filtered items: -->
<div repeat.for="d of myDiv.items">
<doc-template d.bind="d"></doc-template>
</div>
</div>
I know this isn't exactly what you're looking for but it will do the job. I'm looking into whether it would be helpfull to add a let binding command- something like this: <div let.foo="some binding expression">
Edit
Here's something a bit nicer:
https://gist.run/?id=1847b233d0bfa14e0c6c4df1d7952597
<template>
<ul with.bind="myArray | filter">
<li repeat.for="item of $this">${item}</li>
</ul>
</template>

Related

How to use ngRepeat on element returned from a function (a filter, actually) - AngularJS?

I need to use something like the following:
<ul>
<li ng-repeat='item in getShopItems(shop.id)'>{{item.name}}</li>
</ul>
I have to use a function since I need the current element of the ngRepeat to apply the filter.
The getShopItems(id) function is as follows:
$scope.getShopItems = function(id){
filteredItems = $filter('filter')($scope.items, {shop_id: shop_id});
return filteredItems;
}
Using ngInit can help but the official documentation discourages it.
If we cannot use functions for ngRepeat, how else should I apply the filter. Thanks in advance.
And here's a plunker: http://plnkr.co/ugOZ7QgQ3EHVQUGWTCII [not working]
EDIT: provider and shop are the same
EDIT: I don't want to filter in view like item in items | filter:{}
You could use filter as custom filter:
<ul>
<li ng-repeat='item in items | filter:getShopItems'>{{item.name}}</li>
</ul>
and modify filter function a little:
$scope.getShopItems = function(item) {
return item.provider_id == $scope.shop.id;
}
I think, you have two solution, either you init your array in your controller :
function getShopItems(id){
filteredItems = $filter('filter')($scope.items, {provider_id: provider_id});
return filteredItems;
}
$scope.filterItems = getShopItems($scope.shop.id)
And for the views :
{{item.name}}
But it is possible that this solution does not work in your case, for example if your id is suseptible to change.
A other way would be directly use your filter in the views :
<ul>
<li ng-repeat='item in items|filter:{"id":shop.id}'>{{item.name}}</li>
</ul>
I would also recommand you to read this thread: How to Loop through items returned by a function with ng-repeat?

Conditionally bind data in AngularJS

I have an array of tasks. They have titles and and labels.
function Task(taskTitle, taskType) {
this.title = taskTitle;
this.type = taskType;
}
$scope.tasks = [];
I end up declaring a bunch of tasks with different types and adding them to the array
In my html, I show a column of cards, filtered by type of task:
<div ng-model="tasks">
<div class="card" ng-repeat="abc in tasks track by $index" ng-show="abc.type==0">
<p> {{ abc.title }} </p>
</div>
</div>
I want to bind the first card displayed in this filtered view to some other div. I'll be processing an inbox, so I'll whittle this list of cards down to zero. Each time I 'process' a card and remove it from the list, I need the data to refresh.
<div ng-model="firstCardInFilteredArray">
<h4>Title of first card:</h4>
<p> This should be the title of the first card! </p>
</div>
My intuition was to do something like this (in javascript):
// pseudo-code!
$scope.inboxTasks = [];
for (i=0; i<tasks.length(); i++) {
if (tasks[i].type == 0) {
inboxTasks.append(tasks[i]);
}
}
and somehow run that function again any time the page changes. But that seems ridiculous, and not within the spirit of Angular.
Is there a simple way in pure javascript or with Angular that I can accomplish this conditional binding?
You can filter your ng-repeat: https://docs.angularjs.org/api/ng/filter/filter
<div ng-model="tasks">
<div class="card" ng-repeat="abc in filteredData = (tasks | filter: {type==0}) track by $index">
<p> {{ abc.title }} </p>
</div>
</div>
Additionally, by saving the filtered data in a separate list you can display the next task like this:
<div>
<h4>Title of first card:</h4>
<p> filteredData[0].title </p>
</div>
Your data will automatically update as you "process" tasks.
The other answers helped point me in the right direction, but here's how I got it to work:
HTML
<input ng-model="inboxEditTitle" />
JS
$scope.filteredArray = [];
$scope.$watch('tasks',function(){
$scope.filteredArray = filterFilter($scope.tasks, {type:0});
$scope.inboxEditTitle = $scope.filteredArray[0].title;
},true); // the 'true' keyword is the kicker
Setting the third argument of $watch to true means that any changes to any data in my tasks array triggers the watch function. This is what's known as an equality watch, which is apparently more computationally intensive, but is what I need.
This SO question and answer has helpful commentary on a similar problem, and a great fiddle as well.
More on different $watch functionality in Angular
To update inboxTasks, you could use $watchCollection:
$scope.inboxTasks = [];
$scope.$watchCollection('tasks', function(newTasks, oldTasks)
{
for (i=0; i<newTasks.length(); i++)
{
if(newTasks[i].type == 0)
{
$scope.inboxTasks.append(tasks[i]);
}
}
});

Angular Checking What Filters Return

I have a list with ng-repeat and a filter , the filter filters based on an html input . If the filter produces an empty list , I want to show the user some informative message . I can't figure out how...
Search the list: <input type="text" ng-model="searchText">
<div ng-controller="my-ctrl">
<ul id="list">
<li ng-repeat="element in myElements | filter:searchText" >
{{element.data }}
</li>
</ul>
***<div>{{myElements.length}}</div>*** //length stays
// the same no matter what the filter is returning...
<div ng-show="myElements.length">My Informative Msg</div>
What I tried doing was showing my informative msg based on myElements.length , I thought that the filter will update the value of myElements in my scope . However , what I find as I search for items (e.g reducing or enlarging the list) is that myElements.length stays the same .
Is there some elegant solution to know when the filter is empty , or more generally to know what the filter is returning ?
There is a neat trick for that. Instead of filtering the real list, create a new filtered list "in the fly" which will contain only the filtered items or none. If none, show the message.
app.controller('MainCtrl', function($scope) {
$scope.items = ["Foo", "Bar", "Baz"];
});
And:
<input ng-model="searchText" />
<ul>
<li ng-repeat="item in filteredItems = (items | filter: searchText)">
{{ item }}
</li>
</ul>
<div ng-hide="filteredItems.length">There is no item matching</div>
So if your filter doesn't match any item, you will see the message.
Plunker here: http://plnkr.co/edit/3os7gIxSjlyDO1CGKcFT?p=preview
I think you should create your own filter which will do that.
Here it is: http://jsfiddle.net/QXSR8/
HTML
<div>
{{answer | empty}}
</div>
JS
angular.module('project', []).
filter('empty', function() {
return function(input) {
if (!input || !input.length){
return "Array is empty";
} else {
return input;
}
}
});

Angular factory filter - Can't get data passed to the filter

I'm looking through the Angular docs and I can't better documented stuff.
My problem is as follows:
I have a filter:
.filter('filteringService', function () {
return function (photos, categsList) {
if (photos !== undefined) {
var filteredPosts = [];
console.log('Categories or Selected Categories:');
console.log(categsList);
//DEVEL Only, assign a SEARCH value, can't pass the data from the list now.
categsList = 'people';
if (categsList === undefined) {
for(i=0; i < photos.length; i++) {
filteredPosts.push(photos[i]);
}
} else {
console.log('Trying to push a category slug to search for.');
//TASK: Convert in multiple possible selections.
filteredPosts = [];
}
console.log('Filter returns ' + filteredPosts.length + ' posts');
return filteredPosts;
}
};
});
And I have the template
<div class="photos">
<div class="filters">
<ul>
<li><a>ALL</a></li>
<li ng-repeat="category in categsList">
<a ng-checked="category[0]" ng-model="category[0]">{{ category[1] }}</a>
</li>
</ul>
</div>
<ul class="photos-list">
<li ng-repeat="photo in photos|filteringService:category">
<h1>{{ photo.title }} click LINK</h1>
<ul class="categories">
<li ng-repeat="category in photo.categories">
{{ category.title }}
</li>
</ul>
</li>
</ul>
</div>
There's a huge object with posts called photos and then there's a category list called categsList.
The photos object has the items from the categs list in it. I WANT to be able to filter with the CLICKED element through that list, and maybe multiple filter, but first to pass on the actual filter value to the filter service, I can't seem to do that.
How should I do that?
Apparently I managed to pass the filter value, in a dirty way (I GUESS), like this
<a ng-bind="category.slug" ng-click="returnFilter(category.slug);" ng-model="category.slug">{{ category.title }}</a>
it goes here
$scope.returnFilter = function(theSlug) {
$scope.filterBy = theSlug;
};
and it comes out here
<li ng-repeat="photo in photos|filteringService:filterBy">
It's working, but Is this correct?
EDIT: Also with this way in mind, I could pass an array as theSlug so I can do multiple filtering, and when clicking two times on the same item it would get it out of the array. hmmm
EDIT 2:
Let's say the resulting array is under 15 items, could do I run some action in the controller knowing this?
Actually the other way around, could I query from the controller the resulting array that the filter outputs?
I can't $watch the first array, I guess the filter creates a new array and puts those results in page. How could I watch the resulting array for changes and do stuff in the controller?

simple Flickr application with Knockout.js doesn't work (JSBin prepared)

Now I'm writing a simple application which get five photos from Flickr and display the titles as list. At first I tried #current_photos and this works good, but when I use Knockout.js(#currentPhotos), this doesn't work.
root = exports ? this
class root.Flickr
constructor: ->
#photos = []
$.getJSON(
'http://www.flickr.com/services/rest/?jsoncallback=?'
format : 'json'
method : 'flickr.photos.search'
api_key : '7965a8bc5a2a88908e8321f3f56c80ea'
user_id : '29242822#N00'
per_page : 5
).done((data) =>
$.each data.photos.photo, (i, item) =>
#photos.push item
)
root = exports ? this
class root.PhotoListViewModel
index = null
currentPhotos = []
constructor: ->
flickr = new Flickr
# #current_photos = flickr.photos ###### WORKS GOOD
flickr.photos = ko.observableArray []
#currentPhotos = ko.computed ->
flickr.photos
HTML is as below:
<body>
<h4>Photo List</h4>
<ul data-bind="foreach: currentPhotos">
<li>
title: <span data-bind="text: title"> </span>
</li>
</ul>
</body>
I created JSBin page as below:
http://jsbin.com/avazak/7/
Thanks for your kindness.
Because of how the dependency tracking works in computed observable:
While your evaluator function is running, KO keeps a log of any observables (or computed observables) that your evaluator reads the value of.
So you need to call your observables inside the computed it is not enough the reference them:
#currentPhotos = ko.computed ->
flickr.photos()
And I would suggets that you declare the #photos as ko.observableArray inside the Flickr object not in the PhotoListViewModel
Demo JSFiddle.

Categories