How to programmatically select ng-option value? - javascript

I have a view that is filled with dropdownlists to filter a report. I also have a view of saved filters that are displayed as links. When a user clicks on their saved filters, I want the appropriate values of the dropdownlists to be selected. The drop downs are being populated properly. On the saved filter link there is an ng-click that will call a function that iterates through the collection of saved filter values and automatically selects the correct one. I cannot figure out how to programmatically set the selected option. Any help is much appreciated!
<select uid="locSelect"
class="span12"
ng-model="reportDetail.selectedLoc"
ng-options="loc.dbid as loc.serviceName for loc in reportDetail.locList | orderBy:'name'">
<option uid="unselectedLocOption" value="">-- Select One --</option>
</select>
Here is the list of saved filters:
<div class=" well fixed-search" style="overflow-x: hidden;overflow-y: auto;">
<div class="well-header">
Saved Filters
</div>
<div ng-if="!hasSavedFilters">
<span>No saved filters</span>
</div>
<ul ng-if="hasSavedFilters" class="nav nav-list dashboard-list">
<li ng-repeat="filter in reportDetail.savedFilters">
<a uid="savedFilter" href="" ng-click="reportDetail.loadSavedFilters(filter.filters)">
<span ng-bind="filter.title"></span>
</a>
</li>
</ul>
And here is my controller
(function(){
'use strict';
var ReportDetailController = function(ReportsService, $scope){
var _locList = {};
var _hospitalStatusList = {};
var _providerStatusList = {};
var _savedFilters = [];
var _sourceTypeList = {};
var _dateRangeList = {};
var _init = function(){
ReportsService.getCurrentReportSavedFilters().then(function(data){
$scope.reportDetail.savedFilters =data;
$scope.hasSavedFilters = ReportsService.hasSavedFilters();
});
ReportsService.getLOCListForDDL().then(function(data){
$scope.reportDetail.locList = data;
//$scope.reportDetail.selectedLoc = $scope.reportDetail.locList[0];
});
ReportsService.getSelectListData()
.then(function(data){
$scope.reportDetail.sourceTypeList = data.CONNECTION_TARGET_STATUS;
$scope.reportDetail.hospitalStatusList = data.CONNECTION_SOURCE_STATUS;
});
ReportsService.getDateRangesForDDL()
.then(function(data){
$scope.reportDetail.dateRangeList = data;
});
$scope.reportDetail.providerStatusList = ReportsService.getProviderStatusForDDL();
};
var _loadSavedFilters = function(filters){
for(var i = 0, l = $scope.reportDetail.locList.length; i<l; i++){
if($scope.reportDetail.locList[i].serviceName == filters.levelOfCare){
$scope.reportDetail.selectedLoc = $scope.reportDetail.locList[i];
console.log($scope.reportDetail.selectedLoc);
}
}
}
var _isActive = function(filter){
for(var i = 0, l = $scope.reportDetail.savedFilters.length; i<l; i++){
if(filter.title == $scope.reportDetail.savedFilters[i].title){
return true;
}
return false;
}
}
var _generateReport = function(){
return ReportsService.generateReport();
};
$scope.reportDetail = {
init: _init,
selectedLoc: null,
isActive: _isActive,
locList: _locList,
selectedHospitalStatus: 'NOTIFIED',
hospitalStatusList: _hospitalStatusList,
selectedProviderStatus: 'NEW',
providerStatusList: _providerStatusList,
selectedSourceType: 'CONNECTED',
sourceTypeList: _sourceTypeList,
selectedDateRange: '',
dateRangeList: _dateRangeList,
savedFilters: _savedFilters,
loadSavedFilters: _loadSavedFilters,
generateReport: _generateReport
};
$scope.reportDetail.init();
};
app.controller('ReportDetailController', ['ReportsService', '$scope', ReportDetailController]);
})();

You just need to set the ng-model to whatever it should be, so in this case you would set reportDetail.selectedLoc to whatever loc.dbid it should be.
For example: http://jsfiddle.net/uWLua/1/
Note: Make sure they have the same type, so in your example make sure they are either both integers, or both strings, it will not know they are the same if you have one as 5073 and one as "5073"
I updated the fiddle to show that the string and number do not do the same thing.

The ng-model and the expression feeding ng-options -must- match in order for Angular to compare values and see what option is 'selected'. Just as 'dave' indicated.

Due to time constraints I ended up going a different route. I created an event bus of sorts in my service layer and subscribe to the even in my controller, updating the model, and used ng-repeat with ng-selected.
I'm still interested to understand why this was not working with ng-options. The model and ng-options types matched, and everything appeared to be wired up correctly. When I have more time i'll re-address the original issue. Thanks for all who responded!

You need custom directive, or something similar to this two approaches
<div ng-controller="MyCtrl">
<h1>Approach 1</h1>
<span ng-repeat="val in dbs">
<input type="checkbox" ng-model="val.checked">{{val.name}}
</span>
<hr/>
<h1>Approach 1</h1>
<select multiple>
<option ng-repeat="val in dbs" name="val.name" value="val.name" ng-selected="val.checked">{{val.name}}</option>
</select>
<h4>Source (note scope changes)</h4>
{{dbs}}
</div>
also you can use ng-change to do some complex ops

If I understand, in summary, you have a select filled with a list, and you want to programmatically set one of those to be selected, type it as the default right?
If so, you can easily solve this with ng-options, just associate your controller instance with scope and assign the position of the list you want to the model of select, for example:
Select HTML
<select ng-model="vm.aluno_id" name="aluno_id" ng-options="aluno.nome for aluno in alunos">
Controller
app.controller("auxiliarController", function( $scope){
//instancia controller;(Controller instance;)
var vm = this;
$scope.vm = vm;
//carregando lista no scope, que serĂ¡ utilizado pelo angular no select
//Loading list in scope, which will be used by angular in select
$scope.alunos = [{id: 1, nome: "aa"}, {id: 2, nome: "bb"}];
//setando item default no select
$scope.vm.aluno_id = $scope.alunos[0];
});
I hope I have helped

Related

Cascading selects AngularJS filter not display data in second time

Considering the code below:
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script>
</head>
<body ng-controller="appCtrl">
<div class="filters">
<div>
Color: <select id="color" ng-model="colorfilter" ng-options="car.color as car.color for car in cars | unique:'color'" ng-change="changeColor()">
</select>
Model: <select id="model" ng-disabled="!colorfilter" ng-model="modelfilter" ng-options="car.model as car.model for car in cars | filter:{color:colorfilter} | unique:'model'" ng-change="changeModel()">
</select>
</div>
</div>
<div class="list">
<p class="car" ng-repeat="car in cars | filter:colorfilter | filter:modelfilter">{{car.make}} :: {{car.model}} | {{car.color}}</p>
</div>
</body>
</html>
And the controller.js:
angular.module('app', [])
.filter('unique', function() {
return function(input, key) {
var unique = {};
var uniqueList = [];
for(var i = 0; i < input.length; i++){
if(typeof unique[input[i][key]] == "undefined"){
unique[input[i][key]] = "";
uniqueList.push(input[i]);
}
}
return uniqueList;
};
})
.controller('appCtrl', function($scope) {
// define list of cars
$scope.cars = [
{make:"Dodge", color:"Blue", model:"Dakota"},
{make:"Chevy", color:"Black", model:"Aveo"},
{make:"Honda", color:"Black", model:"Accord"},
{make:"Toyota", color:"Red", model:"Corolla"}
//... other lines
];
// initialize filter object
$scope.filter = {};
});
The list of cars is displayed complete below the select fields. When I filter at the first time, the first select normally filters both the list of cars (considering the selected color) and the second select data (only models that have the selected color). And the second select also filters the list of cars considering the selected model. That sounds perfect!
However, when I try to perform a new filter, when I choose another color, it does not display any records in the list of cars, but it usually works by filtering the data of the second select. By choosing one of the models available in the second select, the list is displayed, exactly the combination of the two selections (such as an AND of the selects).
It seems that only the list data that is currently being viewed on the screen are considered for the first select, needing to wait for the second select to display the result. But my intend is to display the results to each filter, always searching for the complete list of items.
My full code on Codepen.
I think this may be what you're looking for. Based on your ng-disabled logic, I figured you want the user to always go in the order of selecting color then model.
https://codepen.io/Cameron64/pen/YZbrqW?editors=1010
To maintain this order I set the color dropdown to reset when the model dropdown is selected from. Additionally I grouped the filter into one object so that it isn't so verbose when passed to the repeater
$scope.changeColor = function() {
$scope.filter.model = undefined;
$scope.filter.color = $scope.filter.color || undefined;
}
Update:
You need to set the null value for modelFilter to reset the filter.
Your code is working fine however when you select a value in second dropdown, you are setting the value to modelfilter which is causing the issue.
Try to insert an empty dropdown option in both the dropdowns so that when you select first dropdown it will reset to default state and modelFilter won't apply.
Working Demo:
CodePen

Create a dynamic multiple dropdown select

I'm fetching a list of objects from a database, say articles, that have a category attribute, and I'll be adding a filtering capability to my angularjs app where I can select multiple articles based on the subcategories, grouped by category
I'm trying to do as follows in my html:
<select multiple >
<option value="" disabled selected>Choose your option</option>
<optgroup ng-repeat="category in categories" label="{{category.category}}">
<option ng-repeat="subcategory in category.subcategories" value="{{subcategory}}">{{subcategory}}</option>
</optgroup>
</select>
but the categories and subcategories can be diverse and I don't want to hardcode it on my app, rather, grouping that information from all the articles I retrieve from the database, so in my Controller, in the function I use to fetch all the articles I have the following
function getAllArticles(){
var promise = article.getAll();
promise.then(function( articles ){
$scope.articles = articles.data
var result = $scope.articles.map(function(a) {return a.category.category;});
var res = arrayUnique(result);
for(var i = 0; i < res.length; i++){
$scope.categories[i] = {'category': res[i] }
var result2 = $scope.articles.map(function(a) {
if (a.category.category === res[i]) {
return a.category.subcategory;
}
});
$scope.categories[i]['subcategories']
$scope.categories[i]['subcategories'] = arrayUnique(result2);
}
});
}
var arrayUnique = function(a) {
return a.reduce(function(p, c) {
if (p.indexOf(c) < 0 && c != undefined) p.push(c);
return p;
}, []);
};
In a way I'm using map/reduce to get the categories and subcategories, but my problem is that with all of these, in my html, the ng-repeat doesn't show anything, as if the $scope.categories is still empty, even we I console.log it I get the following result:
{ 0: {category: "category1",
subcategories: [{subcategory: "sub1"},{subcategory: "sub2"}]
},
1: {category: "category2",
subcategories: [{subcategory: "sub1"},{subcategory: "sub2"}]
}, ...
}
EDIT:
when I do the following:
<div ng-repeat="category in categories">
{{category.category}}
<div ng-repeat="subcategory in category.subcategories">{{subcategory}}</div>
</div>
It prints as it should the list of categories and subcategories, the main difference is that I'm using <div> instead of <optgroup> <option>
If your console.log is accurate then you are using ng-repeat over an object not an array. This is doable but requires special syntax such as:
<div ng-repeat="(key, value) in myObj"> ... </div>
you can find the documentation here.
Otherwise, try translating the results into an array before using them in ng-repeat.
As a side note, angular offers the ng-options directive for ng-select. That way you can assign a data model to it instead of hard coding a template.
The problem was with materializecss select. It was instantiating before updating $scope.categories, which was empty
The solution can be
$(document).ready(function() {
$('select').material_select();
$('input.select-dropdown').click(function(e){
$('select').material_select();
});
});
solves the problem but it's a horrible hack, but I'll be moving to a timeout solution or instantiating after updating $scope.categories

Update unrelated field when clicking Angular checkbox

I have a list of checkboxes for people, and I need to trigger an event that will display information about each person selected in another area of the view. I am getting the event to run in my controller and updating the array of staff information. However, the view is not updated with this information. I think this is probably some kind of scope issue, but cannot find anything that works. I have tried adding a $watch, my code seems to think that is already running. I have also tried adding a directive, but nothing in there seems to make this work any better. I am very, very new to Angular and do not know where to look for help on this.
My view includes the following:
<div data-ng-controller="staffController as staffCtrl" id="providerList" class="scrollDiv">
<fieldset>
<p data-ng-repeat="person in staffCtrl.persons">
<input type="checkbox" name="selectedPersons" value="{{ physician.StaffNumber }}" data-ng-model="person.isSelected"
data-ng-checked="isSelected(person.StaffNumber)" data-ng-change="staffCtrl.toggleSelection(person.StaffNumber)" />
{{ person.LastName }}, {{ person.FirstName }}<br />
</p>
</fieldset>
</div>
<div data-ng-controller="staffController as staffCtrl">
# of items: <span data-ng-bind="staffCtrl.infoList.length"></span>
<ul>
<li data-ng-repeat="info in staffCtrl.infoList">
<span data-ng-bind="info.staffInfoItem1"></span>
</li>
</ul>
</div>
My controller includes the following:
function getStaffInfo(staffId, date) {
staffService.getStaffInfoById(staffId)
.then(success)
.catch(failed);
function success(data) {
if (!self.infoList.length > 0) {
self.infoList = [];
}
var staffItems = { staffId: staffNumber, info: data };
self.infoList.push(staffItems);
}
function failed(err) {
self.errorMessage = err;
}
}
self.toggleSelection = function toggleSelection(staffId) {
var idx = self.selectedStaff.indexOf(staffId);
// is currently selected
if (idx >= 0) {
self.selectedStaff.splice(idx, 1);
removeInfoForStaff(staffId);
} else {
self.selectedStaff.push(staffId);
getStaffInfo(staffId);
}
};
Thanks in advance!!
In the code you posted, there are two main problems. One in the template, and one in the controller logic.
Your template is the following :
<div data-ng-controller="staffController as staffCtrl" id="providerList" class="scrollDiv">
<!-- ngRepeat where you select the persons -->
</div>
<div data-ng-controller="staffController as staffCtrl">
<!-- ngRepeat where you show persons info -->
</div>
Here, you declared twice the controller, therefore, you have two instances of it. When you select the persons, you are storing the info in the data structures of the first instance. But the part of the view that displays the infos is working with other instances of the data structures, that are undefined or empty. The controller should be declared on a parent element of the two divs.
The second mistake is the following :
if (!self.infoList.length > 0) {
self.infoList = [];
}
You probably meant :
if (!self.infoList) {
self.infoList = [];
}
which could be rewrited as :
self.infoList = self.infoList || [];

Options Binding overrides initial View Model value

I tried to follow the other similar questions on Stack Overflow but thus far have been unsuccessful in fixing the problem.
I am using jQuery AJAX to retrieve several items: a contact and its associated information, all available salutation types, all available email types and all available phone types.
I have successfully bound the options to the select boxes. However, it appears to overwrite the 'value' binding that holds the initial view model value.
Could any of you help me solve this? Please let me know if you have any questions for clarification.
Please see the code below:
View Model:
function contactPageViewModel() {
var self = this;
self.contact = ko.observable();
self.availableSalutations = ko.observableArray();
self.availableEmailTypes = ko.observableArray();
self.availablePhoneTypes = ko.observableArray();
self.availableAddressTypes = ko.observableArray();
}
where contact is an object coming from the server, which includes the element contact.salutation.
The json coming back for contact is:
{
//...
"createdBy":null,
"createdOn":1392848929000,
"updatedBy":null,
"updatedOn":1392848929000,
"contactId":305,
"salutation":{"salutationId":102,"salutation":"Mrs."},
"firstName":"Laura",
"middleInitial":"K",
"lastName":"Ritchey"
//...
}
the json coming back from availableSalutations (which is a property of a json object wrapper 'listObject') is:
[{"salutationId":41,"salutation":"Ms."},
{"salutationId":101,"salutation":"Mr."},
{"salutationId":66,"salutation":"CDR"},
{"salutationId":81,"salutation":"LCDR"},
{"salutationId":102,"salutation":"Mrs."},
{"salutationId":121,"salutation":"Mr."},
{"salutationId":64,"salutation":"LTC"}]
The code to map the JSON result to the knockout observables:
contactPageViewModel.contact = ko.mapping.fromJS(data.listObject[0]);
contactPageViewModel.availableEmailTypes = ko.mapping
.fromJS(data.listObject[1]);
contactPageViewModel.availableSalutations = ko.mapping
.fromJS(data.listObject[2]);
....
applyBindings();
The HTML:
<label for="rank"> Rank / Title: </label>
<select data-bind="optionsText: 'salutation',
options: availableSalutations,
value: contactPageViewModel.contact.salutation"
class="rankList"
name="Rank"
id="rankSelect">
</select>
Try value: $root.contact().salutation instead of value: contactPageViewModel.contact.salutation.
Or:
<label for="rank"> Rank / Title: </label>
<!-- ko with: contact -->
<select data-bind="options: $root.availableSalutations, optionsText: 'salutation', value: salutation" class="rankList" name="Rank" id="rankSelect">
</select>
<!-- /ko -->
Update:
You could look at this Fiddle. May be it contains a lot of excess code and you can simplify it, but the main things is to separate initial and selected salutations and add optionsCaption to select bindings:
var initialSalutation = new salutationViewModel(data.salutation);
And:
self.salutation = ko.observable();
self.displayedSalutation = ko.computed(function () {
if (self.salutation()) {
return self.salutation();
} else {
return initialSalutation;
}
})
Update 2:
Look at this Fiddle. I've added optionsValue: 'salutationId' to select bindings and move displayedSalutation to contactPageViewModel.
I think problem was with matching objects (select item and salutation from contact). When value of select is salutationId and contact salutation also salutationId (number value, not object) all working good.

Passing values to ko.computed in Knockout JS

I've been working for a bit with MVC4 SPA, with knockoutJs,
My problem is I want to pass a value to a ko.computed. Here is my code.
<div data-bind="foreach: firms">
<fieldset>
<legend><span data-bind="text: Name"></span></legend>
<div data-bind="foreach: $parent.getClients">
<p>
<span data-bind="text: Name"></span>
</p>
</div>
</fieldset>
</div>
self.getClients = ko.computed(function (Id) {
var filter = Id;
return ko.utils.arrayFilter(self.Clients(), function (item) {
var fId = item.FirmId();
return (fId === filter);
});
});
I simply want to display the Firmname as a header, then show the clients below it.
The function is being called, but Id is undefined (I've tried with 'Firm' as well), if I change:
var filter = id; TO var filter = 1;
It works fine,
So... How do you pass a value to a ko.computed? It doesn't need to be the Id, it can also be the Firm object etc.
Each firm really should be containing a list of clients, but you could use a regular function I think and pass it the firm:
self.getClientsForFirm = function (firm) {
return ko.utils.arrayFilter(self.Clients(), function (item) {
var fId = item.FirmId();
return (fId === firm.Id());
});
});
Then in html, $data is the current model, in your case the firm:
<div data-bind="foreach: $root.getClientsForFirm($data)">
Knockout doesn't allow you pass anything to a computed function. That is not what it is for. You could instead just use a regular function there if you'd like.
Another option is to have the data already in the dataset on which you did the first foreach. This way, you don't use $parent.getClients, but more like $data.clients.

Categories