Weird bug: AngularJS can search single fields but not multiple fields - angularjs

I am writing an app using Angular 1.3. I want to search through a table by any word/field; one search box for everything.
Currently I am able to get my search box to search through a specific field fine by doing:
<label>Search: <input ng-model="searchKeyword.title"></label>
or
<label>Search: <input ng-model="searchKeyword.author"></label>
Those work fine. However, when I try to search all the fields at the same time by doing:
<label>Search: <input type = "text" ng-model="searchKeyword"></label>
It doesn't work at all.
This is my code on the table:
<label>Search: <input ng-model="searchKeyword"></label>
<table ng-repeat="post in posts | orderBy: sort | filter: searchKeyword">
<tr>
<td> {{post.title}} </td>
<td> {{post.author}} </td>
<td> {{post.content}} </td>
<td> {{post.date | date: "d/M/yyyy"}} </td>
</tr>
</table>
And this is inside my main controller:
$scope.posts = posts.posts;
$scope.searchKeyword = "";
Please tell me what exactly is causing this weird bug. Thank you.

Its because ng-repeat create a new scope and cannot write in to a primitive property of its parent scope.but can write in to an object of its parent scope.
A scope (prototypically) inherits properties from its parent scope.
this post explains it well.What you want here is a custom filter check the ng-modal against each property in your object.
. filter('customFilter', function() {
return function(product,query) {
var out = [];
angular.forEach(product,function(value,key){
if(value['author'].indexOf(query)>-1 || value['title'].indexOf(query)>-1)
{
out.push(value)
}
})
return out;
}
})
Here is a working example

i found this post very late. Anyway my suggestion may helpful to other viewer.
There is a solution for weird bug in weird way.
I found this post is very useful for use of multiple filters in controller with example of big data table search.
(after reading the above linked post)
But you dont need to include multiple watcher and multiple input fields in every column of table, for your scenario you will just need the below code change after static json declaration.
$scope.$watch('searchKeyword', function(val) {
$scope.items = $filter('filter')($scope.items2, val); });
considering searchKeyword is the ng-model of input field to search.

Related

ng-repeat with filter for HTML table

I am using Angular doing a minimal search function where I used filter and ng-repeat to print a table on HTML page.
I am trying to write a custom filter that helps me implement filter data by keywords input.
<div>
<label>Filter</label>
<input ng-model="filterText" type="text" placeholder="Keywords"><br>
<p><b>Filter by: </b>{{filterText}}</p>
</div>
<div>
<tr ng-repeat="x in data | myFilter: filterText">
<td>{{x.Key.Food}}</td>
<td>{{x.Key.Color}}</td>
</tr>
</div>
And I have a script code that customs myFilter
app.filter("myFilter",function(){
return function(input,filterText){
if(input["Key"]["Food"]==filterText){
return input;
}
}
})
Here is sample of JSON file that I want to apply on this code
[
{Key:{Food:"Apple",Color:"Red"},Value:{Amount:"1"}},
{Key:{Food:"Orange",Color:"Orange"},Value:{Amount:"2"}}
]
The problem I am facing is that I expect that by calling x in data x will be an object from JSON sample. And when I access it; I could access x.Key.Food etc.
However, it didn't work out because the key-pair is not defined in javascript not like Foreach in Java.
The right way to use custom filters is {{ expression | filter }}.
You can use built-in filter for this.
<tr ng-repeat="x in data | filter : { Key: { Food: filterText }} : true">
<td>{{x.Key.Food}}</td>
<td>{{x.Key.Color}}</td>
</tr>

How to add a new record and not update previous record in angularjs

How do I add a new record and have angular "forget" the new item added after I click the corresponding button.
The ability to add a record works and when I add the new record it display in the table I have, correctly, However my problem is that when I go to add a second record ie I remove the previous input and type something else. This results in the newly added record below to also change.
In short after I add an item to my array I want angular to forget it. How do I accomplish this.
In my controller I have this
(function () {
var app = angular.module("mainApp");
var ordersController = function ($scope,$filter, ordersService,customerService) {
$scope.orders = [];
$scope.addOrder = function (newOrder) {
$scope.orders.push(newOrder);
}
app.controller("ordersController", ["$scope","$filter", "ordersService","customerService", ordersController]);
}());
in my html I have this
<div><table>
<tr>
<td><input ng-model="item.quantity" type="text" /></td>
<td><button type="button" ng-click="addOrder(item)">Add Line Item</button></td>
</tr>
</table></div>
<div>
<table>
<tr ng-repeat="order in orders track by $index">
<td>
{{ order.quantity }}
</td>
</tr>
</table>
</div>
The problem might be that you are adding the newOrder object to the array while Angular keeps it's model bound to that object. Try using Angular's copy functionality like this (assuming that you are using Angular 1):
$scope.addOrder = function (newOrder) {
var copiedOrder = angular.copy(newOrder);
$scope.orders.push(copiedOrder);
}
The documentation for the copy method can be found here.

How to disable/enable sorting while editing in Angularjs

I have a editTable like this:
<tr ng-repeat='guest in guestList | orderBy:orderByField:reverseSort'>
<td><span ng-show='!guest.isedit'>{{guest.firstname}}</span><span ng-show='guest.isedit'><input type="text" ng-model='guest.firstname'/></span></td>
<td><span ng-show='!guest.isedit'>mode_edit</i></span><span ng-show='guest.isedit'><button ng-click='contactUpdate(guest)'>OK</button></span></td>
</tr>
in my controller:
$scope.editGuest = function(guest){
delete $scope.orderByField;
guest.isedit = true;
};
$scope.contactUpdate = function(guest){
//Save the change then put the order back to re-order the table
$scope.orderByField = 'firstname';
}
As you can see, this is a editable table, if I click edit, the table becomes editable. I want to disable the sort when editing, until user finish editing and hit the OK button(Which already saved in the server), then I will re-order with new data. The problem is for the first time I fire editGuest(guest),it will still jump. Is there any way I can achieve this?
You need to have a different ng-model on your input box like this :
<tr ng-repeat='guest in guestList | orderBy:orderByField:reverseSort'>
<td>
<span ng-show='!guest.isedit'>{{guest.firstname}}</span>
<span ng-show='guest.isedit'><input type="text" value="{{ guest.firstname }}" ng-model='firstname'/></span>//I have set the value to the guest's firstname to default the name when the input box becomes active. You can also use ng-init to achieve it.
</td>
<td>
<span ng-show='!guest.isedit'>mode_edit</i></span>
<span ng-show='guest.isedit'><button ng-click='contactUpdate(firstname)'>OK</button></span></td>
</tr>
In your Controller :
$scope.contactUpdate(guest, name){
guest.firstname = name;
name = '';//EDIT
}
You need to remove binding first for this.
Create clone of guestList object and do the editing operation on it.
once it is done assign the clone object back to guestList.
e.g.
var dummyGuestList = angular.copy(guestList)
//some operation on dummyGuestList ;
guestList = dummyGuestList //assign bacl again

Prevent angular from sorting list item alphabetically in real time

I am trying to fix a bug with an angularjs list.
When an item in the list is edited, the record is sorted alphetically in real time and it jumps to a new position in the list.
This is proving problematic as when someone tries to edit a record, "ABC" for example, if they put a "Z" in front of it it jumps to the last page on the list.
The text input value is bound directly to the model, which I suspect is the issue, as the data-ng-repeat directive ensures that the data is sorted alphabetically.
As such I tried changing the data-ng-model binding to data-ng-bind, as it is my understanding that if the input value is changed, ng-model updates the model while ng-bind doesn't.
I would greatly appreciate any advice.
HTML:
<form name="companies" class="clearfix" novalidate data-ng- hide="loadingIcon">
<table>
<tbody>
<tr data-ng-repeat="company in data | orderBy: 'NAME' | siftPage: startIndex: endIndex">
<td width="60%">
<span data-ng-hide="edit"> {{ company.NAME }} </span>
<div class="form-field col-sm-9" data-ng-show="edit">
<input type="text" data-ng-bind="company.NAME" />
</div>
</td>
<td>
<a class="icon edit" data-ng-click="edit = !edit; changed = edit ? edited(company) : changed(company);" href="#"></a>
</td>
<td>
<a class="icon delete" data-ng-click="removeCompany(company)" href="#"></a>
</td>
</tr>
</tbody>
</table>
I'm new to angular so please forgive me if I haven't included enough information. I will update update with any further information as requested.
I believe it is the ng-repeat with the orderBy: 'Name' clause that is the actual issue. It will make sure that the order is maintained even after an edit.
If you want to order initially before edits, copy the name as initialName onto each of your json objects and set the orderBy to 'initialName'. Since you wont be updating the initialName on an edit it wont update via the order by.
If you create the copy as a function to create the initialNames, you could call it through a button to re-sort the data.

AngularJS dynamically add elements to ng-repeat template

I have an array called 'variables' that I turn into a table.
<tr ng-repeat="variable in variables">
<td>
<textarea ng-model="variable.extras" ...>{{variable.extras}}</textarea>
</td>
I then have a function numberOfRowsForChoiceVariable that looks at variable.extras and determines how many lines it has, doing things like removing blank lines, etc... So now on the next <td> element I want to have as many <select> elements as returned by the call to numberOfRowsForChoiceVariable(variable).
So if they put 4 lines into variable.extras, then I want to display 4 select elements inside the next <td>
I'm thinking I need to use some type of $watch() to enable that, but I'm not sure how to use that within the ng-repeat scope.
Use Case
Please let me know if I have misunderstood your question. Here's what I understand from your question.
User enters text into the textarea
System counts the number of lines
Number of lines will create that number of select element. [edit: updated solution to meet this requirement]
Solution
You can use a filter to create a list from your line counting.
Controller
Based on your requirements, this controller uses regular expressions to count the number of lines. I haven't tested this on a Unix system, but it works fine on Windows. The lines function will return the number of lines from a given string input.
function ctrl($scope) {
$scope.variables = [{extras: ""}, {extras: ""}, {extras: ""}];
$scope.lines = function (input) {
if (angular.isString(input)) {
return input.split(/\r?\n/).length;
}
return 0;
};
}
Filter
A filter is required to create a temporary array of a given length. This is a common component from functional programming.
var app = angular.module('app', []);
app.filter('range', function () {
return function (input, total) {
total = parseInt(total);
input = Array.apply(null, Array(total))
.map(function(value, index) {
return index;
});
return input;
};
});
View
Using this view, the <select> can be dynamically created next to the textarea. I've shortened the variable names so it could fit within this screen, but don't do this, it's bad form.
<div ng-app='app' ng-controller="ctrl">
<table>
<tr ng-repeat="v in variables">
<td>
<textarea ng-model="v.extras">
{{v.extras}}
</textarea>
</td>
<td>
<select ng-repeat="n in [] | range:lines(v.extras)">
</select>
</td>
</tr>
</table>
</div>
Demo
You can access a demo from this jsFiddle.

Resources