It's very simple. I have 2 selects. When the user selects the category, it will appear another select with a subcategory.
The problem is that subcategory select is always empty.
Category JSON (vm.categories):
[
{
"doctype":"1120",
"description":"bla bla",
"subcategories":[
{
"#id":1,
"subcategory":"1",
"description":"New Offer",
"templates":[
{
"template":"12",
"description":"asfafasga",
"deliveryChannels":[
]
}
]
},
{
"#id":2,
"subcategory":"2",
"description":"New Offer",
"templates":[
{
"template":"1121",
"description":"asfag",
"deliveryChannels":[
{
"deliveryType":"4",
"description":"Test"
}
]
}
]
}
]
}
]
HTML
<div class="row">
<div class="col-sm-12">
<!-- Categories -->
<label for="category-select"><b>Category </b></label>
<select name="category-select" ng-model="vm.selectedCategory" required>
<option value="" disabled>--- Please select a category ---</option> <!-- not selected / blank option -->
<option value="{{category}}"
ng-repeat="category in vm.categories">
{{category.description}}
</option>
</select>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<!-- Sub Categories -->
<label ng-show="vm.selectedCategory" for="subcategory-select"><b>Sub-Category </b></label>
<select name="subcategory-select"
ng-show="vm.selectedCategory"
ng-model="vm.selectedSubCategory">
<option value="" disabled>--- Please select a sub-category ---</option> <!-- not selected / blank option -->
<option value="{{subCategory}}"
ng-repeat="subCategory in vm.selectedCategory.subcategories">
{{subCategory.description}}
</option>
</select>
</div>
</div>
Any idea why this is happening? Somehow I can't read the subcategories array from the selected category
EDIT: If I put <span>{{vm.selectedCategory}}</span> in my html, it shows the json above. but if I put <span>{{vm.selectedCategory.subcategories}}</span> it's null
Another edit: Even <span>{{vm.selectedCategory.doctype}}</span> is null
It is because your selected value is not evaluated as object but as string. To handle this issue you can:
You can either convert selected string to object with ng-change directive on first select and the following function
$scope.convert = function(){
$scope.vm.selectedCategory = angular.fromJson($scope.vm.selectedCategory)
}
or use ng-options
<select name="mySelect" id="mySelect" ng-options="category.description for category in data" ng-model="vm.selectedCategory">
</select>
Demo
To make a working demo, had to simplify vm.[namedVariables] into simple variables: kindly check below
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
$scope.selectedCategory = '';
$scope.categories = [{
"doctype": "1120",
"description": "bla bla",
"subcategories": [{
"#id": 1,
"subcategory": "1",
"description": "New Offer",
"templates": [{
"template": "12",
"description": "asfafasga",
"deliveryChannels": [
]
}]
},
{
"#id": 2,
"subcategory": "2",
"description": "New Offer2",
"templates": [{
"template": "1121",
"description": "asfag",
"deliveryChannels": [{
"deliveryType": "4",
"description": "Test"
}]
}]
}
]
}];
/*
for(var i=0; i<$scope.profiles.length; i++){
console.log($scope.profiles[0].TravelerExtended);
console.log($scope.profiles[0].TravelerExtended.ExtendedInt_1);
}
*/
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat='item in categories'>
{{item.description}}
<div ng-repeat='subC in item.subcategories'>
{{subC.subcategory}} - {{subC.description}}
</div>
</div>
<hr/>
<div class="row">
<div class="col-sm-12">
<!-- Categories -->
<label for="category-select"><b>Category </b></label>
<select name="category-select" ng-model="selectedCategory" required>
<option value="" disabled>--- Please select a category ---</option>
<!-- not selected / blank option -->
<option value="{{category.doctype}}" ng-repeat="category in categories">{{category.description}}</option>
</select>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<!-- Sub Categories -->
<label ng-show="selectedCategory" for="subcategory-select"><b>Sub-Category </b></label>
<ng-container ng-repeat="item in categories">
<!--
<p>(1) {{selectedCategory}} </p>
<p>(2) {{item.doctype}} </p>
-->
<select name="subcategory-select" ng-show="selectedCategory == item.doctype" ng-model="vm.selectedSubCategory">
<option value="" disabled>--- Please select a sub-category ---</option>
<!-- not selected / blank option -->
<option value="{{subCategory}}" ng-repeat="subCategory in item.subcategories">
{{subCategory.description}}</option>
</select>
</ng-container>
</div>
</div>
</div>
Instead of using the value attribute with interpolation, use the ng-value directive:
<div class="row">
<div class="col-sm-12">
<!-- Categories -->
<label for="category-select"><b>Category </b></label>
<select name="category-select" ng-model="vm.selectedCategory" required>
<option value="" disabled>--- Please select a category ---</option> <!-- not selected / blank option -->
̶<̶o̶p̶t̶i̶o̶n̶ ̶v̶a̶l̶u̶e̶=̶"̶{̶{̶c̶a̶t̶e̶g̶o̶r̶y̶}̶}̶"̶
<option ng-value="category"
ng-repeat="category in vm.categories">
{{category.description}}
</option>
</select>
</div>
</div>
The double curly bracket interpolation directive attempts to convert the category value to a string. This does not work well when the category value is an object.
For more information, see AngularJS ng-value Directive API Reference.
It says [Object Object] when I do <span>{{vm.selectedCategory}}</span> and i can't read the subcategories
Pipe the vm.selectedCategory into the json filter:
<span>{{ vm.selectedCategory | json }}</span>
This allows you to convert a JavaScript object into JSON string.
This filter is mostly useful for debugging. When using the double curly notation the binding is automatically converted to JSON.
For more information, see AngularJS json Filter API Reference.
Related
I have a screen where I create elements dynamically using reactive form, basically I create cards, where each has two selection fields:
Scenario: when I add a card and select a layout, the options of that specific layout are loaded in the select of assets through a service that makes the filter in the API, however when I add another card and select some other option in the layout select the two selects of assets are left with the same option.
Template
<div class="card"
formArrayName="scriptOperationOrders"
*ngFor="let workstation of formOperation.get('scriptOperationOrders')['controls']; index as idx"
>
<div class="card-body" [formGroupName]="idx">
<div class="form-row">
<div class="form-group col-md-1">
<label>Rank</label>
<input type="text" name="rank" class="form-control" formControlName="rank"/>
</div>
<div class="form-group col-md-2">
<label>Layout</label>
<select formGroupName="layout" (ngModelChange)="searchAssetsByLayouts($event)">
<option value="">Choose Layout</option>
<option
*ngFor="let lay of (layouts$ | async)?.dataArray "
[value]="lay.id">{{ lay.description }}
</option>
</select>
</div>
<div class="form-group col-md-2">
<label>Asset</label>
<select formGroupName="asset">
<option value="">Choose Asset</option>
<option
*ngFor="let asset of (assets$ | async)?.dataArray "
[value]="asset.id">{{ asset.description }}
</option>
</select>
</div>
</div>
</div>
</div>
Controller
layouts$: Observable<IResponse<ILayoutModel>>;
assets$: Observable<IResponse<IAssetModel>>;
ngOnInit() {
...
this.buildFormOperation();
this.layouts$ = this.layoutService.listLayouts();
this.providers$ = this.providerService.listProviders();
}
buildFormOperation() {
this.formOperation = this.fb.group({
script: [],
descriptionCode: [],
description: [],
scriptOperationOrders: new FormArray([])
})
}
searchAssetsByLayouts(layoutId: number) {
this.assets$ = this.assetService.listAssetsRoots(layoutId); // The assets$ variable is overridden here
}
Asset listing Service
listAssetsRoots(layoutId?: number | string): Observable<IResponse<IAssetModel>> {
return this.apiService.crudeList({
url: `${this.url}/roots`,
queryParams: {
layoutId
},
})
.pipe(map((res) => {
return new IResponse<IAssetModel>(res, IAssetModel);
}));
}
As you could be doing when selecting an option in the select layout, the options for that layout are loaded only in the select of assets on the same card
this.assets$ = this.assetService.listAssetsRoots(layoutId); as The assets$ variable is overridden here
ts file:-
//declared what type of response is expected
interface Assets{
id:number,
description :string
}
//initially set to empty
assets$ : Observable<Assets[]> = of([]);
//accepting second argument idx as row_index
searchAssetsByLayouts(layoutId: number,row_index:number) {
this.assets$[row_index] = this.assetService.listAssetsRoots(layoutId); // The assets$ variable is no more overridden
}
//html:-
//here used formControlName for 'layout' and 'asset' control insted of formGroupName
//and passed 'idx' as second parameter to (ngModelChange)="searchAssetsByLayouts($event,idx)
<div class="card"
formArrayName="scriptOperationOrders"
*ngFor="let workstation of formOperation.get('scriptOperationOrders')['controls']; index as idx"
>
<div class="card-body" [formGroupName]="idx">
<div class="form-row">
<div class="form-group col-md-1">
<label>Rank</label>
<input type="text" name="rank" class="form-control" formControlName="rank"/>
</div>
<div class="form-group col-md-2">
<label>Layout</label>
<select formControlName="layout" (ngModelChange)="searchAssetsByLayouts($event,idx)">
<option value="">Choose Layout</option>
<option
*ngFor="let lay of (layouts$ | async)?.dataArray "
[value]="lay.id">{{ lay.description }}
</option>
</select>
</div>
<div class="form-group col-md-2">
<label>Asset</label>
<select formControlName="asset">
<option value="">Choose Asset</option>
<option
*ngFor="let asset of (assets$[idx] | async)"
[value]="asset.id">{{ asset.description }}
</option>
</select>
</div>
</div>
</div>
</div>
I am having a terrible time wrapping my head around arrays for Vue JS.
new Vue({
el: '#app',
template: `
<div class="cascading-dropdown">
<div class="dropdown">
<span>Cereal:</span>
<select v-model="cerealname">
<option value="">SELECT A CEREAL</option>
<option v-for="(addon1_obj, addon1) in cereals" :value="addon1">{{addon1}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 1:</span>
<select :disabled="addons1.length == 0" v-model="addon1">
<option value="">Select Addon 1</option>
<option v-for="(addon2_obj, addon2) in addons1">{{addon2}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 2:</span>
<select :disabled="addons2.length == 0" v-model="addon2">
<option value="">Select Addon 2</option>
<option v-for="addon2 in addons2">{{addon2}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 3:</span>
<select :disabled="addons2.length == 0" v-model="addon3">
<option value="">Select Addon 3</option>
<option v-for="addon2 in addons2">{{addon2}}</option>
</select>
</div>
<div>Addon 1 text <input id="addon1desc" type="text" v-midel="addon1desc"></div>
<div>Addon 2 text <input id="addon2desc" type="text" v-midel="addon2desc"></div>
<div>Addon 3 text <input id="addon3desc" type="text" v-midel="addon3desc"></div>
</div>
`,
data: function() {
return {
cereals: {
"Lucky Charms": {
"Marshmallows": ["Green Clovers",
"Pink Hearts",
"Yellow Moons",
"Blue Diamonds",
"Purple Horseshoes"
]
},
"Froot Loops": {
"Loops": ["Red Loop",
"Green Loop",
"Blue Loop",
"Yellow Loop"
]
}
},
addons1: [],
addons2: [],
cerealname: "",
addon1: "",
addon2: "",
addon3: ""
}
},
watch: {
cerealname: function() {
// Clear previously selected values
this.addons1 = [];
this.addons2 = [];
this.addon1 = "";
this.addon2 = "";
this.addon3 = "";
// Populate list of countries in the second dropdown
if (this.cerealname.length > 0) {
this.addons1 = this.cereals[this.cerealname]
}
},
addon1: function() {
// Clear previously selected values
this.addons2 = [];
this.addon2 = "";
this.addon3 = "";
// Now we have a continent and country. Populate list of cities in the third dropdown
if (this.addon1.length > 0) {
this.addons2 = this.cereals[this.cerealname][this.addon1]
}
}
}
})
.dropdown {
margin: 10px 0;
padding: 10px 0;
border-bottom: 1px solid #DDD;
}
.dropdown span {
display: inline-block;
width: 80px;
}
<body>
<div id="app"></div>
</body>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
Here's a fiddle: https://jsfiddle.net/4zfh80au/
When you select Lucky Charms in the first dropdown, the 2nd dropdown changes to Marshmallows. The 3rd and 4th dropdowns change to the individual marshmallow types.
What I want is to add data to the text boxes based on the dropdown selection. When you choose the 2nd dropdown to be Marshmallows, I want the corresponding text box to fill in some text like "Marshmallows are yummy." The 3rd and 4th dropdowns should do the same.
Could anyone please point me in the right direction?? Much appreciated.
The binding is already set up in each of your <select> tags with v-model="addon1", addon2, and addon3.
In your <div>, have the bindings in the text fields match the ones defined in your <select> tags like so:
...
<div class="dropdown">
<span>Addon 1:</span>
<select :disabled="addons1.length == 0" v-model="addon1">
<option value="">Select Addon 1</option>
<option v-for="(addon2_obj, addon2) in addons1">{{addon2}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 2:</span>
<select :disabled="addons2.length == 0" v-model="addon2">
<option value="">Select Addon 2</option>
<option v-for="addon2 in addons2">{{addon2}}</option>
</select>
</div>
<div class="dropdown">
<span>Addon 3:</span>
<select :disabled="addons2.length == 0" v-model="addon3">
<option value="">Select Addon 3</option>
<option v-for="addon2 in addons2">{{addon2}}</option>
</select>
</div>
<div>Addon 1 text <input id="addon1desc" type="text" v-model="addon1"></div>
<div>Addon 2 text <input id="addon2desc" type="text" v-model="addon2"></div>
<div>Addon 3 text <input id="addon3desc" type="text" v-model="addon3"></div>
</div>
...
You can check out the working fiddle here.
Additionally, the fiddle link that you shared had a typo in v-model. If you run the app, you should see an error thrown into the console that says something like this:
[Vue warn]: Failed to resolve directive: midel
(found in anonymous component - use the "name" option for better debugging messages.)
If something in the app is failing, you can use the console to better debug your code.
For anyone wanting the solution, the Fiddle is here:
https://jsfiddle.net/q1jm0ccf/
The array looks as follows:
data: function() {
return {
cereals: {
"Lucky Charms": {
"Marshmallows": [
{"title": "Green Clovers", "value": "The Green Clovers is nice"},
{"title":"Pink Hearts", "value": "The Pink Hearts is cool"},
{"title":"Yellow Moons", "value": "The Yellow Moons is best"},
{"title":"Blue Diamonds","value": "The Blue Diamonds is bad"},
{"title":"Purple Horseshoes","value": "The Purple Horseshoes is normal"},
{"title":"Red","value": "The red ones are great."},
{"title":"Yellow","value": "The yellow ones are better"}
]
},
"Froot Loops": {
"Loops": [
{"title":"Red","value": "Red loops are cherry"},
{"title":"Green","value": "Green loops are lime"}
]
}
},
How do I hide the div class if the value chosen in the select option is usd ?
app.html
<select name="" id="" class="form-control" value.bind="">
<option repeat.for="option of Options" model.bind="option.value">
${option.name}
</option>
</select>
<div class="row">
<!-- enter code here -->
</div>
app.ts
export class Test {
Option = [
{ value: 'usd', name: 'america'},
{ value: 'cad', name: 'canada money'}
];
}
I agree with Rajkumar you should really make some more effort trying to work this out. You do not even have the code for the basic select, the code for which is all over the internet, an example : https://ilikekillnerds.com/2015/10/working-with-forms-in-aurelia/
That said I will try and help:
<template>
<form role="form">
<select value.bind="selectedVal">
<option repeat.for="option of someOptions"
model.bind="option">${option.name}</option>
</select>
</form>
</template>
<div class="row" show.bind="selectedVal === 'usd'">
UsD SeLeCtEd
</div>
<div class="row" show.bind="selectedVal !== 'usd'">
Other SeLeCtEd
</div>
The above should show only the "usd selected" content when you select USD, and "other selected" when you select anything else. This is not the best way of doing it but should work.
I have the following dropdown select HTML tags
<label>Car:</label>
<select ng-model="params.CarName">
<option value="x" disabled="" selected="">Select Car...</option>
<option value="BMW">BMW</option>
<option value="Audi">Audi</option>
</select>
<label>Model:</label>
<select ng-if="params.CarName == 'BMW'" ng-model="params.CarModel" ng-change="PerfRpt()">
<option value="x" disabled="" selected="">BMW Sports</option>
<option value="BMW 328i">BMW 328i</option>
<option value="BMW Sports">BMW Sports</option>
</select>
<select ng-if="params.CarName == 'Audi'" ng-model="params.CarModel" ng-change="PerfRpt()">
<option value=" " disabled="" selected="">Audi Sports</option>
<option value="Audi i345">Audi i345</option>
<option value="Audi Sports">Audi Sports</option>
</select>
<select ng-if="params.CarName!= 'BMW' && params.CarName != 'Audi'">
<option>-</option>
</select>
As per the above code, this is wat it does, We have a dropdown to select Car. It is either BMW or Audi.
Depending on this selection, The Model dropdown will be shown. For e.g If we select Audi for Car in the first dropdown, then Audi i345 and Audi Sports will be shown as the dropdown in the 'Model' field.
Similarly, other options will be shown if we select BMW.
Now I am planning to put the Car data in DB along with Model data. Once the page loads, the data will be retrieved and displayed to the user for the 'Car' section. Once the car is selected, depending on the value of the car, its corresponding Model will be updated in the Model section then.
I can get the data from backend to frontend. I want to know how to display those values dynamically and not hardcode like I have done here.
I have the following response from database for the Car.
Results: Array[2]
0: Object
Car:BMW
1: Object
Car:Audi
You need an array with the Car Names, eg.
$scope.carNameOptions = [
'BMW'
'Audi'
];
Then you would do this:
<select ng-model="params.CarName">
<option value="">Select car...</option>
<option ng-repeat="car in carNameOptions" value="{{car}}">{{car}}</option>
</select>
I would suggest making params.CarModel an object with the keys being the CarNames, and the value an array with options for the car like above. Then you can do this:
<select ng-model="params.CarModel">
<option value="">Select model...</option>
<option ng-repeat="carModel in carModelOptions[params.CarName]" value="{{carModel}}">{{carModel}}</option>
</select>
eg
$scope.carModelOptions = {
'BMW' : [
'BMW 328i',
...
],
'Audi' : [...]
}
Since you have only the cars models stored in your database, you can retrieve them and then make a new array putting inside it the types, as follows:
(function() {
"use strict";
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope'];
function MainCtrl($scope) {
// From database
var data = [
'BMW',
'Audi'
];
$scope.cars = [
{
"model":"BMW",
"types":[
"BMW 328i",
"BMW Sports"
]
},
{
"model":"Audi",
"types":[
"Audi i345",
"Audi Sports"
]
}
];
}
})();
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body ng-controller="MainCtrl">
<div class="col-md-12">
<select class="form-control" ng-options="car.model for car in cars" ng-model="carSelected">
<option value="" label="Select a car" hidden></option>
</select>
<select class="form-control" ng-disabled="!carSelected" ng-options="type for type in carSelected.types" ng-model="typeSelected">
<option value="" label="Select a type" hidden></option>
</select>
<hr>
Car selected: <pre ng-bind="carSelected | json"></pre>
Type selected: <pre ng-bind="typeSelected"></pre>
</div>
</body>
</html>
Note: I've used ngOptions directive, which one MUST be used for <select>, not ngRepeat.
I hope it helps.
If I have a bunch of table rows and with select tags in them how would I get the selected value?
If the variable "selected_ingr_action" is the same for every row then when I select an item they all change instead of just the one.
I know I'm missing something.
{{#Ingredients}}
<tr>
<td class="ingr-bid-col"></td>
<td>
<div class="row">
<div class="col-xs-12">
<p class="ingr-name">{{Name}}</p>
</div>
</div>
<div class="row">
<div class="col-xs-6">
{{VendorName}}<br />
<b>pack:</b><span>{{Pack}}</span>
</div>
<div class="col-xs-6" style="padding-bottom:5px;">
<div class="caption">
<b>unit price</b><br />
<span>{{UnitPrice}}</span>
</div>
<div class="caption">
<b>case price</b><br />
<span>{{CasePrice}}</span>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<select class="form-control" value="{{selected_ingr_action}}">
<option value="-1" selected disabled>Action</option>
<option value="0">Rename</option>
<option value="1">Edit Unit Price</option>
<option value="2">Archive</option>
</select>
</div>
</div>
</td>
</tr>
{{/}}
I think perhaps you might be asking a much simpler question about why the rows are sharing the value. If this is the case, the short answer is to use a restricted reference (notice the .):
<select class="form-control" value="{{.selected_ingr_action}}">
<option value="-1" selected disabled>Action</option>
<option value="0">Rename</option>
<option value="1">Edit Unit Price</option>
<option value="2">Archive</option>
</select>
This causes the selected_ingr_action to be bound to the list item, not the root of the ractive instance.
You can access the result on each item with a wilcard observer:
r.observe('list.*.selected_ingr_action', function(n, o, k, i){
console.log('list index', i, 'changed from', o, 'to', n);
});
Check it out here.
The behavior of defaulting to root, rather than current context, is under consideration to be changed to resolve to the current context.
One option would be to have a separate map of selected values, and use two-way binding to update that map (rather than a single selected_ingr_action property, or a selected_ingr_action property belonging to each item in the list). You can see an example of this approach here - the details are different but the principle is the same.
{{#Ingredients :i}} <!-- note the index reference -->
...
<select class="form-control" value="{{selected_ingr_actions[i]}}">
<option value="-1" selected disabled>Action</option>
<option value="0">Rename</option>
<option value="1">Edit Unit Price</option>
<option value="2">Archive</option>
</select>
There are to ways to solve this.
First, bind the value of the select into data.
I.e. turn this string:
<select class="form-control" value="selected_ingr_action">
into this:
<select class="form-control" value="{{selected_ingr_action}}">
Assuming that your data structure is something like this:
var Ingredients = [
{
Name: String,
VendorName: String,
UnitPrice: Number,
CasePrice: Number,
selected_ingr_action: String
},
/* other entries */
];
As a second thought, maybe you don't want to store action type inside your data.
I beleive that better approach would be to use so-called Proxy Events with custom arguments and Method Calls.
Here, I add key into your template and when you change your select value, corresponding event handler are called with the index of your row.
Like this:
{{#each Ingredients: key}} <!-- <<< note that key -->
<!-- skip -->
<div class="row">
<div class="col-xs-12">
<select class="form-control" on-change="changeAction(this, key)" value="{{this.selected_ingr_action}}">
<!-- note this too ^^^^^^^^^^^^^^^^^^^^^^ -->
<option value="-1" selected disabled>Action</option>
<option value="0">Rename</option>
<option value="1">Edit Unit Price</option>
<option value="2">Archive</option>
</select>
</div>
</div>
{{/each}}
Note, into your Ractive component you can introduce method changeAction, which will receive current row (with corresponding Name, VendorName etc. properties) and it's key. Like this:
var ractive = new Ractive({
/* skip */
changeAction: function (ingridient, key) {
// do something with that ingridient
}
/* skip */
});