Dropdown inside select in angular js - angularjs

I have the following JSON object :
[{
"Question": "Sample question One",
"Options": [{
"OptionId": 1,
"Option": "Yes"
}, {
"OptionId": 2,
"Option": "No"
}]
}, {
"Question": "Sample question Two",
"Options": [{
"OptionId": 1,
"Option": "Yes"
}, {
"OptionId": 2,
"Option": "No"
}]
}, {
"Question": "Sample question Three",
"Options": [{
"OptionId": 1,
"Option": "Yes"
}, {
"OptionId": 2,
"Option": "No",
}, {
"OptionId": 3,
"Option": "May be",
}]
}]
I am using a select with ng-options to iterate each question. Inside the select I am adding a dropdown with the options binded to it.
This is works properly.
Now I have to hide a div if the option selected for the second question is no and if the option selected is yes, i have to show the div.
I tried using ng-show and ng-hide based on a model property "isDetailsVisible". I thought it would be easy to bind this to the second question's answer but I am unable to think of a way to do this.
Does anyone have ideas?

This may help you I think,
In this I have seperated the display as follows
Custom directive questioninfo: for displaying the information about the selected question. This also shows the options of the selected question, but before iterating, i use filter on the options to get only available options(with value 'Yes')
Filter optionFilter: For filtering the options, I have applied the filter to select options with given value('Yes') in 'Option'
var app = angular.module('plunker', []);
app.factory('dataService', function() {
return {
data: {
"Questions": [{
"Question": "Sample question One",
"Options": [{
"OptionId": 1,
"Option": "Yes"
}, {
"OptionId": 2,
"Option": "No"
}]
}, {
"Question": "Sample question Two",
"Options": [{
"OptionId": 1,
"Option": "No"
}, {
"OptionId": 2,
"Option": "No"
}]
}]
}
};
});
// I am using this filter because options should not be many
app.filter('optionFilter', function() {
return function(items, value) {
if (!angular.isUndefined(items)) {
var arrayToReturn = [];
for (var i = 0; i < items.length; i++) {
if (items[i].Option === value) {
arrayToReturn.push(items[i]);
}
}
return arrayToReturn;
}
};
});
app.directive('questioninfo', function() {
return {
restrict: 'AE',
scope: {
selectedQuestion:'=question'
},
controller: function($scope) {},
template: '<h2>{{selectedQuestion.Question}}</h2>\
<h3>Available Options</h3>\
<div ng-repeat="option in selectedQuestion.Options|optionFilter:\'Yes\'"> \
<div ng-if="shouldShowOption(option)"><b>Option {{option.OptionId}}</b> {{option.Option}}</div>\
</div>\
\
'
};
})
app.controller('MainCtrl', function($scope, dataService) {
$scope.name = 'World';
$scope.data = dataService.data;
$scope.Questions = dataService.data.Questions;
$scope.selectedQuestion = dataService.data.Questions[1].Question;
});
HTML Code:
<body ng-controller="MainCtrl">
<h1>Select Question</h1>
<select id="s1" ng-model="selectedQuestion" ng-options="item as item.Question for item in Questions">
</select>
<h3>The selected item just for reference:</h3>
<pre>{{selectedQuestion | json}}</pre>
<hr>
<questioninfo question="selectedQuestion"></questioninfo>
</body>
</html>
Check this link [Plunker]: http://plnkr.co/edit/LIVVofC8nyYtbYYVBBCb?p=preview

Thanks Ganesh for the detailed answer. I am expecting a little more simpler way.
I think I found an answer. I updated the JSON to make it easier, but this does not impact much.
[{
"Question": "Sample question One",
"Options": ["Yes", "No"],
"SelectedOption": "Yes"
}, {
"Question": "Sample question Two",
"Options": ["Yes", "No"],
"SelectedOption": "Yes"
}, {
"Question": "Sample question Three",
"Options": ["Yes", "No"],
"SelectedOption": "Yes"
}]
on the html, I have the following :
<div class="col-md-12 nopadding" data-ng-repeat="Question in Questions">
<select ng-model="question.SelectedOption" ng-options="option for option in Question.Options"> </select></div>
and for the div which I have to show and hide, I am using ng-show.
<div class="box-3" ng-show="Questions[1].SelectedOption === 'Yes'">
This works fine. But I feel the ng-show should have been mapped to a property or a method so that the logic resides in the controller and not in the html.
I would appreciate any comments on my above approach or any thoughts on how to have this logic moved to controller. Questions are not loaded initially. There is a button click on which the question loads and from that time I want the div to respond to the answer of the
second question. Till the question loads, the div is hidden.

Related

Using ng-if and ng-repeat correctly

I have a rudimentary understanding of AngularJS and have a couple (possibly stupid) questions about ng-if and ng-repeat.
If I have a ng-repeat that looks like this:
For all containers without titles, would the ng-repeat produce those containers and not show them? Or do they simply don't exist? The reason I ask is I've tried to replace ng-if with ng-show, but it does not produce the same outcome of "hiding" containers that do not have titles.
Is there a way to code a ng-if to say if the next container has no title, then do something. I tried something like ng-if="item.title=='' && $index+1", without any luck.
Any suggestions?
My data looks like this:
"_sections": [{
"_bootstrap_cells": 6,
"_count": 2,
"visible": true,
"columns": [{
"fields": [{
"name": "type_of_account",
"type": "field"
}, {
"name": "routing_transit_number",
"type": "field"
}]
}, {
"fields": [{
"name": "type_of_payment",
"type": "field"
}, {
"name": "check_digit",
"type": "field"
}]
}],
"caption": "Direct Deposit",
"id": "b456b9d2137ac340177c36328144b0ef"
}, {
"_bootstrap_cells": 12,
"_count": 1,
"visible": true,
"columns": [{
"fields": [{
"name": "account_number",
"type": "field"
}, {
"name": "account_title",
"type": "field"
}, {
"name": "financial_institution_name",
"type": "field"
}]
}],
"caption": "",
"id": ""
}
}]
The first section has two columns and a bootstrap cell value of 6 for each column, the second section only has one column and a bootstrap cell of 12. Since the second doesn't have a caption, I want it to be appended to the first section, but keep both sections' bootstrap formatting.
If containers is an array then it does not have a title property. You need to check the title on each item.
Also note that ng-if will not hide the content, but remove it or not insert it into the DOM. The counterpart of ng-show is ng-hide.
angular.module('appModule', [])
.controller('MyController', [function() {
this.containers = [{
aaa: 1,
title: 'One'
}, {
aaa: 2,
title: ''
}, {
aaa: 3,
title: 'Three'
}]
}]);
angular.bootstrap(window.document, ['appModule'], {
strictDi: true
});
<div ng-controller="MyController as myCtrl">
<div ng-repeat="item in myCtrl.containers track by $index">
<div ng-if="item.title!=''">{{$index}}. {{item.title}}</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>

Dynamic form using AngularJS, multiple values binding

I am looking for a best approach to convert a static form to an angular dynamic form. I am not sure how to bind multiple values to the same answer.
The static page is available at: https://jsfiddle.net/hvuq5h46/
<div ng-repeat="i in items">
<select ng-model="i.answer" ng-options="o.id as o.title for o in i.answersAvailable" ng-visible="y.TYPE = 'SINGLE'"></select>
<input type="checkbox" ng-model="i.answer" ng-visible="y.TYPE = 'MULTIPLE'" />
</div>
The JSON file
[
{
"id": 1,
"title": "Are you a student?",
"type": "SINGLE",
"answersAvailable": [
{
"id": 1,
"title": "Yes"
},
{
"id": 2,
"title": "No"
}
],
"answer": [
1
]
},
{
"id": 2,
"title": "Would you like to be an astronaut?",
"type": "SINGLE",
"answersAvailable": [
{
"id": 4,
"title": "Yes"
},
{
"id": 5,
"title": "No"
},
{
"id": 6,
"title": "I am not sure"
}
],
"answer": [
4
]
},
{
"id": 3,
"title": "What is your favourite planet?",
"type": "MULTIPLE",
"answersAvailable": [
{
"id": 7,
"title": "Earth"
},
{
"id": 8,
"title": "Mars"
},
{
"id": 9,
"title": "Jupiter"
}
],
"answer": [
7,
8
]
}
]
Things would be much simpler if you can use a multiple select, but I understand it might be difficult for user to interact (consider something like md-select, which transforms multiple select into a list of checkbox for you)
Multiple select:
<select multiple
ng-model="i.answer"
ng-options="o.id as o.title for o in i.answersAvailable"
ng-if="i.type == 'MULTIPLE'"></select>
Anyway it is completely ok to use HTML checkbox. To do that we would need to bind checkbox model into the data as usual, and then update the answer array simultaneously.
ng-model="o.selected"
ng-change="updateAnswer(i)"
Also, we'll need to copy existing data to model during init.
ng-init="initMultiple(i)"
Working code:
angular.module('test', []).controller('Test', Test);
function Test($scope) {
$scope.items = [{
"id": 1,
"title": "Are you a student?",
"type": "SINGLE",
"answersAvailable": [{
"id": 1,
"title": "Yes"
},
{
"id": 2,
"title": "No"
}
],
"answer": [
1
]
},
{
"id": 2,
"title": "Would you like to be an astronaut?",
"type": "SINGLE",
"answersAvailable": [{
"id": 4,
"title": "Yes"
},
{
"id": 5,
"title": "No"
},
{
"id": 6,
"title": "I am not sure"
}
],
"answer": [
4
]
},
{
"id": 3,
"title": "What is your favourite planet?",
"type": "MULTIPLE",
"answersAvailable": [{
"id": 7,
"title": "Earth"
},
{
"id": 8,
"title": "Mars"
},
{
"id": 9,
"title": "Jupiter"
}
],
"answer": [
7,
8
]
}
]
$scope.initMultiple = function(item) {
item.answersAvailable.forEach(function(option) {
option.selected = item.answer.indexOf(option.id) != -1;
});
}
$scope.updateAnswer = function(item) {
item.answer = item.answersAvailable.filter(function(option) {
return option.selected;
})
.map(function(option) {
return option.id;
});
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<div ng-app='test' ng-controller='Test'>
<div ng-repeat="i in items">
<select ng-model="i.answer[0]"
ng-options="o.id as o.title for o in i.answersAvailable"
ng-if="i.type == 'SINGLE'"></select>
<label ng-repeat="o in i.answersAvailable"
ng-if="i.type == 'MULTIPLE'"
ng-init="initMultiple(i)">
<input type="checkbox"
ng-model="o.selected"
ng-change="updateAnswer(i)" /> {{o.title}}
</label>
<div>{{i.answer}}</div>
</div>
</div>
Based on my experience, I will make a separation as two Angular models (or usually called services) for the form questions and another one which will collect the answers and eventually will be passed to the backend for further processing. This will provide me a flexibility to maintain both logic and presentation.
var myModule = angular.module('myModule', []);
myModule.factory('QuestionsFormService', function() {
var _question1;
var _question2;
var _question3;
function init(data){
//questions initiation
}
return init;
});
var myModule = angular.module('myModule', []);
myModule.factory('FormDataService', function() {
var _dataAnswer = {}
function init(){
//data initialization
}
function insertData(key, value){
_dataAnswer[key] = value
}
return init;
});
From the example of service models above, you need to make these available to your presentation through the Angular controller with Dependency Injection.
myModule.controller("MyCtrl", function($scope, FormDataService, QuestionsFormService) {
$scope.form_questions = QuestionsFormService.init();
$scope.form_answers = FormDataService.init()
//further logic to make these available on your view on your convenience
});
What you write on the HTML page as an Angular view is already close enough. You only need to change the binding to two models as I propose above. Thank you.

tags-input show a a element of an array

hi i have a JSON like this:
pages[
{
"id": "74682309",
"labels": [
{
"term": "test1",
"probability": 0.069
},
{
"term": "test2",
"probability": 0.037
}
]
}];
and using tags-input i want the tags to read only the term and show the term so i can show and update.
i have
<tags-input ng-model="se.labels"></tags-input>
the 'se' comes from ng-repeat="se in searchCtrl.pages
Based on the documentation (http://mbenford.github.io/ngTagsInput/documentation/api) you can change the keyProperty and displayProperty to make it use "term" instead of "text"
I've created a fiddle to demonstrate how you can obtain the data needed, considering that the provided JSON is a valid JSON. Your JSON is invalid.
var myApp = angular.module('myApp',['ngTagsInput']);
myApp.factory('data', function() {
var data = [
{
"id": "74682309",
"labels": [
{
"text": "test1",
"probability": 0.069
},
{
"text": "test2",
"probability": 0.037
}
]
}];
return data;
});
myApp.controller('MyCtrl', ['$scope', 'data', function($scope, data) {
var values = [];
data.map(function(elem) {
return elem.labels.forEach(function(el) {
values.push({
"text" : el.text
});
});
});
$scope.tags = values;
}]);
And the html part:
<div ng-controller="MyCtrl">
<div class="elem">
<tags-input ng-model="tags"></tags-input>
</div>
</div>
Here is the fiddle:
http://jsfiddle.net/HB7LU/16554/
Update:
You haven't included the angular ng-tags-input extension as a tag into your question. Please see my updated fiddle:
http://jsfiddle.net/HB7LU/16557/

Bind scope property to tab title

I am new to angularJS and I am having trouble with binding properties of scope so two way binding can work properly. I am using sample code to generate tabs.
<div ng-app="SampleApp">
<div id="tabs" ng-controller="GridController as gridcon">
<ul>
<li ng-repeat="tab in tabs"
ng-class="{active:isActiveTab(tab.url)}"
ng-click="onClickTab(tab)">{{tab.title}}</li>
</ul>
<div id="mainView">
<div ng-include="currentTab"></div>
</div>
<input type="button" ng-click="changedata()" value="Check" />
</div>
</div>
Now my scope have two observable arrays and I want to show count of those arrays in tab title. I am using following code for controller.
var appRoot = angular.module('SampleApp', ["kendo.directives"]);
appRoot.controller('GridController', ['$scope', function ($scope) {
$scope.data1 = new kendo.data.ObservableArray([
{
issueId: 1,
issue: "County Incorrect"
},
{
issueId: 2,
issue: "City Incorrect"
},
{
issueId: 3,
issue: "Name Incorrect"
}
]);
$scope.data2 = new kendo.data.ObservableArray([
{
"id": 11,
"first_name": "James",
"last_name": "Butt",
"company_name": "Benton, John B Jr",
"address": "6649 N Blue Gum St",
"city": "New Orleans",
"county": "Bridgepoort",
"state": "LA",
"zip": 70116,
"phone1": "504-621-8927",
"phone2": "504-845-1427",
"email": "jbutt#gmail.com",
"web": "http://www.bentonjohnbjr.com"
},
{
"id": 12,
"first_name": "Josephine",
"last_name": "Darakjy",
"company_name": "Chanay, Jeffrey A Esq",
"address": "4 B Blue Ridge Blvd",
"city": "Brighton",
"county": "Livingston",
"state": "MI",
"zip": 48116,
"phone1": "810-292-9388",
"phone2": "810-374-9840",
"email": "josephine_darakjy#darakjy.org",
"web": "http://www.chanayjeffreyaesq.com"
}
]);
$scope.tabs = [{
title: 'Issue List (0)'.replace("0", $scope.data1.length),
url: 'tab1.html'
}, {
title: 'Corrected (0)'.replace("0", $scope.data2.length),
url: 'tab2.html'
}];
$scope.currentTab = 'tab1.html';
$scope.onClickTab = function (tab) {
$scope.currentTab = tab.url;
}
$scope.isActiveTab = function (tabUrl) {
return tabUrl == $scope.currentTab;
}
$scope.changedata = function () {
$scope.data1.pop();
$scope.data2.pop();
console.log($scope.data1.length);
console.log($scope.data2.length);
}
}]);
Now this works fine when you are loading page for first time. Now on a button click ("Check" button), I am modifying the arrays. What should I do so that tab title is always in sync with length of arrays ? I have tried observable objects but unless I am using binding in view, they will just notify the event. Is there no other way except handling change event of observable arrays ?
you can Copy this piece of code inside the $scope.changedata function.
$scope.tabs = [{
title: 'Issue List (0)'.replace("0", $scope.data1.length),
url: 'tab1.html'
}, {
title: 'Corrected (0)'.replace("0", $scope.data2.length),
url: 'tab2.html'
}];
Also it remains outside as well.

How to set a selected option of a dropdown list control using angular JS

I am using Angular JS and I need to set a selected option of a dropdown list control using angular JS. Forgive me if this is ridiculous but I am new with Angular JS
Here is the dropdown list control of my html
<select ng-required="item.id==8 && item.quantity > 0" name="posterVariants"
ng-show="item.id==8" ng-model="item.selectedVariant"
ng-change="calculateServicesSubTotal(item)"
ng-options="v.name for v in variants | filter:{type:2}">
</select>
After it gets populated I get
<select ng-options="v.name for v in variants | filter:{type:2}" ng-change="calculateServicesSubTotal(item)"
ng-model="item.selectedVariant" ng-show="item.id==8" name="posterVariants"
ng-required="item.id==8 && item.quantity > 0" class="ng-pristine ng-valid ng-valid-required">
<option value="?" selected="selected"></option>
<option value="0">set of 6 traits</option>
<option value="1">5 complete sets</option>
</select>
How can I set the control for value="0" to be selected?
I hope I understand your question, but the ng-model directive creates a two-way binding between the selected item in the control and the value of item.selectedVariant. This means that changing item.selectedVariant in JavaScript, or changing the value in the control, updates the other. If item.selectedVariant has a value of 0, that item should get selected.
If variants is an array of objects, item.selectedVariant must be set to one of those objects. I do not know which information you have in your scope, but here's an example:
JS:
$scope.options = [{ name: "a", id: 1 }, { name: "b", id: 2 }];
$scope.selectedOption = $scope.options[1];
HTML:
<select data-ng-options="o.name for o in options" data-ng-model="selectedOption"></select>
This would leave the "b" item to be selected.
I don't know if this will help anyone or not but as I was facing the same issue I thought of sharing how I got the solution.
You can use track by attribute in your ng-options.
Assume that you have:
variants:[{'id':0, name:'set of 6 traits'}, {'id':1, name:'5 complete sets'}]
You can mention your ng-options as:
ng-options="v.name for v in variants track by v.id"
Hope this helps someone in future.
If you assign value 0 to item.selectedVariant it should be selected automatically.
Check out sample on http://docs.angularjs.org/api/ng.directive:select which selects red color by default by simply assigning $scope.color='red'.
i see here already wrote good answers, but sometime to write the same in other form can be helpful
<div ng-app ng-controller="MyCtrl">
<select ng-model="referral.organization" ng-options="c for c in organizations"></select>
</div>
<script type='text/javascript'>
function MyCtrl($scope) {
$scope.organizations = ['a', 'b', 'c', 'd', 'e'];
$scope.referral = {
organization: $scope.organizations[2]
};
}
</script>
Simple way
If you have a Users as response or a Array/JSON you defined, First You need to set the selected value in controller, then you put the same model name in html. This example i wrote to explain in easiest way.
Simple example
Inside Controller:
$scope.Users = ["Suresh","Mahesh","Ramesh"];
$scope.selectedUser = $scope.Users[0];
Your HTML
<select data-ng-options="usr for usr in Users" data-ng-model="selectedUser">
</select>
complex example
Inside Controller:
$scope.JSON = {
"ResponseObject":
[{
"Name": "Suresh",
"userID": 1
},
{
"Name": "Mahesh",
"userID": 2
}]
};
$scope.selectedUser = $scope.JSON.ResponseObject[0];
Your HTML
<select data-ng-options="usr.Name for usr in JSON.ResponseObject" data-ng-model="selectedUser"></select>
<h3>You selected: {{selectedUser.Name}}</h3>
It can be usefull. Bindings dose not always work.
<select id="product" class="form-control" name="product" required
ng-model="issue.productId"
ng-change="getProductVersions()"
ng-options="p.id as p.shortName for p in products">
</select>
For example. You fill options list source model from rest-service. Selected value was known befor filling list and was set. After executing rest-request with $http list option be done. But selected option is not set. By unknown reasons AngularJS in shadow $digest executing not bind selected as it shuold be. I gotta use JQuery to set selected. It`s important! Angular in shadow add prefix to value of attr "value" for generated by ng-repeat optinos. For int it is "number:".
$scope.issue.productId = productId;
function activate() {
$http.get('/product/list')
.then(function (response) {
$scope.products = response.data;
if (productId) {
console.log("" + $("#product option").length);//for clarity
$timeout(function () {
console.log("" + $("#product option").length);//for clarity
$('#product').val('number:'+productId);
//$scope.issue.productId = productId;//not work at all
}, 200);
}
});
}
Try the following:
JS file
this.options = {
languages: [{language: 'English', lg:'en'}, {language:'German', lg:'de'}]
};
console.log(signinDetails.language);
HTML file
<div class="form-group col-sm-6">
<label>Preferred language</label>
<select class="form-control" name="right" ng-model="signinDetails.language" ng-init="signinDetails.language = options.languages[0]" ng-options="l as l.language for l in options.languages"><option></option>
</select>
</div>
This is the code what I used for the set selected value
countryList: any = [{ "value": "AF", "group": "A", "text": "Afghanistan"}, { "value": "AL", "group": "A", "text": "Albania"}, { "value": "DZ", "group": "A", "text": "Algeria"}, { "value": "AD", "group": "A", "text": "Andorra"}, { "value": "AO", "group": "A", "text": "Angola"}, { "value": "AR", "group": "A", "text": "Argentina"}, { "value": "AM", "group": "A", "text": "Armenia"}, { "value": "AW", "group": "A", "text": "Aruba"}, { "value": "AU", "group": "A", "text": "Australia"}, { "value": "AT", "group": "A", "text": "Austria"}, { "value": "AZ", "group": "A", "text": "Azerbaijan"}];
for (var j = 0; j < countryList.length; j++) {
//debugger
if (countryList[j].text == "Australia") {
console.log(countryList[j].text);
countryList[j].isSelected = 'selected';
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<label>Country</label>
<select class="custom-select col-12" id="Country" name="Country" >
<option value="0" selected>Choose...</option>
<option *ngFor="let country of countryList" value="{{country.text}}" selected="{{country.isSelected}}" > {{country.text}}</option>
</select>
try this on an angular framework
JS:
$scope.options = [
{
name: "a",
id: 1
},
{
name: "b",
id: 2
}
];
$scope.selectedOption = $scope.options[1];

Resources