How can I update this JSON object automatically using AngularJS? - javascript

I am creating a page where users can order reports as PDF. I have tried to create it using Angular. When the user changes a report type, this is correctly updated in the debug information present on the page. However, the request for a report is sent as JSON and I would prefer if this JSON object was updated automatically as well. As it is now, I have to click on the "Create JSON" button for it to be updated.
This example can be seen over at JSFiddle as well: http://jsfiddle.net/HenricF/wgvd7wLx/2/
IMPORTANT
The JSON object will not only include the report type, but also a lot of other options not shown here. These options include accounts, languages and dates and the JSON object should preferably be updated whenever anyone of these are changed. I am sorry for not mentioning this initially.
HTML
<body ng-app="OrderPageApp">
<div id="all" ng-controller="ReportController">
<div id="top">
<div class="pagesection" id="ChooseReportType">
<h3>Select report type</h3>
<select id="dropdownlist" ng-change="setAccountTypes(chosenReport)" ng-options="report.name for report in reports" ng-model="chosenReport"></select>
</div>
<div class="pagesection" id="ChooseLanguage">
<h3>Select language</h3>
<select id="dropdownlist" ng-options="language.name for language in languages" ng-model="chosenLanguage"></select>
</div>
<div class="pagesection" id="IncludeHeadlines">
<h4>Include headlines</h4>
<input name="includeHeadlines" type="checkbox" ng-model="includeHeadlines">
</div>
<div class="bottom" id="bottom">
<h4>Selected report</h4>
<div id="reportName">Name: {{name}}</div>
<div id="reportCode">Code: {{code}}</div>
<div id="Language">Language: {{chosenLanguage.code}}</div>
<div id="includeHeadlines">Include headlines: {{includeHeadlines}}</div>
<h4>JSON data</h4>
<input type="button" value="Create JSON" ng-click="showJson()">
<div id="pdfData"><pre>{{pdfData}}</pre>
</div>
</div>
</div>
JS
var reportTypes = [{
name: 'Report type 1',
code: 1
}, {
name: 'Report type 2',
code: 2
}, {
name: 'Report type 3',
code: 3
}];
var languages = [{
name: 'Swedish',
code: 1053
}, {
name: 'English',
code: 1033
}];
var app = angular.module('OrderPageApp', []);
app.controller('ReportController', function ($scope) {
$scope.reports = reportTypes;
$scope.languages = languages;
$scope.setAccountTypes = function (chosenReport) {
$scope.name = chosenReport.name;
$scope.code = chosenReport.code;
};
$scope.showJson = function () {
$scope.pdfData = angular.toJson(new CreatePdfData($scope.name, $scope.chosenLanguage, $scope.includeHeadlines));
};
function CreatePdfData(reportType, chosenLanguage, includeHeadlines) {
this.reportType = reportType;
this.chosenLanguage = chosenLanguage.code;
this.includeHeadlines = includeHeadlines;
};
})

I ended up solving it like this (thanks to the input from deitch and Dipali Vasani):
The pdfData was updated manually as soon as any input was changed - I couldn't figure out a way to have it update automatically.
This is how I did it in HTML:
<input name="includeHeadlines" type="checkbox" ng-model="includeHeadlines" ng-change="showJson()">
And this is how I did it in JS:
$scope.setAccountTypes = function (chosenReport) {
...
$scope.showJson();
};

try this changes :
var reportTypes = [{
name: 'Report type 1',
code: 1
}, {
name: 'Report type 2',
code: 2
}, {
name: 'Report type 3',
code: 3
}];
var languages = [{
name: 'Swedish',
code: 1053
}, {
name: 'English',
code: 1033
}];
var app = angular.module('OrderPageApp', []);
app.controller('ReportController', function($scope) {
$scope.reports = reportTypes;
$scope.languages = languages;
$scope.setAccountTypes = function(chosenReport) {
$scope.name = chosenReport.name;
$scope.code = chosenReport.code;
};
$scope.showJson = function() {
var name = $scope.name;
var chosenLanguage = $scope.chosenLanguage;
var includeHeadlines = $scope.includeHeadlines;
if (name == undefined) {
name = null;
}
if (chosenLanguage == undefined) {
chosenLanguage = null;
}
if (includeHeadlines == undefined) {
includeHeadlines = false;
}
$scope.pdfData = angular.toJson(new CreatePdfData(name, chosenLanguage, includeHeadlines));
};
function CreatePdfData(reportType, chosenLanguage, includeHeadlines) {
this.reportType = reportType;
this.chosenLanguage = chosenLanguage != null ? chosenLanguage.code : null;
this.includeHeadlines = includeHeadlines;
};
})
<body ng-app="OrderPageApp">
<div id="all" ng-controller="ReportController">
<div id="top">
<div class="pagesection" id="ChooseReportType">
<h3>Select report type</h3>
<select id="dropdownlist" ng-change="setAccountTypes(chosenReport);showJson()" ng-options="report.name for report in reports" ng-model="chosenReport"></select>
</div>
<div class="pagesection" id="ChooseLanguage">
<h3>Select language</h3>
<select id="dropdownlist" ng-options="language.name for language in languages" ng-model="chosenLanguage" ng-change="showJson()"></select>
</div>
<div class="pagesection" id="IncludeHeadlines">
<h4>Include headlines</h4>
<input name="includeHeadlines" type="checkbox" ng-model="includeHeadlines" ng-change="showJson()">
</div>
<div class="bottom" id="bottom">
<h4>Selected report</h4>
<div id="reportName">Name: {{name}}</div>
<div id="reportCode">Code: {{code}}</div>
<div id="Language">Language: {{chosenLanguage.code}}</div>
<div id="includeHeadlines">Include headlines: {{includeHeadlines}}</div>
<h4>JSON data</h4>
<input type="button" value="Create JSON" ng-click="showJson()">
<div id="pdfData"><pre>{{pdfData}}</pre>
</div>
</div>
</div>
</body>
JSFiddle
This will solve your problem

Related

How do I Output a value of a nested object in Javascript after looping through?

I'm having some trouble printing out some of my data of a forEach loop. I'm working on a Twitter clone and I'm trying to loop through an object and print out the tweets for a user. I'm getting the divs to print out in the console but I can't seem to figure out to get the tweets to print inside the div.
Any help would be appreciated.
var user1 = {
userName: '#elonmusk',
displayName: 'Elon Musk',
joinedDate: 'June 2009',
followingCount: 103,
tweetAmount: 18600,
followerCount: 47900000,
avatarURL: 'assets/elonmusk.jpg',
coverPhotoURL: 'assets/elonmusk-cover.jpeg',
tweets: [{
text: 'I admit to judging books by their cover',
timestamp: '2/10/2021 00:01:20'
},
{
text: 'Starship to the moon',
timestamp: '2/09/2021 18:37:12'
},
{
text: 'Out on launch pad, engine swap underway',
timestamp: '2/09/2021 12:11:51'
}
]
};
var container = document.getElementById('container');
var midSection = document.getElementById('mid-section')
var sections = document.createElement('div');
sections.classList.add('container');
sections.innerHTML = `
<div class="left-section"></div>
<div class="mid-section">
<div id="header">
<img class="arrow" src="./assets/arrow-left.svg" />
<div class="user-info">
<h2>${user1.displayName}</h2>
<div class="numberOfTweets light-text">${user1.tweetAmount}K Tweets</div>
</div>
</div>
<div class="cover-photo"><img src="${user1.coverPhotoURL}"/></div>
<div id="avatar">
<div class="profileImage"><img src="${user1.avatarURL}"/></div>
<div class="follow">
<button class="btn-more"><img src="./assets/three-dots.svg"/></button>
<button class="btn-follow btn-primary">Follow</button>
</div>
</div>
<h2 class="pl20">${user1.displayName}</h2>
<div class="user-info2 light-text pl20">${user1.userName}</div>
<div class="join-date light-text pl20 pt10">Joined ${user1.joinedDate}</div>
<div class="following pl20 pt10">
<p><span class="bold">${user1.followingCount}</span> Following</p>
<p class="pl20"><span class="bold">${user1.followerCount}</span> Followers</p>
</div>
<div class="tab-navigation pt20">
<div class="tabs">Tweets</div>
<div class="tabs">Tweets & replies</div>
<div class="tabs">Media</div>
<div class="tabs">Likes</div>
</div>
<div class="tweets">
</div>
</div>
<div class="right-section"></div>
`;
var user1Tweets = user1.tweets;
var tweetDivs = user1Tweets.map((tweetDiv) => {
if (!tweetDiv.tweets) {
return { ...tweetDiv,
tweets: []
};
} else if (!Array.isArray(tweetDiv.tweets)) {
return { ...tweetDiv,
tweets: Object.values(tweetDiv.tweets)
};
}
return tweetDiv;
});
tweetDivs.forEach(function(tweetDiv, i) {
var tDiv = document.createElement('div');
tDiv.classList.add('tweetDiv');
tDiv.innerHTML = `
${tweetDiv.tweets.map(function(tweet) {
return `<div>${tweet}</div>`
})}
`;
//console.log(tDiv)
midSection.appendChild(tDiv);
})
container.appendChild(sections);
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
<div id="container">
<div id="mid-section"></div>
</div>
user1.tweets is an array. So you can iterate over the array, get the text from each object, and do something with it.
var user1 = {
tweets: [{
text: 'I admit to judging books by their cover',
timestamp: '2/10/2021 00:01:20'
},
{
text: 'Starship to the moon',
timestamp: '2/09/2021 18:37:12'
},
{
text: 'Out on launch pad, engine swap underway',
timestamp: '2/09/2021 12:11:51'
}
]
};
var midSection = document.getElementById('mid-section')
user1.tweets.forEach(function(tweetDiv, i) {
var tDiv = document.createElement('div');
tDiv.classList.add('tweetDiv');
tDiv.innerHTML = tweetDiv.text;
midSection.appendChild(tDiv);
})
<div id="mid-section"></div>

AngularJS: loop through radio button groups

I am making an AngularJs survey application.
I want to show tasks one by one. And every task can have one or multiple questions with radio button answers. And I want to save question+answer pair in the new array.
If I want to show only one question per task than I was able to get answer values from radio buttons and push then into answer array. But as I have multiple questions and multiple radio button groups per page, I can't find a way to get the selected radio buttons values and push them into the answers array. I have read that ng-model can solve this, but I couldn't figure how.
This is what I have so far: https://jsfiddle.net/8qfom9th
<div ng-app="surveyApp" ng-controller="surveyCtrl">
<div ng-repeat="questionSet in questionSet">
<div ng-if="question_index == $index">
<div ng-repeat="onePageQuestions in questionSet.onePageQuestions">
<div ng-repeat="question in onePageQuestions.question">
{{question.description}}
<form action="">
<div ng-repeat="options in question.options">
<input type="radio" name="gender" ng-model="question.random" ng-value="options.answer"> {{options.answer}}
</div>
</form>
</div>
</div>
</div>
</div>
<hr>
<button ng-click="next(); submitAnswers()">Next</button>
It's actually pretty simple buddy.
You can get the value of the selected button by using the checked property. Since only one radio button can be selected from a group, you would be able to get the value of the selected one easily using this property in an if loop within the javascript.
Since you have given the options of the radio buttons a name already, i.e., gender. You can simply get all the options elements, using the following:
var options = document.getElementsByName('gender');
var option_value; //to get the selected value
The next step is to loop through all the buttons and check which of those is selected.. To loop through them use a for loop as follows: for (var i = 0; i < options.length; i++) {...}
To check if it is selected or not, use the checked attribute as follows:
if (options[i].checked) {
option_value = options[i].value;
}
I'm not sure what you intend to do with those values, hence I have assumed that you need to display those and to do that just create another element, like a <div> and give it an ID. And then just add the selected option value to that element. Should be something like this:
HTML: <div id="selected">The selected options are:</div>
JS:document.getElementById('selected').innerHTML += "<br>" + option_value;
Updated your fiddle.
Or if you want to check it right here,
here is the updated code:
var app = angular.module('surveyApp', []);
app.controller('surveyCtrl', function($scope) {
$scope.questionSet = [{
onePageQuestions: [{
question: [{
description: 'question#1?',
options: [{
answer: 'answer#1'
}, {
answer: 'answer#2'
}, {
answer: 'answer#3'
}]
},
{
description: 'question#2?',
options: [{
answer: 'answer#4'
}, {
answer: 'answer#5'
}, {
answer: 'answer#6'
}]
}
]
}]
},
{
onePageQuestions: [{
question: [{
description: 'question#3?',
options: [{
answer: 'answer#7'
}, {
answer: 'answer#8'
}, {
answer: 'answer#9'
}]
}]
}]
}
];
$scope.question_index = 0;
$scope.next = function() {
if ($scope.question_index >= $scope.questionSet.length - 1) {
$scope.question_index = 0;
} else {
$scope.question_index++;
}
};
$scope.submitAnswers = function() {
var options = document.getElementsByName('gender');
var option_value;
for (var i = 0; i < options.length; i++) {
if (options[i].checked) {
option_value = options[i].value;
document.getElementById('selected').innerHTML += "<br>" + option_value;
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="surveyApp" ng-controller="surveyCtrl">
<div ng-repeat="questionSet in questionSet">
<div ng-if="question_index == $index">
<div ng-repeat="onePageQuestions in questionSet.onePageQuestions">
<div ng-repeat="question in onePageQuestions.question">
{{question.description}}
<form action="">
<div ng-repeat="options in question.options">
<input type="radio" name="gender" ng-model="question.random" ng-value="options.answer"> {{options.answer}}
</div>
</form>
</div>
</div>
</div>
</div>
<hr>
<button ng-click="next(); submitAnswers()">Next</button>
<hr>
<div id="selected">The selected options are:
</div>
</div>
Use parent.answer for ng-model for dynamic radio button.
And for your use case I have added one saveAnswers functions to manipulate and save users answers.
Below is the code for your use case demo, run it and see updated questionSet in console.
var app = angular.module('surveyApp', []);
app.controller('surveyCtrl', function($scope) {
$scope.answer = '';
$scope.saveAnswers = function(description, options) {
$scope.questionSet.map(function(value, key) {
value.onePageQuestions.map(function(item, index) {
item.question.map(function(question, count) {
if (question.description === description.description) {
question.answer = options;
}
})
})
})
}
$scope.questionSet = [{
onePageQuestions: [{
question: [{
description: 'question#1?',
answer: '',
options: [{
answer: 'answer#1'
}, {
answer: 'answer#2'
}, {
answer: 'answer#3'
}]
},
{
description: 'question#2?',
answer: '',
options: [{
answer: 'answer#4'
}, {
answer: 'answer#5'
}, {
answer: 'answer#6'
}]
}
]
}]
},
{
onePageQuestions: [{
question: [{
description: 'question#3?',
answer: '',
options: [{
answer: 'answer#7'
}, {
answer: 'answer#8'
}, {
answer: 'answer#9'
}]
}]
}]
}
];
$scope.question_index = 0;
$scope.next = function() {
if ($scope.question_index >= $scope.questionSet.length - 1) {
$scope.question_index = 0;
} else {
$scope.question_index++;
}
};
$scope.submitAnswers = function() {
console.log($scope.questionSet)
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="surveyApp" ng-controller="surveyCtrl">
<div ng-repeat="questionSet in questionSet">
<div ng-if="question_index == $index">
<div ng-repeat="onePageQuestions in questionSet.onePageQuestions">
<div ng-repeat="question in onePageQuestions.question">
{{question.description}}
<form action="">
<div ng-repeat="options in question.options">
<input ng-change="saveAnswers(question,options.answer)" type="radio" name="gender" ng-model="$parent.answer" ng-value="options.answer"> {{options.answer}}
</div>
</form>
</div>
</div>
</div>
</div>
<hr>
<button ng-click="next()">Next</button>
<button ng-click="submitAnswers()"> Submit</button>
Hope this will help you!

knockout.js remove doesn't work with nested viewmodel and viewmodel in main viewmodel

<div data-bind="with: SimpleListModel">
<form data-bind="submit: addItem" >
New item:
<input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' />
<button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
<p>Your items:</p>
<select multiple="multiple" width="50" data-bind="options: items"> </select>
</form>
</div>
<div data-bind="with: SimpleListModel2">
<div data-bind="foreach: baselist">
<div>
<span data-bind="text: basename"></span>
<div data-bind="foreach: subItems">
<span data-bind="text: subitemname"></span>
Del
</div>
</div>
<button data-bind="click:$parent.addChild">Add</button>
</div>
</div>
this is the viewmodel
var SimpleListModel = function(items) {
this.items = ko.observableArray(items);
this.itemToAdd = ko.observable("");
this.addItem = function() {
if (this.itemToAdd() != "") {
this.items.push(this.itemToAdd()); // Adds the item. Writing to the "items" observableArray causes any associated UI to update.
this.itemToAdd(""); // Clears the text box, because it's bound to the "itemToAdd" observable
}
}.bind(this); // Ensure that "this" is always this view model
};
var initialData = [
{ basename: "Danny", subItems: [
{ subitemname: "Mobile"},
{ subitemname: "Home"}]
},
{ basename: "Sensei", subItems: [
{ subitemname: "Mobile"},
{ subitemname: "Home"}]
}];
var SimpleListModel2 = function(baselist) {
var self= this;
self.baselist= ko.observableArray(baselist);
self.addChild = function(list) {
alert(list.basename);
}.bind(this);
self.removecard = function (data) {
//tried
data.baselist.subItems.remove(data);
data.subItems.remove(data);
$.each(self.baselist(), function() { this.subItems.remove(data) })
};
};
var masterVM = (function () {
var self = this;
self.SimpleListModel= new SimpleListModel(["Alpha", "Beta", "Gamma"]);
self.SimpleListModel2= new SimpleListModel2(initialData);
})();
ko.applyBindings(masterVM);
This is a small code snippet i constructed of my project. Can someone make remove card work? last two increments of my questions are of the same type. but this question is the highest i reach.
removecard doesn't work now in this scenario at least for me.
Use $parents[index] to get to specific parent. http://knockoutjs.com/documentation/binding-context.html.
$parents[0] --> parent
$parents[1] --> grand parent
etc
var initialData = [
{ basename: "Danny", subItems: [
{ subitemname: "Mobile"},
{ subitemname: "Home"}]
},
{ basename: "Sensei", subItems: [
{ subitemname: "Mobile"},
{ subitemname: "Home"}]
}];
var SimpleListModel2 = function(baselist) {
var self= this;
self.baselist= ko.observableArray(baselist);
self.addChild = function(list) {
alert(list.basename);
}.bind(this);
self.removecard = function (data) {
//tried
console.log(data);
};
};
var masterVM = (function () {
var self = this;
self.SimpleListModel2= new SimpleListModel2(initialData);
})();
ko.applyBindings(masterVM);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="with: SimpleListModel2">
<div data-bind="foreach: baselist">
<div>
<span data-bind="text: basename"></span>
<div data-bind="foreach: subItems">
<span data-bind="text: subitemname"></span>
Del
</div>
</div>
<button data-bind="click:$parent.addChild">Add</button>
</div>
</div>

reset ng-model from controller in ng-repeat

I am trying to create a list of editable inputs from a list of items. I want the user to be able to edit any of the items, but if they change there mind they can click a button and reset it back to the way it was.
So far I have everything working except the reset.
my html
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item.value" type="text" ng-click="copy(item)"/>
<button ng-click="reset(item)">
x
</button>
</div>
{{list}}<br>
{{selected_item_backup}}
</div>
my controller
function TestController($scope) {
$scope.selected_item_backup = {};
$scope.list = [ { value: 'value 1' }, { value: 'value 2' }, { value: 'value 3' } ];
$scope.reset = function (item) {
// i know this wont work for many reasons, it is just an example of what I would like to do
item = $scope.selected_item_backup;
};
$scope.copy = function (item) {
angular.copy(item, $scope.selected_item_backup);
};
}
and here is a fiddle
http://jsfiddle.net/ryanmc/1ab24o4t/1/
Keep in mind that this is a simplified version of my real code. My objects will have many editable fields each. Also they are not indexed, and so the index cannot be relied on. I just want to be able to assign the original item on top of the new and have it replaced.
This is work solution jsfiddle
function TestController($scope) {
$scope.list = [{
value: 'value 1'
}, {
value: 'value 2'
}, {
value: 'value 3'
}];
var orgiinalList = [];
angular.forEach($scope.list, function(item) {
orgiinalList.push(angular.copy(item));
});
$scope.reset = function(index) {
$scope.list[index] = angular.copy(orgiinalList[index]);
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item.value" type="text" />
<button ng-click="reset($index)">
x
</button>
</div>
{{list}}
<br>
</div>
Changing your reset function so that it looks like this:
$scope.reset = function(index) {
$scope.list[index].value = "value "+ (index+1);
};
will make it so that your 'reset' buttons restore what would be those original values...
Angular controller:
function TestController($scope) {
$scope.selected_item_backup = {};
$scope.list = [{
value: 'value 1'
}, {
value: 'value 2'
}, {
value: 'value 3'
}];
$scope.reset = function(index) {
$scope.list[index].value = "value " + (index+1);
};
$scope.copy = function(item) {
angular.copy(item, $scope.selected_item_backup);
};
}
HTML:
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item.value" type="text" ng-click="copy(item)" />
<button ng-click="reset($index)">
x
</button>
</div>
</div>
The values of your array are directly modeled by those inputs - therefore as a user types changes into those inputs they are directly manipulating the $scope.list array so you cant really use it as a reference... Hope that makes sense.
You can't use global functions as controllers in newer versions of angular but you can register your controllers in your application:
<div ng-app ng-controller="TestController">
<div ng-repeat="item in list">
<label>Input {{$index+1}}:</label>
<input ng-model="item.value" type="text" ng-click="copy($index,item)"/>
<button ng-click="reset($index,item)">
x
</button>
</div>
{{list}}<br>
{{selected_item_backup}}
function TestController($scope) {
$scope.selected_item_backup = [];
$scope.list = [ { value: 'value 1' }, { value: 'value 2' }, { value: 'value 3' } ];
$scope.reset = function (index) {
$scope.list[index].value = $scope.selected_item_backup[index];
};
$scope.copy = function (index,item) {
// backup the old value
$scope.selected_item_backup[index] = item.value;
};
}

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