ng-options inside ng-repeat - can't access selected item in $scope - javascript

I want to do the following:
I have an array of all possible items 'all'.
I want to create a subset of that array in 'subset':
js.js:
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.all = [];
$scope.subset = [{name: 'n1', inUse: false}, {name: 'n2', inUse: false}, {name: 'n3', inUse: false} ];
// adding new item in all
$scope.addItem = function() {
var newc = {name: '?', inUse: false};
$scope.all.push(newc);
};
$scope.updateC = function(index) {
// index refers to all array
$scope.all[index].inUse = false;
$scope.all[index] = $scope.selectedItem;
$scope.all[index].inUse = true;
};
});
HTML.html:
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="angular-1.4.8.js"></script>
<script type="text/javascript" src="js.js"></script>
</head>
<body ng-controller="myController">
<button ng-click="addItem()">Add Item: </button>
<div ng-repeat="c in all">
{{c.name}}
<select ng-options="c as c.name for c in subset | filter: {inUse: false}"
ng-change="updateC($index)"
ng-model="selectedItem"></select>
</div>
</body>
</html>
But I'm getting 'TypeError: Cannot set property 'inUse' of undefined'. I see the inUse property in a child scope. Is that behaviour expected? How can I access the selected item in my scope?
I can do the following, but I don't believe it's correct:
var child = $scope.$$childHead;
for (var i = 0; i < index ; i++) {
child = child.$$nextSibling;
}
$scope.all[index] = child.selectedItem;
What is the correct way of doing what I want?

Charlie, you gave me the idea of changing the way I'm adding items to the subset:
JS
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.subset = [];
$scope.all = [{name: 'n1', inUse: false}, {name: 'n2', inUse: false}, {name: 'n3', inUse: false} ];
// adding new item in all
$scope.addItem = function() {
if ($scope.selectedItem != null) {
$scope.selectedItem.inUse = true;
$scope.subset.push($scope.selectedItem);
}
};
});
It's not the same I wanted to do, but it works.
HTML
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="angular-1.4.8.js"></script>
<script type="text/javascript" src="js.js"></script>
</head>
<body ng-controller="myController">
<select ng-options="c2 as c2.name for c2 in all | filter: {inUse: false}"
ng-model="selectedItem"></select>
<button ng-click="addItem()">Add Item: </button>
<div ng-repeat="c in subset">
{{c.name}}
</div>
</body>
</html>

Related

Angular Get Selected Value From Checkbox

I can't seem to get the selected item from an input checkbox.
<ul>
<li ng-repeat='shoe in shoebox'>
<input type='checkbox' ng-model='shoe.selected' id='shoe-{{shoe.name}}'>
<label for='shoe-{{shoe.name}}'>{{shoe.name}}</label>
</li>
<button ng-click='whatIsChecked(shoe.selected)'>Apply</button>
</ul>
Then in my controller:
$scope.whatIsChecked = function(selectedThing) {
console.log(selectedThing);
};
The above returns undefined.
The list items are displayed correctly, but the shoe.name or checked item doesn't seem to be stored by the ng-model.
the variable shoe is only valid in ng-repeat block.
If you want to got what is selected, you should try filter.
UPD:
Since you don't have the selected property, you should keep the selected items somewhere else by firing the ng-click event.
refer below code snippet.
angular.module("app", [])
.controller("myCtrl", function($scope) {
$scope.checkedShoes = [];
$scope.shoebox = [{
name: 'shoe1'
},
{
name: 'shoe2'
},
{
name: 'shoe3'
},
{
name: 'shoe4'
}
];
$scope.save = function(e, shoe) {
if (e.target.checked) {
$scope.checkedShoes.push(shoe);
} else {
var index = $scope.checkedShoes.indexOf(shoe);
if( index > -1) {
$scope.checkedShoes.splice(index, 1);
}
}
};
$scope.whatIsChecked = function() {
//var selected = $scope.shoebox.filter(function(item) {
// return item.selected === true;
//});
console.log($scope.checkedShoes);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-app="app" , ng-controller="myCtrl">
<ul>
<li ng-repeat='shoe in shoebox'>
<input type='checkbox' ng-click="save($event, shoe)" id='shoe-{{shoe.name}}'>
<label for='shoe-{{shoe.name}}'>{{shoe.name}}</label>
</li>
<button ng-click='whatIsChecked()'>Apply</button>
</ul>
</div>
You can get the checked items using a angular.Foreach it becomes easy when you have multiple items checked.
$scope.checkedItems = [];
angular.forEach($scope.shoebox, function(value, key) {
if (value.selected)
$scope.checkedItems.push(value.name);
});
Demo
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.shoebox = [{
name: 'Reboke',
selected: false
}, {
name: 'woodlands',
selected: false
}, {
name: 'Nike',
selected: false
}, {
name: 'Fila',
selected: false
}];
$scope.checkedItems = function() {
$scope.checkedItems = [];
angular.forEach($scope.shoebox, function(value, key) {
if (value.selected)
$scope.checkedItems.push(value.name);
});
}
});
<html>
<head>
<meta charset="utf-8" />
<title>AngularJS </title>
<link rel="stylesheet" href="style.css" />
<script src="https://code.angularjs.org/1.4.7/angular.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<ul>
<li ng-repeat='shoe in shoebox'>
<input type='checkbox' ng-model='shoe.selected' id='shoe-{{shoe.name}}'>
<label for='shoe-{{shoe.name}}'>{{shoe.name}}</label>
</li>
<button ng-click='checkedItems()'>Apply</button>
Checked items are: {{checkedItems}}
</ul>
</body>
</html>

Angular ngModel doesn't fire Kendo change event

I have a Kendo model instance (person for this example) and watching it is modified or not by using dirty property.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Kendo + Angular</title>
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2016.2.714/styles/kendo.common.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2016.2.714/styles/kendo.rtl.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2016.2.714/styles/kendo.default.min.css">
<link rel="stylesheet" href="http://kendo.cdn.telerik.com/2016.2.714/styles/kendo.mobile.all.min.css">
<script src="http://code.jquery.com/jquery-1.12.3.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2016.2.714/js/angular.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2016.2.714/js/jszip.min.js"></script>
<script src="http://kendo.cdn.telerik.com/2016.2.714/js/kendo.all.min.js"></script>
</head>
<body>
<div ng-app="app" ng-controller="MainCtrl">
<div>Person name: {{ person.name }}</div>
<input type="text" name="name" ng-model="person.name"> <!-- This input don't work -->
<button ng-click="foo()">Foo</button> <!-- This button work because I call person.set method manually -->
<div>This person is modified? {{ person.dirty }}</div>
</div>
<script>
var Person = kendo.data.Model.define({
id: "personId", // the identifier of the model
fields: {
"name": {
type: "string"
},
"age": {
type: "number"
}
}
});
angular.module("app", ["kendo.directives"])
.controller("MainCtrl", function ($scope) {
$scope.person = new Person({
name: "John Doe",
age: 42
});
$scope.foo = function () {
$scope.person.set('name', "Kendo");
}
});
</script>
</body>
</html>
But when I type to text box dirty don't change because Angular ngModel doesn't fire Kendo "change" event. My real app have dozens of models like this, so is there any way to fix this automatically???
Thanks.
You can write a directive to replace for ng-model,
<input type="text" name="name" k-bind-model="person.name">
angular.module('app')
.directive("kBindModel", ["$parse", function ($parse) {
return {
restrict: "A",
scope: false,
link: function (scope, element, attributes, controller) {
var key = null;
var strs = attributes.kBindModel.split('.');
if (strs && strs.length > 1) {
key = strs[1];
}
var model = scope[strs[0]];
element.change(function () {
scope.$apply(function () {
model.set(key, element.val());
});
});
scope.$watch(attributes.kBindModel, function (n, o) {
element.val(n);
});
}
}
}]);

Can I dynamically filter an array of names with Angular with a user input character?

I am presently playing around with some Angular examples that I found over here - https://code.angularjs.org/1.3.10/docs/guide/filter
I am using the built in filterFilter filter (he he) and I have read that I may need to create my own filter (so I can pass params) however seems like I should not have to if the filter will just re-run. The idea I am working on is to try to let the user define the character that is used to filter the array.
My present code looks like so:
var myApp = angular.module('FilterInControllerModule', [])
.controller('FilterController', ['filterFilter', function(filterFilter) {
this.filterChar = 'a';
this.array = [
{name: 'Tobias'},
{name: 'John'},
{name: 'Jack'},
{name: 'Frank'},
{name: 'Desmond'},
{name: 'Allan'},
{name: 'Margie'}
];
this.filteredArray = filterFilter(this.array, this.filterChar);
}]);
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Filters Extended Example 1</title>
</head>
<body ng-app="FilterInControllerModule">
<div ng-controller="FilterController as ctrl">
Filter by character: <input ng-model="ctrl.filterChar" type="text" maxlength="1"><br><br>
<div>
All entries:
<div ng-repeat="entry in ctrl.array">{{entry.name}}</div>
</div><br>
<div>
Entries that contain an "{{ctrl.filterChar}}":
<div ng-repeat="entry in ctrl.filteredArray">{{entry.name}}</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.11/angular.min.js"></script>
<script src="script.js"></script>
</body>
</html>
If you run the code you will find that the two-way binding between the model and view is working between the controller and the expression {{ctrl.filterChar}} however the controller does not seem to re-evaluate the actual filtering. Why might this be?
The problem occurs because your function filterFilter is only evaluated once. To re-evaluate it and get the filtered array on every digest cycle, you can do this:
In your controller, use a function:
var that = this;
this.filterMyArray = function() {
return filterFilter(that.array, that.filterChar);
}
...and in your html
<div ng-repeat="entry in ctrl.filterMyArray()">{{entry.name}}</div>
You can use Array.filter() in your controller:
var myApp = angular.module('FilterInControllerModule', [])
.controller('FilterController', ['filterFilter', function(filterFilter) {
this.filterChar = 'a';
this.array = [
{name: 'Tobias'},
{name: 'John'},
{name: 'Jack'},
{name: 'Frank'},
{name: 'Desmond'},
{name: 'Allan'},
{name: 'Margie'}
];
this.filterFilter = function() {
return this.array.filter(function(element, index, sourceArray) {
return element.name.indexOf(this.filterChar) !== -1;
}.bind(this));
}
}]);

AngularJS - How to order ng-repeat using another array

Say I have an array of keys in a specific order
orderedNames=["mike","bob","sarah];
and I have a JSON that I want to show using ng-repeat but in the order that they appear in the array:
{"people":
{
"bob":{
"hair":"brown",
"eyes":"blue",
"height":"tall"
},
"sarah":{
"hair":"blonde",
"eyes":"blue",
"height":"short"
},
"mike":{
"hair":"red",
"eyes":"blue",
"height":"tall"
}
}
}
How do I write a filer that would cause ng-repeat to spit out the people in the order in which they are specified in the array?
<li ng-repeat="person in people | orderNames"></li>
You can define a custom filter.
Plunker: http://plnkr.co/edit/fiuuGoGZK7tM5oefKQlS
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>Filter Example</title>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.2.x" src="http://code.angularjs.org/1.2.15/angular.js" data-semver="1.2.15"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<li ng-repeat="person in people|orderNames:orderedNames track by $index">{{person.hair}}</li>
</body>
</html>
app.js:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.people={
bob:{
hair:"brown",
eyes:"blue",
height:"tall"
},
sarah:{
hair:"blonde",
eyes:"blue",
height:"short"
},
mike:{
hair:"red",
eyes:"blue",
height:"tall"
}
};
$scope.orderedNames=["mike","bob","sarah"];
});
app.filter("orderNames",function(){
return function(input,sortBy) {
var ordered = [];
for (var key in sortBy) {
ordered.push(input[sortBy[key]]);
}
return ordered;
};
});
You could try something like this
<li ng-repeat="name in orderNames">
{{people[name]}}
</li>
use array as a reference :
http://jsbin.com/jujoj/14/edit
$scope.persons = {
bob:{
name:'bob'
},
mike:{
name: 'mike'
},
sarah: {
name: 'sarah'
}
};
$scope.orderedNames = [$scope.persons.mike, $scope.persons.bob, $scope.persons.sarah];
HTML : <ul ng-repeat="person in orderedNames">
<li>{{ person.name }}</li>
</ul>
to ng-repeat order by another array use
in the controller
$scope.somearray = [1,2,3,4] //or what ever you want to sort by
in the template.
| orderByArray:somearray:true/false
the true/false setting determines if stuff that isn't in the sortby array is outputted after the stuff that is or just dropped.
add the filter
var app = angular.module("app", []).filter("orderByArray", function () {
return function (input, sortBy, includeOrphans) {
//sorts by array and returns items with unsorted items at the end if they are not in the sortby array
if (!angular.isDefined(input)) { return; }
var ordered = [];
var remainder = _.cloneDeep(input);
angular.forEach(sortBy, function (sortitem) {
if (angular.isDefined(input[sortitem])) {
ordered.push(input[sortitem]);
delete (remainder[sortitem]);
}
});
if (includeOrphans) {
angular.forEach(remainder, function (remainingItem, key) {
ordered.push(input[key]);
});
}
return ordered;
};
});

AngularJS group check box validation

I have a list of check boxes, of which at least one is compulsory. I have tried to achieve this via AngularJS validation, but had a hard time. Below is my code:
// Code goes here for js
var app = angular.module('App', []);
function Ctrl($scope) {
$scope.formData = {};
$scope.formData.selectedGender = '';
$scope.gender = [{
'name': 'Male',
'id': 1
}, {
'name': 'Female',
'id': 2
}];
$scope.formData.selectedFruits = {};
$scope.fruits = [{
'name': 'Apple',
'id': 1
}, {
'name': 'Orange',
'id': 2
}, {
'name': 'Banana',
'id': 3
}, {
'name': 'Mango',
'id': 4
}, ];
$scope.submitForm = function() {
}
}
// Code goes here for html
<!doctype html>
<html ng-app="App">
<head>
<!-- css file -->
<!--App file -->
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
<!-- External file -->
</head>
<body>
<div ng-controller="Ctrl">
<form class="Scroller-Container">
<div ng-app>
<form class="Scroller-Container" ng-submit="submit()" ng-controller="Ctrl">
<div>
What would you like?
<div ng-repeat="(key, val) in fruits">
<input type="checkbox" ng-model="formData.selectedFruits[val.id]" name="group[]" id="group[{{val.id}}]" required />{{val.name}}
</div>
<br />
<div ng-repeat="(key, val) in gender">
<input type="radio" ng-model="$parent.formData.selectedGender" name="formData.selectedGender" id="{{val.id}}" value="{{val.id}}" required />{{val.name}}
</div>
<br />
</div>
<pre>{{formData.selectedFruits}}</pre>
<input type="submit" id="submit" value="Submit" />
</form>
</div>
<br>
</form>
</div>
</body>
</html>
Here is the code in plunker: http://plnkr.co/edit/Bz9yhSoPYUNzFDpfASwt?p=preview Has anyone done this on AngularJS? Making the checkboxes required, forces me to select all the checkbox values. This problem is also not documented in the AngularJS documentation.
If you want to require at least one item in group being selected, it's possible to define dynamic required attribute with ng-required.
For gender radio buttons this would be easy:
<input
type="radio"
ng-model="formData.selectedGender"
value="{{val.id}}"
ng-required="!formData.selectedGender"
>
Checkbox group would be easy too, if you used array to store selected fruits (just check array length), but with object it's necessary to check if any of values are set to true with filter or function in controller:
$scope.someSelected = function (object) {
return Object.keys(object).some(function (key) {
return object[key];
});
}
<input
type="checkbox"
value="{{val.id}}"
ng-model="formData.selectedFruits[val.id]"
ng-required="!someSelected(formData.selectedFruits)"
>
Update:
const someSelected = (object = {}) => Object.keys(object).some(key => object[key])
Also keep in mind that it will return false if value is 0.
Thanks to Klaster_1 for the accepted answer. I've built on this by using ng-change on the checkbox inputs to set a local scope variable, which would be used as the ng-required expression. This prevents the running of someSelected() on every digest.
Here is a plunkr demonstrating this: http://embed.plnkr.co/ScqA4aqno5XFSp9n3q6d/
Here is the HTML and JS for posterity.
<!DOCTYPE html>
<html ng-app="App">
<head>
<meta charset="utf-8" />
<script data-require="angular.js#1.4.9" data-semver="1.4.9" src="https://code.angularjs.org/1.4.9/angular.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="AppCtrl">
<form class="Scroller-Container" name="multipleCheckbox" novalidate="">
<h3>What would you like?</h3>
<div ng-repeat="fruit in fruits">
<input type="checkbox" ng-model="formData.selectedFruits[fruit.name]" ng-change="checkboxChanged()" ng-required="!someSelected" /> {{fruit.name}}
</div>
<p style="color: red;" ng-show="multipleCheckbox.$error.required">You must choose one fruit</p>
</form>
<pre>Fruits model:
{{formData.selectedFruits | json}}</pre>
</div>
</body>
</html>
angular
.module('App', [])
.controller('AppCtrl', function($scope) {
var selectedFruits = {
Mango: true
};
var calculateSomeSelected = function() {
$scope.someSelected = Object.keys(selectedFruits).some(function(key) {
return selectedFruits[key];
});
};
$scope.formData = {
selectedFruits: selectedFruits
};
$scope.fruits = [{
'name': 'Apple'
}, {
'name': 'Orange'
}, {
'name': 'Banana'
}, {
'name': 'Mango'
}];
$scope.checkboxChanged = calculateSomeSelected;
calculateSomeSelected();
});
Okay maybe very late to the party but if you have an array of objects which you are looking at, the solution of just checking the length of array of selected objects worked for me.
HTML
<div ng-repeat="vehicle in vehicles">
<input type="checkbox" name="car" ng-model="car.ids[vehicle._id]" ng-required=" car.objects.length <= 0"> {{vehicle.model}} {{vehicle.brand}} <b>{{vehicle.geofenceName}}</b>
</div>
JS
$scope.vehicles = [{},{}] // Your array of objects;
$scope.car = {
ids: {},
objects: []
};
$scope.$watch(function() {
return $scope.car.ids;
}, function(value) {
$scope.car.objects = [];
angular.forEach($scope.car.ids, function(value, key) {
value && $scope.car.objects.push(getCategoryById(key));
});
}, true);
function getCategoryById(id) {
for (var i = 0; i < $scope.vehicles.length; i++) {
if ($scope.vehicles[i]._id == id) {
return $scope.vehicles[i];
}
}
}
I understand what is happening in this solution that you are checking all the checkbox options to know which one is clicked. But from what I know this is by far the easiest solution(to understand and implement) and it works like a charm if you know that your options will be limited.
For a more time optimized solution. Check the answers above.
We can achieve your requirement without traversing all check-boxes instance.
Here is the sample code
<script>
angular.module('atLeastOneExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.e = function(s){
eval(s);
};
}]);
</script>
Here i am wrapping javascript eval function in "e" function to access it.
<form name="myForm" ng-controller="ExampleController" ng-init="c=0">
<label>
Value1: <input type="checkbox" ng-model="checkboxModel.value[0]" ng-change="e('($scope.checkboxModel.value[0])?$scope.c++:$scope.c--')"/>
</label><br/>
<label>
Value2: <input type="checkbox" ng-model="checkboxModel.value[1]" ng-change="e('($scope.checkboxModel.value[1])?$scope.c++:$scope.c--')"/>
</label><br/>
value = {{checkboxModel.value}}<br/>
isAtLeastOneChecked = {{c>0}}
</form>

Categories