AngularUI-Bootstrap Typeahead: Grouping results - javascript

I am implementing typeahead using AngularUI-Bootstrap. I need to show the results grouped based on some values coming from the database. Here's a sample scenario
There are some users in the database, each user has a "Department". One user name can be available in multiple departments.
The end-user types in the names to search users from the database and retrieves the list in the typeahead list. Since one user name can belong to multiple departments, the requirement is to show the user names grouped by different departments. Something like this:
Then the user can select the desired user name and proceed.
As per the Typeahead documentation present here, I don't see any option to cater to my requirement.
I have tried the this workaround: Whenever the typeahead array is getting formed, I appended the user department to the array element:
$scope.fetchUsers = function(val) {
console.log("Entered fetchUsers function");
return $http.get("http://localhost:8080/TestWeb/users", {
params : {
username : val
}
}).then(function(res) {
console.log("Response:",res);
var users = [];
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
console.log("users=",users);
return users;
});
};
This way, at least the end user sees the department. But when I select the record, the selected value is the full content of the array element. Below is sample screenshot to elaborate:
HTML
Users from local service
<pre>Model: {{userList | json}}</pre>
<input type="text" ng-model="userList" placeholder="Users loaded from local database"
typeahead="username for username in fetchUsers($viewValue)"
typeahead-loading="loadingUsers" class="form-control">
<i ng-show="loadingUsers" class="glyphicon glyphicon-refresh"></i>
User types in the string
User selects one record
I want to avoid the department (in this case, string - Desc 4 ) when user selects a record.
Is there any way I can achieve this grouping without any workaround? Or is there any way I can enhance my workaround?

I used to have a similar requirement and here is how I did it that time.
Example Plunker: http://plnkr.co/edit/zujdouvB4bz7tFX8HaNu?p=preview
The trick is to set the typeahead-template-url to a custom item template:
<input type="text" class="form-control" placeholder="Users loaded from local database"
ng-model="selectedUser"
typeahead="user as user.name for user in getUsers($viewValue)"
typeahead-template-url="typeahead-item.html" />
The item template, this represent each item in a dropdown:
<div class="typeahead-group-header" ng-if="match.model.firstInGroup">Desc {{match.model.group}}</div>
<a>
<span ng-bind-html="match.label | typeaheadHighlight:query"></span>
</a>
As you can see, there is an ng-if to show a group header if that item has a property firstInGroup set to true.
The firstInGroup properties are populated like this using lodashjs:
$scope.getUsers = function (search) {
var filtered = filterFilter(users, search);
var results = _(filtered)
.groupBy('group')
.map(function (g) {
g[0].firstInGroup = true; // the first item in each group
return g;
})
.flatten()
.value();
return results;
}
Hope this fit to your requirement too.

please see here http://plnkr.co/edit/DmoEWzAUHGEXuHILLPBp?p=preview
instead of creating new objects here:
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
use create template :
<script type="text/ng-template" id="customTemplate.html">
<a> {{ match.model.name}} - department : {{match.model.dept}}</a>
</script>
and use it in your Typeahead directive
<input type="text" ng-model="selected"
typeahead="user.name as user for user in users | filter:$viewValue | limitTo:8" class="form-control"
typeahead-template-url="customTemplate.html">

Related

Angular dynamic checkbox Filtering implementation

thanks in advance
my Requirement is to make a custom filter with name wise search(done) and checkboxes which filters a Table's Rows(array of objects) by matching the checkbox value with the Row['tags'] (array of strings) and returns row if the tags array consist of value in a checkbox ,
The problem is that the filters(checkbox) is obtained from DB and Dynamically populated thus I cannot use ngmodel
Any implementation ideas are highly appreciated, I've seen a lot of questions with static filters and some filters using pipes but how to handle the dynamic case
so far my implementation,
Template:
<div id="searchByTag" *ngFor="let tag of tagList">
<input
type="checkbox"
(change)="filterByTags(tag, $event)"
/>{{ tag }}
</div>
Ts:
rows=[{},{}] //from db
temp = rows // copied when getting row from db
filterByTags(FilterTag, event) {
if (event.target.checked) {
const filteredRow = this.rows.filter((obj) => {
return tag.includes(FilterTag.toLowerCase());
});
this.rows = filteredRow;
} else {
return (this.rows = this.temp);
}
}
a Row object:
{
"xx":'yyy',
....,
"tags" : [
"org",
"pcb",
]
}
other problem is that the filtering technique currently returns only one row which matches the condition (cleared), but the main thing is the dynamic implementation of tags
you can have ngModel:
if this is your checkboxes = ["org", "pcb"];
then all you need is a record to bind checkboxes values to it:
checkboxes: {[id: string]: {value: any}} = {};
for(let tag of this.tags) {
this.checkboxes[tag] = {value: false}
}
now in your template:
<input type="checkbox" *ngFor="let item of tags"
[(ngModel)]="checkboxes[item].value">
you can see this in this stackblitz:
stackblitz

how to set the updated value in searched field by default after updation in angularjs ui-select directive?

I am facing problem in angularjs ui-select directive. It works fine, it shows me full data in dropdown. When I select a value from search field and want to update it the it is updated but it can't show by default in the search field. I have to manually search again to see the updated value. Let me paste the code...
Here is the angularjs code
$scope.consignee = [];
$http.get("get-consignee", {
}).then(function(response){
$scope.consignee = response.data;
//$scope.consignee.selected = $scope.consignee[0];
});
Here is the ui-select code
<ui-select ng-model="consignee.selected" theme="select2">
<ui-select-match placeholder="Select Consignee">
<% $select.selected.CONSIGNEE_NAME %>
</ui-select-match>
<ui-select-choices ng-repeat="e in consignee | filter: $select.search">
<div><% e.CONSIGNEE_NAME %></div>
</ui-select-choices>
</ui-select>
Let say I have 5 consignee names in dropdown like!
1. hamad
2. test2
3. yasin Gul
4. hamid
5. munir
So the problem is when I use this $scope.consignee.selected = $scope.consignee[0]; then at 0 index it gives me hamad name after updation even if I update test2 or yasin Gul it gives me hamad by default set in search field. I know I have 0,1,2,3,4 indexes but I want it dynamic not manual. And I want to set only that name which I update. If I update test2 so that it should give me test2 by default set in searched field after updation and same for yasin Gul etc.. Any help would be appreciated Thanks
cons_id is your selected database id.
$scope.consignee = [];
$http.get("get-consignee", {
}).then(function(response){
$scope.consignee = response.data;
var index = $scope.consignee.findIndex(x => x.CONSIGNEE_ID==cons_id); // use this
$scope.consignee.selected = $scope.consignee[index];
});

ngFor inputs copy values of eachother

I'm using Angular5 and would like to create a form where user can insert new records with a button. Each record has many controls in it and should be changed independently from other records. I have done this many times but now I'm getting weird results.
<form #newRequestForm="ngForm">
<a class="list-group-item" *ngFor="let detail of currentRequest.details; let index$ = index">
.
.
.
<ng-select [items]="products" [searchFn]="searchProduct" (change)="productChanged($event, detail)">
<ng-template ng-label-tmp let-item="item">
{{item.code}} - {{item.name1}}
</ng-template>
</ng-select>
<input class="form-control" name="productname1" type="text" [ngModel]="detail.product.name1" />
<input class="form-control" name="productname2" type="text" [ngModel]="detail.product.name2" />
<input class="form-control" name="productname3" type="text" [ngModel]="detail.product.name3" />
<input class="form-control" type="text" name="description" [(ngModel)]="detail.description" />
The problem is that the method called by ng-select onchange productChanged sets the product names of the current selected product. (Products has 3 seperate name fields in erp.) And when this happens all 3 name fields of all records in the form changes to the name fields of the currently selected product. No matter which ng-select I use, all has changed. All the other fields working seperately eg: description.
So I suppose the bug is in the method but it looks like this:
productChanged($event, detail) {
detail.product = $event;
console.log('-----------------------------------------');
this.currentRequest.details.forEach((d, i) => {
console.log(i, d.product !== null ? d.product.name1 : '');
});
}
Yes, I have tried to debug with the good old console.log and it says that the content of each of the detail.products are perfect, according to the last selection of that record's ng-select.
The input controls are still rewritten by whatever select I make. Why?
Because you didn't specify a track by function, so Angular doesn't really know how to keep track of your inputs.
<a class="list-group-item" *ngFor="let detail of currentRequest.details; let index$ = index; trackBy: customTB">
customTB(index, item) { return index + '-' + item.product.name1; }

collect filtered ng-repeat data to use in controller

I am trying to build a picture library in a responsive grid with data coming down from a Mongo DB. Data from db is in this form. name, image, keyword, bio, reference", searchable,
I start with an ng-repeat to display category selection checkboxes based on the picture keywords. This seems to work fine.
p CATEGORIES:
span.formgroup(data-ng-repeat='category in mongoController.catData')
input.checks( type='checkbox', ng-model='mongoController.filter[category]')
| {{category}}
Here is the factory that sorts and identifies the keyword checkboxes:
getCategories: function (cat) {
return (cat || []).map(function (pic) {
return pic.keyword;
}).filter(function (pic, idx, array) {
return array.indexOf(pic) === idx;
});
}
From there I filter an ng-repeat to display pictures based on the checkbox selection, and/or a search field which works well also.
input.form-control.input-med(placeholder="Search" type='text', ng-model="search.searchable")
br
div.light.imageItem(data-ng-repeat="picture in filtered=( mongoController.allData | filter:search | filter:mongoController.filterByCategory)")
a(data-toggle="modal", data-target="#myModal", data-backdrop="static", ng-click='mongoController.selectPicture(picture)')
img( ng-src='../images/{{picture.image}}_tn.jpg', alt='Photo of {{picture.image}}')
p Number of results: {{filtered.length}}
Functions to display picture lists:
//Returns pictures of checkboxes || calls the noFilter function to show all
mongoController.filterByCategory = function (picture) {
return mongoController.filter[picture.keyword] || noFilter(mongoController.filter);
};
function noFilter(filterObj) {
for (var key in filterObj) {
if (filterObj[key]) {
return false;
}
}
return true;
}
If you click one of the pictures a modal box pops up with all of the input fields where you can edit image specific fields.
The part I am really struggling with is how to gather the usable data from the filtered ng-repeat to use in the controller so when the modal box is up I can scroll right or left through the other pictures that met the criteria of the search.
Any suggestions would help, even why the hell are you doing it this way?
When you declare the ng-repeat that filters the pictures, your filtered variable belongs to the current $scope (which I cannot infer from the question, as it stands). You could associate the filtered variable to a specific controller by using Controller As syntax: (i.e. using <elem ng-repeat="picture in ctrl.filtered = (ctrl.data | filter1 | filterN)"/>)
Example: (also in jsfiddle)
var mod = angular.module('myapp', []);
mod.controller('ctrl', function() {
var vm = this;
vm.data = ['alice', 'bob', 'carol', 'david', 'ema'];
vm.onbutton = function() {
// access here the filtered data that mets the criteria of the search.
console.log(vm.filtered);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="ctrl as vm">
<input ng-model="search" type="text" />
<p data-ng-repeat="item in vm.filtered = (vm.data | filter:search)">
{{item}}
</p>
<p>Items: {{vm.filtered.length}}</p>
<button ng-click="vm.onbutton()">Show me in console</button>
</div>

angularJs - Is it possible for 2 different models of different structures to sync or share states?

I have a list of checkboxes and values I"m loading from a list which comes back from the database.
Controller
listA = ['item1','item2'...'itemn']; //Master list of items
$scope.selectedItems = ["item1",... "item5"]; //selected items
$scope.attributesModel = [ //new model based on selected items
{"index":5,"attribute":"item1"},
{"index":10, "attribute":"item2"},
{"index":13, "attribute":"item3"},
{"index":21, "attribute":"item4"},
{"index":24, "attribute":"item5"}
];
View part 1
<td>
<div class="checkbox checkbox-notext">
<input checklist-model="selectedItems" checklist-value="key" type="checkbox" id="{{key}}" ng-disabled="exceededLimit && !checked" />
</div>
</td>
<td>
<label for="{{key}}">{{key}}{{$index}}</label>
</td>
view part 2
<div ng-repeat="(index, row) in attributesModel" >
<div class="margin10">
<div>Index<input ng-model="row.index" value="row.index" type="number" class="indexInputs"></input>{{row.attribute}}</div>
</div>
</div>
Now I would like to sync $scope.selectedItems and $scope.attributesModel. When a checkbox is deselected, both selectedItems and attributesModel models remove that item, and vice versa. So every time someone checks a new checkbox they are presented a attributesModel with an empty text field to type the index value.
catch The index key is null initially for every newly selected item that is added to attributesModel. The user must enter a new index # once the new item is created.
I've tried using watch but the problem I run into is when a new item is selected, I don't have access to the item itself. I only have access to the list without any idea whether the new item is X or if the item removed is Y in order to push/delete the right item.
So this might be a watch solution that I'm missing.
Let me know if I can clarify anything.
I am not sure what the problem is, but you could use ngChange on the checkboxes:
<input type="checkbox" ... ng-change="..." />
I asdume you have a checklist directive or something, so should do something there, but (since you don't share it with us) I can't tell what exactly :)
UPDATE:
Since the checklist directive is an external dependency, you could handle the ng-chage in your code:
<input type="checkbox" ... ng-change="changed(key)" />
/* In the controller: */
...
$scope.changed = function (key) {
if ($scope.selectedItems.indexOf(key) === -1) {
// The checkbox for `key` was unchecked...
} else {
// The checkbox for `key` was checked...
}
};

Categories

Resources