I am following a todo list angular tutorial. Right now, I would like to loop through all of my todo list and clear all of the items where the attribute done is true.
Right now in my index.html file I have a button with the ng-click attribute of "clearCompleted()". That function looks like this in my js file:
$scope.clearCompleted = function () {
$scope.todos = $filter($scope.todos, function(todo){
return !todo.done;
});
};
What is wrong with this function because it is not clearing the todo items that are being set to done:true.
And in more of a general question, what is the typical way I could say something along the lines of "select all the items where the attribute done is true" because I am more used to ruby and not javascript.
$filter(name)gets the filter with that name. In order to actually call it you can write `$filter(name)(/arguments for the filter/)
There is a predefined filter called filter that can be used for filtering arrays. You pass in an object for comparisons:
$scope.clearCompleted = function () {
$scope.todos = $filter("filter")($scope.todos, {done:false});
};
Which returns all items that have the property donewith the value false.
Try this syntax:
$scope.todos = $filter("filter")($scope.todos, function(todo){
Example:
http://jsfiddle.net/cherniv/89Qqs/
And don't forget to inject the $filter service into controller first!
Related
I'm paginating an Angular table and want to display all the page numbers beneath the table.
I'm planning to create an array of the page numbers and then use ng-repeat to display them all:
HTML
<tr ng-repeat-start="item in c.filteredList = (c.data | dynamicFilter:c.filter | orderBy:c.sortOrder.order:c.sortOrder.reverse)">
JS
this.checkPage = function(){
this.pageNumArr = [];
for(i=0; i<this.filteredList.length/this.perPage; i++){
this.pageNumArr.push(i);
}
}
Where this.perPage is the number of items per page (set by the user).
What I can't figure out is how to trigger checkPage() whenever the filter changes.
You would be best binding your page number ng-repeat to a function that creates and returns the array of page numbers. This will create a watcher for the function and keep the array of page numbers up to date.
There will be no need to manually create a $watch in your controller.
this.pageNumbers= function(){
var pageNumArr = [];
for(i=0; i<this.filteredList.length/this.perPage; i++){
pageNumArr.push(i);
}
return pageNumArr
}
<span ng-repeat="page in c.pageNumbers()">{{page}}</span>
I think that triggering events inside a filters shouldn't be considered a best practice, probably, you need to find another approach.
By the way, there are many way:
If you can edit that filter, simply, pass the $scope reference to it and trigger the event via $scope.emit or $scope.broadcast: <li ng-repeat="item in items | myFilter:[param1, param2, paramN]"></li>
Angular supports filter inside a controller, so, probably this should be a better solution https://toddmotto.com/everything-about-custom-filters-in-angular-js/ (have a look at Filter 4: Controller/$scope filter);
Register a watcher on your model, but, this is bad for performances...
You can watch for the filteredList and call the checkPage() there:
var self = this;
$scope.$watch(
function() {
return self.filteredList.length;
},
function(newValue, oldValue) {
if (newValue !== oldValue) {
self.checkPage();
}
}
);
Probably an easy question, but I am making a call to an API that returns a full list of products in JSON. The products are listed under 4 categories in the JSON - 'connectivity','cables','routers' and 'servers'. Using the getProducts() function below, I assign the list of 'connectivity' products to a variable called $scope.connectivitylistOfProducts - and this is what I display to the user in the UI as a default.
productsServices.getProducts()
.then(function (allProducts) {
$scope.listOfProducts = allProducts.data.category[0];
$scope.connectivitylistOfProducts = allProducts.data.category[0].connectivity;
})
.finally(function () {
});
In the UI, I have a select box that's contains a list of the categories where the user can change to view the products under the category they choose. changeProduct() is what is called to change the category
$scope.changeProduct = function () {
// change the product
};
I am already loading the full list of categories and products into $scope.listOfProducts and I dont want to make another API call by calling getProducts again. I'm not sure how to set up another variable (for example $scope.routerslistOfProducts) and assing the correct products to it. Could anyone tell me the best way to handle this? Many thanks
Could you try to access lists by array notation:
you have:
$scope.listOfProducts = allProducts.data.category[0];
you could create a category variable:
$scope.category = 'connectivity';
and to access using, for example:
<div ng-repeat="product in listOfProducts[category]">
If your payload has all the arrays then on API call back assign to scope variable say $scope.mylist, now you bind $scope.mylist.categories to drop down and on drop down change send category Id to change function () and filter using for loop and equal operator , while running through loop I.e filtering , which ever product matches category push to a variable say $scope.filteredproducts ....and here you got your filtered products.
This is simple way to understand , there are better ways to maintain filtered arrays too.
I have a simple action that can be attached to list items in an {{#each}} loop, and when that action is triggered, it will link to that instance of the model.
This is what it looks like now
VpcYeoman.SuperTableController = Ember.ArrayController.extend({
actions: {
goTo: function(input) {
this.transitionToRoute('someModel', input);
}
}
});
The action is called on an HTML element like this
{{action 'goTo' this bubbles=false}}
You can see the problem with this in that 'goTo' cannot be reused on other models because it is specifically looking at the 'someModel' model.
Please help me make this action work for whatever the current model is
I tried replacing 'someModel' with a generic 'model' & even 'this.model' but they didn't work.
Do not reply with 'use {{#link-to}}' please. I am aware that this exists and
Before you read this, you should know that I do recommend you use the link-to helper. I normally pass a computed property to the helper when I need it to change based on the model...
I am not sure where you have that action in your code, but you could just compute that path as needed. For example, take this item controller:
App.ItemController = Ember.ObjectController.extend({
getTransitionPath: function () {
return this.get('foo') + '_bar';
},
transitionPath: function () {
return this.get('foo') + '_bar';
}.property('foo'),
actions: {
goTo: function(input) {
//this.transitionToRoute(this.getTransitionPath(), input); // Regular method
this.transitionToRoute(this.get('transitionPath'), input); // Computed property
}
}
});
I also don't know what kind of logic you are looking for inside of those methods, but this pattern should work on a per model basis.
Good luck!
This is a follow up to: accessing another models data in ember.js
I have a situation where I would like to filter a list with a chosen multi-select box. When sending the data, this.store.find('tag') always returns a DS.PromiseArray. The Ember.Select seems to handle this fine, but the chosen multi select doesn't seem to like it. I have seen something like this:
this.store.find('tag').then(function(items) {
return items.map(function(item){
return [item.get('id'), item.get('name')]
})
})
but I always seem to get a typeerror{} on the map function...
Here is a jsfiddle that outlines the problem:
http://jsfiddle.net/viciousfish/TEZjW/
Bonus Points! demo shows the chosen select as a single select (for clarity). I would like to use this as a multi select, which can be set by setting multiple: true in App.MultipleSelect
Update here is another jsfiddle with what I think should work, but doesn't seem to! http://jsfiddle.net/viciousfish/FZ6yw/1/
And even further, this fiddle shows that the .then should work to deconstruct the promiseArray http://jsfiddle.net/marciojunior/DGT5L/
Here is a functional jsfiddle http://jsfiddle.net/FZ6yw/2/
I moved the promise code to the afterModel hook of the route, as is promise friendly https://gist.github.com/machty/5723945
I also changed the map function, you were returning arrays and in my code it returns objects
App.AssetsRoute = Ember.Route.extend({
model: function () {
return this.store.find('asset');
},
afterModel: function () {
var self = this;
return this.store.find('tag').then(function(items) {
var allTags = items.map(function(item){
return {id:item.get('id'), name:item.get('name')};
});
self.controllerFor('tags').set('content', allTags);
})
},
});
I am using the click event on a button to set the value of an item that was generated using a foreach.
<table>
<tbody data-bind="foreach: Employees">
<a data-bind="click:$parent.delete()">
..
in my delete function I am setting the value but it doesn't update the screen
Delete :(emp) {
emp.active=false;
}
When I create I am setting all the individual properties as observable but seems like they are not when in the foreach loop.
Update
Employees is filtered.computed
var Employees=ko.computed(function() {
return ko.utils.arrayFilter(AllEmployees(), function (empid) {
return empid.ID == filter();
});
When you get/set observables you need to call them like this:
var val = obj.prop(); //Getter
obj.prop(false); //Setter
One other issue you have is that you are using parenthesis in your click binding. Remember that Knockout bindings are just javascript, so it will actually execute that expression when it binds.
You need to get rid of those parenthesis or emp will be undefined initially.
UPDATE:
I've updated this jsFiddle to include three filtered lists similar to what you have shown above. You can see that using a filtered list via a computed has no bearing on how knockout handles the bindings, and the UI updates seamlessly.
http://jsfiddle.net/jwcarroll/ceRPK/
To set an observable, you have to call it (since observables are implemented as functions):
emp.active(false);
Your method simply overwrites the observable.
Knockout subscribes to the observable array, but not to each observable within that array. If you want to subscribe to individual properties you need to subscribe manually by using myObservable.subscribe()
Knockout subscribes to the observable array, but not to each observable within that array. If you want to subscribe to individual properties you need to subscribe manually using myObservable.subscribe()
Edit
If you are trying to have your computed keep track of what should be in your computed you can do so like this -
var allEmployees = ko.observableArray([my data goes here]);
var Employees=ko.computed(function() {
return ko.utils.arrayFilter(allEmployees(), function (emp) {
return emp.active === true;
});
});
That works if active is not an observable property of each allEmployees(). If it is an observable just change that to -
var allEmployees = ko.observableArray([my data goes here]);
var Employees=ko.computed(function() {
return ko.utils.arrayFilter(allEmployees(), function (emp) {
return emp.active();
});
});