Handle selectbox update in Angular directive - angularjs

uiModel.appreciation is a key / value object:
{ "key": "B", "value": "Better"}
In my Directive:
scope.appreciationsArray = [{key: 'A', value: 'Good'}, {key: 'B', value: 'Better'}, {key: 'C', value: 'Best'}];
Directive template:
<select name="type" ng-change="onChangeAppreciationSelectBox($index)" class="form-control" ng-disabled="disabled" required ng-model="uiModel.appreciation.key" data-ng-options="appreciationObj.key as appreciationObj.value for appreciationObj in appreciationsList">
</select>
This renders a nice select box and sets the selected item to B / Better.
When changing to another item I want the uiModel.appreciation object to be updated accordingly. So far only the key is updated, the value propertyhowever keeps its initial value.
Looking for the correct Angular way to do it, must be possible without the use of custom Javascript right?

Use the appreciation as the model and as the selector in the comprehension expression instead of the key property:
<select
ng-model="uiModel.appreciation"
ng-options="appreciation.value for appreciation in appreciations">
angular.module('MyModule', [])
.controller('MyController', function($scope) {
$scope.uiModel = {
appreciation: {
"key": "B",
"value": "Better"
}
}
$scope.appreciations = [{
key: 'A',
value: 'Good'
}, {
key: 'B',
value: 'Better'
}, {
key: 'C',
value: 'Best'
}];
$scope.uiModel = {
appreciation: $scope.appreciations[0]
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='MyModule' ng-controller='MyController'>
<p>Selected appreciation: {{uiModel.appreciation | json}}</p>
<select
ng-model="uiModel.appreciation"
ng-options="appreciation.value for appreciation in appreciations">
</select>
</div>

Related

AngularJS two-way data binding not working properly in directive

i am trying to implement radio-button list using ng-repeat.
typeList.html
<div ng-repeat="type in types" >
<input type="radio" id={{type.id}} name="{{type.name}}" ng-model="result" ng-value="type.id" >
{{type.name}}
<div> Result {{result}} </div> //result is changing only in the row of clicked radio-button. It should change in every row.(two way data-binding).
</div>
Directive:
angular.module('app').directive('myList',function(){
return{
restrict: 'A',
scope: {
types: '=', //here list is passed to be printed with ng-repeat
result: '=' //here I want to store which radio-button was selected last time by id
},
templateUrl: 'html/typeList.html'
};
});
Directive has isolated scope. I am passing two parameters. List to be printed with radio buttons and result object which stores answer(id-what radio button was clicked last time) in parent scope. Unfortunately whenever i click on radio-buttons my result is changing only locally.
Passing parameters to my directive.
<div my-list types="list" result="selected"></div>
Passed list and result paramater from controller to myList directive.
$scope.list = [
{ id: 1, name:'Name 1' },
{ id: 2, name:'Name 2' },
{ id: 3, name:'Name 3' }
];
$scope.selected = -1;
I would be grateful for any help.
You have to pass a non-primitive object to the model to get its reference for two-war binding. Just wrap selected into an object for its reference.
In your controller use.
$scope.list = [{
id: 1,
name: 'Name 1'
}, {
id: 2,
name: 'Name 2'
}, {
id: 3,
name: 'Name 3'
}];
$scope.ctrlModel = {
selected: -1
}
And in the Markup that is 'html/typeList.html'
<div ng-repeat="type in types" >
<input type="radio" id={{type.id}} ng-model="result.selected" ng-value="type.id" >
{{type.name}}
</div>
Result {{result.selected}}
Working Fiddle Demo
Hope it helps.
try to have scope variables as object like
$scope.types = {
list: {},
selected: 'radioValueThatNeedsToBeSelected'
}

Angular Radio Buttons Instead of Select

I'm trying to replace a select with radio buttons - but when I swap to radio buttons I loose my initial selection and the selecting breaks.
$scope.selection = Address.get(); // { id: 1, name: "Home" };
$scope.addresses = Address.query(); // [{ id: 1, name: "Home" }, { id: 2, name: "Work" }];
From (in this sample the select has "Home" selected):
<select class="form-control" ng-model="selection"
ng-options="address.street for address in addresses track by address.id">
</select>
To (in this sample the radio button for "Home" is not selected):
<div ng-repeat="address in addresses track by address.id">
<label>
<input type="radio" ng-value="address" ng-model="selection" />
<span>{{address.name}}</span>
</label>
</div>
Fiddle: https://jsfiddle.net/xczdcqx0/7/
EDIT:
I need the selection to reflect the id and name after changes:
<span>Selection:</span> <span>{{selection.name}} - {{selection.id}}</span>
Try this instead (for you radio input). Turns out that the ng-repeat is what's throwing it off (you needed to use $parent.selection):
<input type="radio" ng-value="address" ng-model="$parent.selection"/>
I think it's working the way you'd hope with this update to your fiddle
Initial Value
Jeez. I feel like an idiot, but I finally figured out that angular has no way of telling that your initial "selection" is the same as address[0].
So - I had to change the way you set $scope.selection as well:
app.controller("SampleController", ['$scope', function($scope) {
$scope.addresses = [
{ id: 1, name: "Home" },
{ id: 2, name: "Work" },
];
$scope.selection = $scope.addresses[0];
}]);
Now we're all set. Sheesh ... that was one of those "hiding in plain sight" bugs...
The value and model should be a string according to the input[radio] docs:
<input type="radio" ng-value="address.name" ng-model="selection.name" />
JSFiddle Demo: **https://jsfiddle.net/xczdcqx0/2/
Source:
<div ng-repeat="address in addresses track by address.id">
<label>
<input type="radio" ng-checked="checked(address)" ng-click="click(address)" />
<span>{{address.name}}</span>
</label>
</div>
Controller:
app.controller("SampleController", ["$scope", "Address", function($scope, Address) {
$scope.selection = Address.get(); // { id: 1, name: "Home" };
$scope.addresses = Address.query(); // [{ id: 1, name: "Home" }, { id: 2, name: "Work" }];
$scope.checked = function(address) {
return address.id === $scope.selection.id;
};
$scope.click = function(address) {
$scope.selection = address;
};
}]);
Note: extracted partially from solutions provided by #bri.

Watching for change in select options

I want to disable an input based on number of elements in it (eg. disable in case of only 1 element). Right now I have my eyes on a directive. The reason I went with a directive is that the ng-options often get quite complicated so it would be annoying to copy/paste them with a comparison into an ng-disabled.
My problem is that I cannot listen to the number of options in the select (or at least I couldn't find a way yet). I.e. my idea would have been to do something like so in the link-function. Basic select (auto-disable being my directive):
<select name="field1" ng-model="data.field1" ng-options="i.value as i.text for i in values" auto-disable >
<option disabled value="" style="display: none;">placeholder</option>
</select>
and the link method being defined as such:
link: function (scope, elem, attrs) {
scope.$watch(elem[0].length, function() {
elem.prop('disabled', elem[0].length <= 1);
});
}
So: Is there some way to watch the elements in a select, or can I somehow access the select's option count easily inside the ng-disabled?
PS: I already tried listening to "data.field1" but that does not work in all cases (eg. if just other values change but the selected value does not).
If I understand what you need here is the JSFiddle to disable the select when there are less than 2 elements, you don't need a directive just look at the array length:
HTML:
<div ng-app ng-controller="MyCtrl">
<select name="field1" ng-model="data.field1" ng-options="i.value as i.text for i in values | filter: { valid: true }" ng-disabled="(values |filter: { valid: true }).length < 2">
</div>
JS:
function MyCtrl($scope) {
$scope.values =
[
{value: 'hello', text: 'world', valid: true},
{value: 'hello2', text: 'world', valid: false},
{value: 'hello3', text: 'world', valid: false},
{value: 'hello4', text: 'world', valid: false},
];
}
EDIT USING DIRECTIVE:
HTML:
<div ng-controller="MyCtrl">
<select id="field1" ng-model="data.field1" ng-options="i.value as i.text for i in values | filter: { valid: true }" auto-disable></select>
</div>
JS DIRECTIVE:
app.directive('autoDisable', function() {
return {
restrict: 'A',
replace: true,
link: function(scope, elm, attrs) {
angular.element(document).ready(function () {
if ((elm[0].options.length - 1) < 2) {
elm.attr("disabled", "disabled");
}
});
}
};
});
Why do you need to subtract 1 from options.length? Because ng-options automatically adds 1 empty option you have to remove from the count.
Hope it helps
Here is the JSFiddle with the directive:

Selecting a default value on select box options with AngularJS

I have read several posts on this issue and the solutions given on this site don't seem to work for me. I have a select box with several options and I need a default value to be show to the user. Currently no option is selected. What could be wrong with this code.
HTML
<select class="form-control input-sm" name="collegeTranscripts1" id="collegeTranscripts1" data-ng- model='transcriptCAdata.typeAccepted' data-ng-options='option.value as option.name for option in typeOptions1'></select>
JS
$scope.typeOptions1 = [
{ name: 'Please Select', value: 'selected' },
{ name: 'Official', value: 'OFFICIAL' },
{ name: 'Unofficial', value: 'UNOFFICIAL' },
{ name: 'None', value: 'NONE' }
];
$scope.form = {type : $scope.typeOptions1[0].value};
There are a couple of problems that I see in your code:
The HTML attribute data-ng-model is separated, it looks like this: data-ng- model='transcriptCAdata.typeAccepted'.
Another is that in your model, you are using a scope attribute transcriptCAdata.typeAccepted but you didn't declare it in your controller. What I see in your controller though is $scope.form = {type : $scope.typeOptions1[0].value};.
You simply have to change your data-ng-model to data-ng-model="form.type".
DEMO
angular.module('app', [])
.controller('Ctrl', function($scope) {
$scope.typeOptions1 = [
{ name: 'Please Select', value: 'selected' },
{ name: 'Official', value: 'OFFICIAL' },
{ name: 'Unofficial', value: 'UNOFFICIAL' },
{ name: 'None', value: 'NONE' }
];
$scope.form = {type : $scope.typeOptions1[1].value};
});
<div ng-app="app" ng-controller="Ctrl">
<select class="form-control input-sm" name="collegeTranscripts1"
id="collegeTranscripts1" data-ng-model='form.type'
data-ng-options='option.value as option.name for option in typeOptions1'>
</select>
{{form.type}}
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
UPDATE:
I have copied the PLUNKER you have provided and revised it, since it had problems in it.

AngularJS ng-options inside custom directive which is inside ng-repeat

Could you help me to find a way to set and get select element values which are placed inside my custom directive.
This is what I have:
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<div ng-repeat="category in categories">
{{category.Name}}:
<status-selector items="statuses" ng-model="selectedStates[status.Id]"></status-selector>
</div>
</div>
</body>
I have two arrays: categories and statuses. Each category can have its own status. When a status for a category is chosen, it should be saved to selectedStatus to finally have something like [{CategoryId:1,StatusId:2},{CategoryId:2,StatusId:3}]. In case if selectedStatus was already initialized, I would like to see chosen statuses for corresponding categories, means that I also need to put values, not just read them.
myApp
.controller("MyCtrl", function($scope) {
$scope.categories = [{Id:1, Name: "Category1"}, {Id: 2, Name: "Category2"}];
$scope.statuses = [{Id: 1, Name: "Low"}, {Id: 2, Name: "Normal"}, {Id: 3, Name: "High"}]
$scope.selectedStates = {};
})
.directive('statusSelector', function() {
return {
restrict: 'E',
replace: true,
scope: { items: '=', ngModel: '='},
template: '<span><select class="select" ng-options="obj.Id as obj.Name for obj in items" ng-model="ngModel"></select></span>',
link: function(scope, element, attrs) {
}
}
});
Thank you.
Demo: Fiddle
You should have your own category model as ng-model. It probably make more sense.
<status-selector items="statuses" ng-model="category.Status"></status-selector>
To set the status, just bring the Status filled in the JSON.
// ...
.controller("MyCtrl", function($scope) {
$scope.categories = [{Id:1, Name: "Category1", Status: 1}, {Id: 2, Name: "Category2"}];
// ...
Obviouslly, to get user's selection, just grab the category.Status property. Here's the updated fiddle.
Also, just a side tip. You seem to come from a .Net background, where you're used to Pascal case. In javascript, it's common sense to use camel case, instead, so you'd have {id: ..., status: ...} instead of {Id: ..., Status: ...}.

Resources