AngularJs filter on two object fields - html

I've done filtering numerous times before, but am having trouble with this specific case. I have a list of objects set up in pagination, but I also want to be able to filter them for ease of use. An example object might look like:
{
baseline:0.75
clonedFrom:Object
description:"Just a simple description.."
includeable:false
key:"bc889881-7979-4e04-b586-d53faab26b6b"
name:"Just a simple question?"
type:"BayesianModel"
version:1483477351992
versionComment:null
versionLabel:"0.11"
}
For the most part, what the object looks like is arbitrary, I just included it for ease of answering. I am trying to filter on both the name and description of the object at the same time. For example if the user knew a key word in either they could narrow down the results.
I have the input for the filter and the results inside of a <ul> shown below:
<ul class="list-group">
<li class="list-group-item">
<div class="form-group has-feedback">
<input type="text"
ng-model="vm.resultFilter"
ng-change="vm.updateFilter()"
class="form-control filterInput"
placeholder="Filter Results" />
<span ng-if="vm.resultFilter"
ng-click="vm.resultFilter = ''; vm.updateFilter();"
class="fa fa-times fa-lg form-control-feedback">
</span>
</div>
</li>
<li class="list-group-item link-row model-list"
ng-repeat="result in vm.filteredResults
| startFrom : ((vm.currentPage - 1) * vm.itemsPerPage)
| limitTo: vm.itemsPerPage"
ng-if="result.name != vm.model.name"
ng-click="vm.selected = result;"
ng-class="{selected: result === vm.selected}"
ng-init="displayNumber = vm.incrementDisplayIndex();">
<h4 class="list-group-item-heading">{{result.description}} - {{result.name !== vm.model.name}}</h4>
<p class="list-group-item-text">
{{result.name}}
</p>
</li>
</ul>
So the name of the filter is vm.resultFilter and the cooresponding method that is called every time the filter is updated is updateFilter() (which is necessary for the pagination stuff).
function updateFilter() {
vm.filteredResults = $filter('filter')(vm.results, {
'name': vm.resultFilter,
'description': vm.resultFilter
});
vm.totalItems = vm.filteredResults.length;
vm.noOfPages = Math.ceil(vm.filteredResults.length / vm.itemsPerPage);
}
I am just having trouble with getting the filtering to work 100%. Sometimes it appears it's only filtering on the description field, but then sometimes I will search a word that is in the description field of only one result and it will filter with no results (which would be false). Any tips appreciated.

Since your are not using the filter inside your HTML, I recommend you to use the filter property from the Array prototype.
function updateFilter() {
vm.filteredResults = vm.results.filter(function(item){
return (item.name==vm.resultFilter || item.description==vm.resultFilter )
}
vm.totalItems = vm.filteredResults.length;
vm.noOfPages = Math.ceil(vm.filteredResults.length / vm.itemsPerPage);
}
In case you need to determines whether a string contains the characters of a specified string. You might need to use includes instead of ==
vm.filteredResults = vm.results.filter(function(item){
return (item.name.includes(vm.resultFilter) || item.description.includes(vm.resultFilter) )
}

Related

Angular - How to display single "no results" message on no results

I'm having trouble coming up with a way to show my "no results" div element. Basically, I have a list component containg order timeline section components, each one of these section contains order components. Like so:
My orders-list.component.html (check bottom div):
<div class="list-container" [ngClass]="{section: isDeliverySlotsActive === false}">
<label class="list-header" *ngIf="isDeliverySlotsActive === true" style="margin-top: 1.625rem">DELIVERY SLOTS ORDERS</label>
<div [ngClass]="{section: isDeliverySlotsActive === true}" *ngFor="let date of timelines">
<app-orders-list-section
[orders]="orders"
[timeline]="date"
[isDeliverySlotsActive]="isDeliverySlotsActive"
[searchTerm]="searchTerm"
></app-orders-list-section>
</div>
</div>
/* I want to show the below div when there are no results for the search */
<div id="no-results">
<img src="../../../assets/my-orders/no-results.png" alt="No Results" style="margin-top: 6.063rem; margin-bottom: 2.837rem;">
<label class="no-results-text">COULDN'T FIND ANYTHING</label>
<label class="no-results-text weight-medium">Search by order number or customer</label>
</div>
For each section, a filtering method is applied when the user searches for an order using the search bar. If the search term does not correspond to an order in a section, the order is not displayed for that section. If there are no results for that section the section header is also not displayed.
My orders-list-section.component.html:
<div *ngIf="filteredSectionOrders.length > 0">
<label
*ngIf="isDeliverySlotsActive === true"
[ngClass]="{ slots: isDeliverySlotsActive === true }">
{{ timeline | addSectionDateFormat }}
</label>
</div>
<div *ngFor="let order of filteredSectionOrders">
<app-orders-list-item
[order]="order"
[timeline]="timeline"
></app-orders-list-item>
</div>
My filter method in the section component:
filterSectionOrders(searchString: string){
if(!searchString) return;
if(this.hasNumbers(searchString)){
this.filteredSectionOrders = this.filteredSectionOrders.filter(order => order.order_num.toString().indexOf(searchString) !== -1);
}
else{
this.filteredSectionOrders = this.filteredSectionOrders.filter(order => {
if(order.first_name && order.last_name){
let fullName = order.first_name + " " + order.last_name;
if(fullName.toLowerCase().indexOf(searchString.toLowerCase()) !== -1){
return order;
}
}
})
}
}
Given that I apply this filter to each section and not to the list as a whole, how can I find out when there are 0 total results so I can show only one (not for each section) div element with a "no results found" message?
Thank you in advance.
You can easily use *ngIf;else link to ngIf from angular inside your HTML
I am not sure where do you use filteredSectionOrders, because it is not shown in your html, but let's assume your app-orders-list-section has some HTML logic where you use *ngFor to loop through orders and show it properly
so, I guess your code looks something like this
<div class="order" *ngFor="let order of filteredSectionOrders">
<img/>
<p>
{{ order.first_name + ' ' + order.last_name }}
</p>
</div>
This is simplified html how I assume it looks like.
What you can do is next:
<ng-template *ngIf="filteredSectionOrders.length > 0; else noResultsBlock">
// here you insert your code to render orders
<div class="order" *ngFor="let order of filteredSectionOrders">
<img/>
<p>
{{ order.first_name + ' ' + order.last_name }}
</p>
</div>
</ng-template>
<ng-template #noResultsBlock>
<p> No results </p>
</ng-template>
So, this would simple solution
If you want to improve it even more, it would be better to have a new variable, lets say areThereResults, which you will set to true or false, at the end of your method filterSectionOrders, based on filterSectionOrders.length. Then, you would use this new variable inside *ngIf check, instead of filterSectionOrders.length > 0.
Reason for using boolean variable instead of using actual array is detection changes, and will anguar re-render UI inside *ngIf. You can read more about it on Angular documentation, just search for detection changes.

React + Next js: Cross json values in component

first I'd like to thank you for your time trying to help. I am a designer and I suck at developing stuff, so I have no other option than to scream for help.
So this is the situation:
I was asked to add an image (country flag) that's gonna be dynamic, in an element that fetches info from a JSON file with, among others, Resorts (one of the elements being its country's long name, i.e. Australia) and Countries (by long name and shortcode, i.e. Australia, au).
I need to get the country shortcode printed in the img src, but the resort array is only containing its long name.
The code:
This is the way the JSON file presents the information:
{
"Countries":[
{"name":"Australia",
"code":"au",
"continent_code":"oc",
"slug":"australia"}],
"Continents":[
{"name":"Oceania",
"code":"oc",
"slug":"oceania"}],
"Resorts":[{
"id":"1",
"resort_name":"Resort Name",
"encoded_name":"resort-name",
...
"country":"Australia",
...}]
}
And this is my file bit:
const DesktopResort = ({resort}) => (
<Link href="/resort/[resort]" as={`/resort/${resort.encoded_name}`}>
<a target='_blank' className='resort-item'>
<div className="resort">
<div className="top">
<div className="title">{resort.resort_name}</div>
<img className="logo" src="/assets/img/resort-logo-sample.png" />
<span className="info">{`${resort.ski_network} - ${resort.region}`}</span>
// Down below is the "dynamic" file call
<img className="flag-icon" src={`/assets/img/flags/${resort.country}.svg`} />
</div>
<div className="arrow"><img src="/assets/img/arrow-link.png" /></div>
</div>
</a>
</Link>
)
I know its badly done right now, for this australian resort my image src is /assets/img/flags/Australia.svg and what I would need to print is of course /assets/img/flags/au.svg
How would you do it?
Thanks again!
I'd write a little helper function to look up a country code based on the country name.
Note: you'll need to handle what should happen if the country is not found, or the code is not there. I'm just defaulting to an empty string here.
const countryCode = name => {
const country = yourData.Countries.find(country => country.name === name);
return country && country.code || '';
};
Then use this when you're passing the src to your img.
<img
className="flag-icon"
src={`/assets/img/flags/${countryCode(resort.country)}.svg`}
/>

How do I create a filter with AngularJS to show/hide certain items?

I am trying to create a filter to show and hide certain items in my list. I already have two filters set but I can't seem to get this one working. As you can see I had some help with the others but this help is no longer with me. I am running AngularJS.
So far, I have the following:
1) To show and hide prices.
<h4 class="pl_header">Hidden Prices</h4>
<ul class="price_list_filter">
<li>
<label>
<span>Show</span>
<input type="checkbox" class="faChkSqr pull-right" ng-model="filter_hi" value="Hi" ng-change="hidden_show()">
</label>
</li>
2)Added it into my body.
<tbody ng-repeat="price_level in price_levels | orderBy:propertyName:reverse | filter:filterByCategory | filter:filterByType | filter:searchSKU | filter:filterByHide"
ng-style="{ 'background-color' : (price_level.buyer) ? 'lightgray' : 'white' }">
3) My javascript.
$scope.hidden_show = function() {
$scope.UIfilterby.hide = [];
if($scope.filter_hi == false){
$scope.UIfilterby.hide.push("Hi");
}
$scope.UIfilterby.hide = $scope.UIfilterby.hide.join();
}
and
$scope.filterByHide = function(price_level){
if($scope.UIfilterby.hide){
return ($scope.UIfilterby.hide.indexOf(price_level.hidden) !== false);
}else{
return {};
}
}
I followed similar structures to the other two filters, one was filter by category and the other as by type. Maybe I need to add it to my filters.py in django? I am new to this, any help would be appreciated. Thank you.

angular 2 validation on dynamic generated fields in loop

I have a list of input fields that are generated with a model. I am trying to add validation to them.
The requirement is they should not be empty or less than 2 characters.
problem is in the documentation only shows validation with non-dynamically generated variable Name. My fields are all generated dynamically. So there is no tempVariableName I can hardcode (otherwise they conflict), so I created temp variable from the name of the property I binded the field to. So I came up with something like this :
<div *ngFor="let field of connector.configFields">
<label>{{field.name}}</label>
<input [(ngModel)]="field.value" [type]="field.name === 'Password' ? 'password' : 'text'"
placeholder="{{field.name}} (required)"
ngControl="[fieldName+field.name]"
required minlength="2"
#fieldName+[field.name]="ngModel" />
<div *ngIf="(fieldName+[field.name]).errors && ((fieldName+[field.name]).dirty || (fieldName+[field.name]).touched)">
<span *ngIf="(fieldName+[field.name]).errors.required">Enter Name</span>
<span *ngIf="(fieldName+[field.name]).errors.minlength">Name minimum at 2 characters</span>
</div>
</div>
and the configFields in typescript look like this :
export class FieldModel {
public name: string;
public type: string;
public value: any;
}
But this simply would not work. I am new to angular 2 so I am not exactly sure what I did wrong.
You can use the unique index for each field in the array. Use this together with the name attribute (and ngModel) which will evaluate each form controls separately. So each input field gets the unique name, eg:
name="f{{i}}"
where we get {{i}} from the iteration:
<div *ngFor="let field of connector.configFields; let i = index">
So finally, your template could look like this:
<form #myForm="ngForm">
<div *ngFor="let field of connector.configFields; let i = index">
<input name="f{{i}}" [(ngModel)]="field.value" [type]="field.name === 'Password' ? 'password' : 'text'" required #f="ngModel" minlength="2"/>
<div *ngIf="f.errors && (f.dirty || f.touched)">
<div *ngIf="f.errors.required"> This field is required </div>
<div *ngIf="f.errors.minlength"> Min 2 chars </div>
</div>
</div>
</form>
Here's a live
Demo
Prepare data in model and return to angular. Angular and hard logic in the template = bad friends.
But if you have a select option and if has *ngFor for option then error message loses its mapping, due second *ngFor loop
better to define custom class for error message and use css display: none or **block*
.custom-valid-box{
display: none;
}
.form-control-danger + .custom-valid-box{
display: block;
}

Angularjs : Selecting one span tag in one div selects another span tag ng-repeat

I have list of posts that gets displayed using ng-repeat. In this I have one question and multiple answers. Answers are displayed in span tag which behaves as option button. Here is the problem, if I select one answer (option button) from one question then similar numbered answer get selected in another question.
my code for html:
<div ng-repeat="post in posts" >
<form id="pollForm" ng-submit="submitPoll()">
<span class="quest"> <strong>Poll:</strong>{{post.question}}</span><br>
<div class="post-container">
<br>
<span ng-style="{'background-image':'url('+ img1 +')'}" ng-click="chgImg(1)"
class="Pollchoice--radio">
</span>
<span class="Pollchoice--text">{{post.choice1}}</span><br><br>
<span ng-style="{'background-image':'url('+ img2 +')'}" ng-click="chgImg(2)"
class="Pollchoice--radio"></span>
<span class="Pollchoice--text">{{post.choice2}}</span><br><br>
<span ng-style="{'background-image':'url('+ img3 +')'}" ng-click="chgImg(3)"
ng-show="post.choice3" class="Pollchoice--radio"></span>
<span ng-show="post.choice3" class="Pollchoice--text">{{post.choice3}}</span><br><br>
<span ng-style="{'background-image':'url('+ img4 +')'}" ng-click="chgImg(4)"
ng-show="post.choice4" class="Pollchoice--radio"></span>
<span ng-show="post.choice4" class="Pollchoice--text">{{post.choice4}}</span><br><br>
<hr/>
<div>
<button id="btn" type="submit" class="btn btn-default">Vote</button>
<span style="margin:0 0 0 20px"> 50,000 votes</span> • <span> 23 hours left</span>
</div>
<br>
</div>
<br><br><br>
</form>
</div>
javascript:
$scope.chgImg = function(varParam){
//alert(varParam);
if(varParam === 1){
$scope.img1 = "/images/chk.svg";$scope.img2 = undefined;
$scope.img3 = undefined;$scope.img4 = undefined;
}
if(varParam === 2){
$scope.img2 = "/images/chk.svg";$scope.img1 = undefined;
$scope.img3 = undefined;$scope.img4 = undefined;
}
if(varParam === 3){
$scope.img3 = "/images/chk.svg";$scope.img1 = undefined;
$scope.img2 = undefined;$scope.img4 = undefined;
}
if(varParam === 4){
$scope.img4 = "/images/chk.svg";$scope.img1 = undefined;
$scope.img2 = undefined;$scope.img3 = undefined;
}
};
Thanks in advance.
This problem happens due to there are many HTML tags that have the same id.
so you need to differentiate between every id.
for example you will add question id & answer id in order to make it unique.
OR
in your JSON data add a new property called ImageURL and set is undefiend
and in your binding
ng-style="{'background-image':'url('+{post.ImageURL}+')'}"
and in ng-click pass the object
ng-click="chkimg(post)"
and in chkimg function set the ImageURL with the value
post.ImageURL="/images/chk.svg";
IMO there are some problems with the code, you are iterating over posts array but not using post object in the param of chgImg also your ngrepeat would print as many img1, img2 img3 as many time it would iterate and hence when you set it in scope this would be ambiguous for the scope to reflect the change in any one div, solution to this would be through some index to be associated with the img1+postID something like this and then modify the method accordingly as well.
You can index each element using some index and keep incrementing that in ngrepeat, that way would make the elements unique and allow to keep and index.