My contains 2 different objects, obj1 and obj2.
<select multiple class="full-width" style="min-height: 200px" ng-model="vm.obj1" >
<optgroup label="First obj">
<option ng-repeat="item in vm.obj1" >{{item.valeur}}</option>
</optgroup>
<optgroup label="Second obj">
<option ng-repeat="item in vm.obj2">{{item.libelle}}</option>
</optgroup>
</select>
obj1 = {[
0: {valeur: non},
1: {valeur: oui}
]}
obj2 = {[
0: {libelle: instance},
]}
What I get when I select values :
What I actually want :
I want the values to be in separated arrays since they are both from different objects so 1 array with ['oui','non'] and the second array with ['instance']. How can I do that ?
Visual of the dropdown ( sorry for the big blue lines I wanted to stay on the point with the datas I made in the question )
You can use ngChange to respond to any changes to the ngModel value and store that in a new property:
function ctrl($scope) {
$scope.options = [{
name: "A",
options: ["A1", "A2"]
},
{
name: "B",
options: ["B1", "B2"]
},
];
$scope.parseSelection = function() {
const selected = {};
// Loop over the selected options and check from which group they came
$scope.rawSelected.forEach((value) => {
$scope.options.forEach(({ name, options }) => {
if (options.includes(value)) {
// The option comes from the current group
if (!selected[name]) {
selected[name] = [];
}
selected[name].push(value);
}
});
});
$scope.selected = selected;
};
}
angular.module("app", [])
.controller("ctrl", ctrl)
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.js"></script>
<div ng-app="app" ng-controller="ctrl">
<select multiple ng-model="rawSelected" ng-change="parseSelection()">
<optgroup ng-repeat="group in options" label="group.name">
<option ng-repeat="option in group.options">{{option}}</option>
</optgroup>
</select>
{{rawSelected}} - {{selected}}
</div>
Related
I have 2 filters on a user list. A user can select a group containing members and use the search filter to search by last name. When the user backspaces a user to look for another, this resets the groups to all users. I need this to only show the users in the selected group.
TS
updateFilter(event) {
const val = event.target.value.toLowerCase();
const temp = this.temp.filter(function (d) {
return d.lastName.toLowerCase().indexOf(val) !== -1 || !val;
});
this.rows = temp;
if (this.table) {
this.table.offset = 0;
}
}
onGroupSelected($event) {
const groupId = $event.target ? $event.target.value : $event;
if (groupId === 'none') {
this.rows = this.temp;
} else {
const groupUsers = this.groupUserMap.get(groupId);
if (groupUsers) {
this.rows = this.temp.filter((serviceUser) =>
groupUsers.includes(serviceUser.id));
} else {
this.rows = [];
}
}
// #ts-ignore
this.userSelections = this.userSelections ? this.userSelections : {};
this.userSelections.groupId = groupId;
localForage.setItem(this.username, this.userSelections);
}
HTML
<input
type='text'
class="form-control w-200px"
placeholder='Search by Last Name...'
(keyup)='updateFilter($event)'
/>
<select class="form-control w-200px" (change)="onGroupSelected($event)">
<option value="none">All service users</option>
<option *ngFor="let group of groups"
[value]="group.id"
[selected]="userSelections.groupId === group.id">
{{group.name}}
</option>
</select>
You can use ngModel with tow-way binding, to save and manipulate search filters:
<select
class="form-control w-200px"
[(ngModel)]="selectedGroup"
(change)="onGroupSelected()"
>
<option value="none">All service users</option>
<option *ngFor="let group of groups" [value]="group.id">
{{ group.name }}
</option>
</select>
<input
type="text"
class="form-control w-200px"
placeholder="Search by Last Name..."
[(ngModel)]="search"
(keyup)="updateFilter()"
/>
And in order not to lose your users table you can create a copy which will be filtered and displayed.
public initialUsers = [
{ id: 100, groupId: 1, name: 'foo' },
{ id: 101, groupId: 2, name: 'bar' },
{ id: 102, groupId: 1, name: 'john' },
{ id: 103, groupId: 2, name: 'doe' },
{ id: 104, groupId: 2, name: 'baaar' },
{ id: 105, groupId: 1, name: 'fooodoe' },
];
public filteredUsers = [];
ngOnInit(): void {
this.filteredUsers = this.initialUsers;
}
Here is a demo on stackblitz, I used a list to go fast but It's just display. You just have to replace <ul> <li></li> </ul> by your <table> ... </table>
If you would want to take an observable way of doing this, then I would suggest to make a form of your controls, ooooor just use 2 form controls instead. I chose form here as it wraps it up nicely with both form controls (search and dropdown):
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
search: [''],
group: [0] // "all" option as initial id
})
}
Then we would listen to when the form value changes and assign the filtered data to a variable, here named filteredUsers$.
this.filteredUsers$ = this.form.valueChanges.pipe(
startWith(this.form.value), // to trigger initially
// 'this.users' refers to your original users array
map((value: any) => {
// 'all' option is chosen, just filter based on search
if (value.group === 0) return this.users.filter(x => x.lastName.toLowerCase().includes(value.search))
// filter by group and search
return this.users.filter(x => {
return (x.groupId === value.group) && (x.lastName.toLowerCase().includes(value.search.toLowerCase()))
})
})
)
That is it, then we just iterate filteredUsers$ in the template:
<tr *ngFor="let user of filteredUsers$ | async">
Of course we need the form in the view and it would look like this:
<form [formGroup]="form">
<input
type="text"
placeholder="Search by Last Name..."
formControlName="search"
/>
<select formControlName="group">
<option *ngFor="let group of groups" [ngValue]="group.id">
{{ group.name }}
</option>
</select>
</form>
Here is a DEMO with the above code
I'm struggling to find a second solution to how I have this implemented. First I will show you how it's implemented then explain how it needs to be changed.
html:
<div class="input-group">
<h4>Session: </h4>
<select class="custom-select form-control-sm" id="inputGroupSelect01"
(change)="sessionDataChange($event)" [(ngModel)]="sessionReportFilter.sessionName">
<option value="null">Select session...</option>
<option *ngFor="let session of sessionData; index as i;"
[value]="i">
{{session.sessionName}}
</option>
</select>
</div>
<div class="input-group">
<h4>Report Date: </h4>
<select class="custom-select form-control-sm" id="inputGroupSelect01"[(ngModel)]="sessionReportFilter.fileName">
<option value="">Select report date...</option>
<option *ngFor="let report of reports"
[value]="report">
{{report}}
</option>
</select>
</div>
<div>
<button type="button" class="btn btn-primary" (click) ="orderExceptionReportData()">Retrieve</button>
</div>
</div>
component.ts:
export class OrderExceptionReportComponent implements OnInit {
public sessionData: ExceptionReportSessionData[] = [];
public sessionReportData: ExceptionReportData;
public sessionReportFilter: ExceptionReportFilter = {
sessionName: "Washington",
fileName: "EXCEPTION20130211060056882.csv"
}
reports = [];
cols = [
{ header: 'ItemId' },
{ header: 'AltItemId' },
{ header: 'GenName' },
{ header: 'StationName' },
{ header: 'HospitalCode' },
{ header: 'Facility' },
{ header: 'Reason' }
];
constructor(private orderExceptionReportService: OrderExceptionReportService) {
}
public async getExceptionReportSessionData(): Promise<void> {
return this.orderExceptionReportService.GetExceptionReportSessionData()
.then(
data => {
this.sessionData = data;
});
}
public async orderExceptionReportData(): Promise<void> {
return this.orderExceptionReportService.OrderExceptionReport(this.sessionReportFilter)
.then(
data => {
this.sessionReportData = data;
console.log(this.sessionReportData)
});
}
async ngOnInit() {
await this.getExceptionReportSessionData();
}
sessionDataChange(evt) {
const value = evt.target.value;
if (isNaN(Number(value))) {
this.reports = [];
} else {
this.reports = this.sessionData[Number(value)].reportFiles;
}
console.log(this.reports);
}
}
Right now as you can see in my first drop down the [value] is set to i and that's so the function I have called on (change) can get the correct data show in the second drop down. Well I need [value] to be set to session.sessionName because I am using two way databinding so that when the user clicks the retrieve button the correct data is being sent to the function. How can I change the implementation so that based on which Session the user selects in the drop downs only the ReportDates associated with the Session are correct so that I can use two way data binding to make the function call on the button Retrieve?
One simple way is to update the sessionData of the report select when change event occurs on session select. You have several way of doing this I will propose you a simple one,
component.ts
sessions: ExceptionReportSessionData[] = []; // the list obtained from service
reports: string[] = []; // the reports list
sessionChange(evt) {
const value = evt.target.value;
console.log(`session index: ${value}`);
if (isNaN(Number(value))) {
this.reports = [];
} else {
this.reports = this.sessions[Number(value)].ReportFiles;
}
console.log(`reports: ${this.reports}`);
}
component.html
<div class="input-group">
<h4>Session: </h4>
<select class="custom-select form-control-sm"
(change)="sessionChange($event.target.value)">
<option value="null">Select session...</option>
<option *ngFor="let session of sessions; index as i;"
value="{{i}}">{{session.SessionName}}</option>
</select>
</div>
<div class="input-group">
<h4>Report Date: </h4>
<select class="custom-select form-control-sm">
<option value="null">Select report date...</option>
<option *ngFor="let report of reports"
value="report">{{report}}</option>
</select>
</div>
Pretty straight forward implementation of the above idea. Like I mention there are several ways to do this.
Working Blitz
Change your html to :-
<div class="input-group">
<h4>Session: </h4>
<select class="custom-select form-control-sm" id="inputGroupSelect01"
(change)="sessionDataChange($event)" [(ngModel)]="sessionReportFilter.sessionName">
<option value="null">Select session...</option>
<option *ngFor="let session of sessionData; index as i;"
[value]="session.sessionName">
{{session.sessionName}}
</option>
</select>
</div>
<div class="input-group">
<h4>Report Date: </h4>
<select class="custom-select form-control-sm" id="inputGroupSelect01"[(ngModel)]="sessionReportFilter.fileName">
<option value="">Select report date...</option>
<option *ngFor="let report of reports"
[value]="report">
{{report}}
</option>
</select>
</div>
<div>
<button type="button" class="btn btn-primary" (click) ="orderExceptionReportData()">Retrieve</button>
</div>
</div>
Change your typescript method to :-
sessionDataChange(evt) {
const sessionName= evt.target.value;
if (!sessionName || sessionName.length === 0) {
this.reports = [];
} else {
this.reports = this.sessionData.find((session) => session.sessionName === sessionName).reportFiles;
}
console.log(this.reports);
}
I have a select dropdown,and divs coming from loop.Here when I change the drop down option to city,my div id should change to one,two three which comes from details column from json.Again when I change the drop down option to state,my div id should change to title1,title2 title3 which comes from title column from json.Here it is working fine but I am creating new divs for each condition,can it possible to make in a single div with multiple condition.Here is the code below.
HTML
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<select class="change" ng-model="x" ng-change="update()">
<option value="city">Cities</option>
<option value="state">States</option>
<option value="country">Countries</option>
</select>
<div ng-if="id=='city'">
<div ng-repeat="emp in groups" ng-attr-id="{{emp.details}}" >hello</div>
</div>
<div ng-if="id=='state'">
<div ng-repeat="emp in groups" ng-attr-id="{{emp.title}}" >hello</div>
</div>
<div ng-if="id=='country'">
<div ng-repeat="emp in groups" ng-attr-id="{{emp.name}}" >hello</div>
</div>
script
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.groups = [
{
title: 'title1',
name:'name1',
details:'one'
},
{
title: 'title2',
name:'name2',
details:'two'
},
{
title: 'title3',
name:'name2',
details:'three'
}
]
$scope.update = function() {
if($scope.x == 'city'){
$scope.id='city';
}
if($scope.x == 'state'){
$scope.id='state';
}
if($scope.x == 'country'){
$scope.id='country';
}
}
});
To achieve the desired result, try to:
create an attr for each value - city, state and country, like so:
if($scope.x == 'city'){
$scope.id='city';
$scope.attr = 'details';
}
Use {{emp[attr]}} to display values based on the dropdown selection:
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.groups = [{
title: 'title1',
name: 'name1',
details: 'one'
},
{
title: 'title2',
name: 'name2',
details: 'two'
},
{
title: 'title3',
name: 'name2',
details: 'three'
}
]
$scope.update = function() {
if ($scope.x == 'city') {
$scope.id = 'city';
$scope.attr = 'details';
}
if ($scope.x == 'state') {
$scope.id = 'state';
$scope.attr = 'title';
}
if ($scope.x == 'country') {
$scope.id = 'country';
$scope.attr = 'name';
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<select class="change" ng-model="x" ng-change="update()">
<option value="city">Cities</option>
<option value="state">States</option>
<option value="country">Countries</option>
</select>
<div ng-repeat="emp in groups" ng-attr-id="{{emp[attr]}}">{{emp[attr]}}</div>
</div>
</div>
codepen - https://codepen.io/nagasai/pen/wRQWRM
To this in a singal element you can use a nested ternary operator. For your case it will look like that:
<div ng-repeat="emp in groups" ng-attr-id="{{id=='city' ? emp.details : id=='state' ? emp.title : emp.name}}" >hello</div>
I didn't implement this on angular 1 but it works on angular 2. Angular built in directive (ngif, ngfor ngclass etc) works almost same for both versions.
Need help creating a dynamic search form with select options for Districts, Regions and locations.
Regions select must be populated based on the District and Locations based on the Regions
The data is stored on a JSON file with the following structure:
[
{
"level": 1,
"code": 1,
"name": "District"
},
{
"level": 2,
"code": 101,
"name": "Region"
},
{
"level": 3,
"code": 10101,
"name": "Location"
}
]
here´s the complete JSON file:
https://gist.github.com/tomahock/a6c07dd255d04499d8336237e35a4827
html snippet
<select name="district" v-model="district">
<option value=''>Select District</option>
<option v-for="district in filterDistricts" :value="district.code">
{{ district.name }}
</option>
</select>
<select name="region" v-model="region">
<option value=''>Select Region</option>
<option v-for="region in filterRegions" :value="region.code">
{{ region.name }}
</option>
</select>
<select name="location" v-model="location">
<option value=''>Select Location</option>
<option v-for="location in filterLocations" :value="location.code">
{{ location.name }}
</option>
</select>
javascript snippet
data() {
return {
searchData: [],
districts: [],
regions: [],
locations: []
}
},
created(){
this.fetchData();
},
computed: {
filterDistricts() {
return this.districts = this.searchData.map(res => ({
level: res.level,
code: res.code,
name: res.name
}))
.filter( res => res.level === 1)
},
filterRegions() {
return this.regions = this.searchData.map(res => ({
level: res.level,
code: res.code,
name: res.name
}))
.filter( res => res.level === 2)
},
filterLocations() {
return this.locations = this.searchData.map(res => ({
level: res.level,
code: res.code,
name: res.name
}))
.filter( res => res.level === 3)
}
},
methods: {
fetchData(){
axios.get('http://localhost:8000/json/searchData.json')
.then((response) => (
this.searchData = response.data
))
.catch((err) => {
console.log(err)
})
}
}
I think I need to associate de code numbers, but I can´t figure out how.
Any ideas?
Thanks
First, I wouldn't bother with those map calls since you're only reproducing the same structure.
Second, I'll assume that each sub-element (region / location) relates to its parent (district / region) via a pattern where each sub's code is prefixed with the parent code, followed by a two-digits, zero-padded.
With that in mind, try this in your computed properties
filterDistricts () {
return this.searchData.filter(({ level }) => level === 1)
},
filterRegions () {
// assuming you don't want any selections until a district is chosen
if (!this.district) return []
const codeCheck = new RegExp(`^${this.district}\\d{2}$`)
return this.searchData.filter(({ level, code }) =>
level === 2 && codeCheck.test(code))
},
filterLocations () {
if (!this.region) return []
const codeCheck = new RegExp(`^${this.region}\\d{2}$`)
return this.searchData.filter(({ level, code }) =>
level === 3 && codeCheck.test(code))
}
Extra notes...
From looking at your template, it seems you should initialise your data as
data () {
return {
searchData: [],
district: null,
region: null,
location: null
}
}
Computed properties don't need to be stored in data properties so you don't need districts, regions and locations.
Your label options should also be disabled so they cannot be selected, eg
<option disabled value="">Select Region</option>
I have a select options and a couple of textareas inside an ng-repeat and what I would like to do is, when I select an option from the menu the respective set of textareas show up some information that belongs to what I just selected.
Instead of doing this all of the textareas created with the ng-repeat show the information.
Here's the link to a JSFiddle that may explain better the problem:
https://jsfiddle.net/711yvm8g/5/
Here's the HTML code:
<div ng-app="App">
<div ng-controller="Ctrl">
<div ng-repeat="data in carData">
<select ng-model = "carSelected" ng-change = "changeInfo(carSelected)" data-ng-options = "car as car.name for car in cars">
<option value = "">Select car</option>
</select>
<textarea>{{colorData}}</textarea>
<textarea>{{yearData}}</textarea>
</div>
And here's the Javascript code:
angular.module('App', []);
function Ctrl($scope) {
//This carData object was created only to make the ng-repeat show multiple instances of the fields.
$scope.carData = {
a:"abc",
b:"def"
}
$scope.cars = [{
name: "Volvo"
}, {
name: "Saab"
}]
var volvoInfo = {
color:"Blue",
year:"2016"
}
var saabInfo = {
color:"Red",
year:"2015"
}
$scope.changeInfo = function(carSelected) {
if(carSelected.name == "Volvo") {
$scope.colorData = volvoInfo.color;
$scope.yearData = volvoInfo.year;
} else {
$scope.colorData = saabInfo.color;
$scope.yearData = saabInfo.year;
}
}
}
Is there a way I can solve this issue? Thanks a lot.
You should restructure your code to use arrays of objects. That way it is easier to manage.
angular.module('App', []);
function Ctrl($scope) {
let carInfoModel = {
name: '',
color: '',
year: '',
}
$scope.cars = [angular.copy(carInfoModel),angular.copy(carInfoModel)]
$scope.carsInfo = [{
name: 'Volvo',
color: 'Blue',
year: "2016"
}, {
name: 'Saab',
color: 'Red',
year: "2015"
}]
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="App">
<div ng-controller="Ctrl">
<div ng-repeat="data in cars">
<select ng-model="data" data-ng-options="car as car.name for car in carsInfo">
<option value="">Select car</option>
</select>
<textarea>{{data.color}}</textarea>
<textarea>{{data.year}}</textarea>
</div>
</div>
</div>
Try Ctrl.colorData and Ctrl.yearData for your textarea bindings