AngularJS two-way data binding not working properly in directive - angularjs

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'
}

Related

Angular 1.5 ng-change on select element in component triggering previous response

I'm just starting to play with components in Angular 1.5.x and have created one for a select box on a form - I pass in the field, list and label to the component and it creates the form 'part' consisting of the label and select box with the correct options - so far, so good.
Now I want to implement an ng-change on the select so I am passing in the function I want to use for this. The problem I have is that the function is triggering with the old value for the select not the new value. I can see that the function is triggering prior to the actual change. If I put the change function within the component change event it registers correctly, but not using a passed in function.
I have created a cut-down fiddle at https://jsfiddle.net/7gd3m2k0/
<div ng-app="demoApp">
<div ng-controller="RoleController as ctrl">
<nac-select
field="ctrl.MemberRole.record.member"
list="ctrl.Lists.Member"
label="Member"
on-change="ctrl.MemberRole.changeMember();"></nac-select>
<div ng-bind="ctrl.MemberRole.record.member.name"></div>
</div>
</div>
angular.module('demoApp', [])
.component('nacSelect', nacSelect)
.controller('RoleController', roleController)
var nacSelect = {
bindings: {
field: '=',
list: '<',
label: '#label',
onChange: '&'
},
controller: function () {
var ctrl = this;
ctrl.change = function () {
alert(ctrl.field.name); //gives new selection
ctrl.onChange(); //gives previous selection
}
},
template: `
<label>{{$ctrl.label}}</label>
<select ng-model="$ctrl.field" ng-options="r as r.name for r in $ctrl.list" ng-change="$ctrl.change();">
<option value="">(Please Select)</option>
</select>
`
};
var roleController = function(){
var ctrl = this;
ctrl.Lists = {
Member: [
{id: 1, name: 'Fred Smith'},
{id: 2, name: 'Jenny Jones'},
{id: 3, name: 'Jane Doe'}
]
};
ctrl.MemberRole = {
record: {
member: null
},
changeMember: function(){
alert(ctrl.MemberRole.record.member.name);
}
};
};
I guess I am missing something simple, but I can't figure it out. Thank you for your assistance
You need to pass in variable that you want to alert in the first controller, check this:
https://jsfiddle.net/pegla/7gd3m2k0/1/
so your function would look like this:
changeMember: function(name){
alert(name);
}
when you're using nac-select
<nac-select field="ctrl.MemberRole.record.member" list="ctrl.Lists.Member" label="Member" on-change="ctrl.MemberRole.changeMember(name);"></nac-select>
and in the end in the nac-select:
<select ng-model="$ctrl.field" ng-options="r as r.name for r in $ctrl.list | orderBy: 'name'" ng-required="$ctrl.req" ng-if="!$ctrl.list.processing" ng-change="$ctrl.onChange($ctrl.field)">
or if you want to pass in object:
<nac-select field="ctrl.MemberRole.record.member" list="ctrl.Lists.Member" label="Member" on-change="ctrl.MemberRole.changeMember({id, name});"></nac-select>
then your changeMember can look like this:
changeMember: function(obj){
alert(`${obj.name} ${obj.id}`);
}
New fiddle:
https://jsfiddle.net/pegla/7gd3m2k0/2/

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.

Get Selected Object Of Kendo Drop Down List

I am using the Kendo Drop Down List. More specifically, I'm using Kendo Angular directives. Currently, I have the following in my markup:
<input id='myDropDownList' kendo-drop-down-list ng-model="selectedSport" k-data-source="sports" k-data-text-field="'name'" />
<button ng-click='send()'>Submit</button>
My controller has the following:
$scope.selectedSport = null;
$scope.sports: [
{ id: 1, name: 'Basketball' },
{ id: 2, name: 'Football' },
{ id: 3, name: 'Tennis' }
];
$scope.send = function () {
alert($scope.selectedSport);
};
When I run this code, I get the selectedSport ID. However, I want the entire object. Every other StackOverflow post I've found uses retrieves the ID. For the life of me, I can't figure out how to get the object. Does anyone know how to get the selected JSON object instead of just the id?
Thanks!
This answer is probably outdated for current versions of the Kendo Angular bindings.
As mentioned in hally9k's answer, there is now an attribute k-ng-model that can handle this, so you would use
k-ng-model="selectedSport"
in place of
ng-model="selectedSport"
Previous answer below; this may or may not still be relevant in case you're using an older version of Kendo UI:
I don't think you can configure the kendo widget to store the dataItem directly - underneath it all is still a <select> with a primitive value. So you'll probably have to get the dataItem from the widget's data source, e.g. like this:
HTML:
<div ng-controller="MyController">
<select id='myDropDownList' kendo-drop-down-list ng-model="selectedSport" k-data-source="sports" k-data-value-field="'id'" k-data-text-field="'name'"></select>
<button ng-click='send()'>Submit</button>
</div>
JS:
function MyController($scope) {
$scope.selectedSport = null;
$scope.sports = new kendo.data.DataSource({
data: [{
id: 1,
name: 'Basketball'
}, {
id: 2,
name: 'Football'
}, {
id: 3,
name: 'Tennis'
}]
});
$scope.send = function () {
var dataItem = $scope.sports.get($scope.selectedSport);
console.log(dataItem);
};
}
You could, however, create your own directive for kendoDropDownList which uses a k-data-item attribute (for example) and use it like this:
HTML:
<select id='date' k-ddl k-data-source="sports" k-data-text-field="name" k-data-item="dataItem">
JS:
var app = angular.module('Sample', ['kendo.directives']).directive("kDdl", function () {
return {
link: function (scope, element, attrs) {
$(element).kendoDropDownList({
dataTextField: attrs.kDataTextField,
dataValueField: "id",
dataSource: scope[attrs.kDataSource],
change: function () {
var that = this;
var item = that.dataItem();
scope.$apply(function () {
scope[attrs.kDataItem] = item.toJSON();
});
}
});
}
};
});
function MyController($scope) {
$scope.sports = [{
id: 1,
name: 'Basketball'
}, {
id: 2,
name: 'Football'
}, {
id: 3,
name: 'Tennis'
}];
$scope.dataItem = $scope.sports[0];
$scope.send = function () {
console.log($scope.dataItem);
};
}
That way, you could keep the controller free from Kendo UI DataSource-specific code and instead only work with JS data types. (see JSBin)
Using k-ng-model will bind the dataItem instead of just the text value:
<input id='myDropDownList' kendo-drop-down-list k-ng-model="selectedSport" k-data-source="sports" k-data-text-field="'name'" />
I know this is an old question, but you could use the select event of the dropdownlist to get the underlying json object:
select: function (e) {
var item = this.dataItem(e.item.index());
...
}
You would then store the json object (item variable above) from the select event so that you can get to it from your submit method. There is probably a way to get the selected json object without having to use the select event as well.
The proper way to get the text value is to use the 'k-select' event of Kendos dropdownlist:
<select kendo-drop-down-list k-select="vm.test" k-data-text-field="'groupName'" k-data-value-field="'id'" k-data-source="vm.groupList" ng-model="vm.groupId"></select>
Then in your angular controller expose the 'test' function (example assumes you are using 'controller as vm'):
function DocumentTypeController() {
var vm = this;
vm.test = test;
vm.groupId = null;
function test(dropdown) {
alert('text is:' + dropdown.item.text());
}
}
I hope that helps.

Saving a checkbox list in Angularjs with node

I want to save and load a checkbox list using binding in angularjs with a node backend. This SO question (How do I bind to list of checkbox values with AngularJS?) answers how I can load the check box from a static javascript object but how can I save the checkbox values after the user selects them?
I want to save the checkbox values in a single field but how can I tell angular to bind the checkbox values into a single field defined in a model to my mongodb? I cant just use ng-model as there are multiple checkboxes.
Needless to say that I am new to angular so any help here is greatly appreciated ...
Thanks for any help you can provide.
kseudo
Just add ng-model to your checkbox. By this way you can update model in controller on any checkbox state change.
Here is example:
HTML
<div ng-controller="Ctrl">
<label ng-repeat="fruit in fruits">
<input
type="checkbox"
name="fruit.name"
ng-model="fruit.value"
> {{fruit.name}}
</label>
<pre>{{fruits| json}}</pre>
</div>
JS
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.fruits = [{
name: 'apple',
value: false
}, {
name: 'orange',
value: false
}, {
name: 'pear',
value: false
}, {
name: 'naartjie',
value: false
}];
}
Demo Fiddle
[EDIT]
BTW we can make the copy by using angular.copy() method. When we press button, the new copy of fruits model will be created (and you should send it to server as json). Old model fruits will stay the same:
$scope.fruitsCopy = [];
$scope.init = function(){
$scope.fruitsCopy = angular.copy($scope.fruits );
}
To convert data to JSon I would use:
var jsonData = JSON.stringify($scope.fruitsCopy);
Demo2 Fiddle
Let's say you defined your model as such:
function Ctrl($scope) {
$scope.items = [{
name: 'A',
checked: false
}, {
name: 'B',
checked: false
}, {
name: 'C',
checked: false
}, {
name: 'D',
checked: false
}];
}
And created a view for the model:
<ul>
<li ng-repeat="item in items">
<label>
<input type="checkbox" ng-model="item.checked">
{{item.name}}
</label>
</li>
</ul>
<button ng-click="save()">save</button>
Next you have to define save function:
$scope.save = function() {
//angular.toJson converts array to string, something like
// '[{"name":"A","checked":false},{"name":"B","checked":true},...]'
var json = angular.toJson($scope.items);
//Service is angular service for your model that makes http requests to your backend
Service.save(json).then(function(){
//here you can notify user that data is persisted
}, function() {
//here you can notify user that there was a problem with request
});
}
And a simple model service:
.service('Service', function($http) {
return new function() {
this.save = function(data) {
return $http.post('url/to/backend', data);
}
}
});

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