Get value from all checked checkbox in angular - json

i'm creating quiz app using angular and i have code like this
ngOnInit() {
this.myForm = this.fb.group({
lessonCode: "test",
answer: this.fb.array([])
});
}
onChange(email: string, code: string, isChecked: boolean) {
const emailFormArray = <FormArray>this.myForm.controls.answer;
if (isChecked) {
let array = new FormGroup({
questionCode: new FormControl(email),
emailCode: new FormArray([
new FormControl(code)
]),
});
emailFormArray.push(array);
} else {
let index = emailFormArray.controls.findIndex(x => x.value == email);
emailFormArray.removeAt(index);
}
}
which producing array like this
Form values: {
"lessonCode": "test",
"answer": [
{
"questionCode": "pertanyaan2",
"emailCode": [
"option2"
]
},
{
"questionCode": "pertanyaan2",
"emailCode": [
"option1"
]
}
]
}
but what i'm actually needed is like this
Form values: {
"lessonCode": "test",
"answer": [
{
"questionCode": "pertanyaan2",
"emailCode": {
"option2",
"option1"
}
}
]
}
how can i achieve that? any thoughts would be very helpful
i have minimal reproduce here https://stackblitz.com/edit/angular-ca1jin?file=src%2Fapp%2Fapp.component.ts

I suggested another aproach
If we create a formGroup like
this.myForm = this.fb.group({
lessonCode: "test",
answer: this.fb.array(this.users.map(x=>this.fb.group({
questionCode:x.code,
email:[[]]
})))
});
See that answer is a FormArray of FormGroup, each FormGroup has two FormsControl, questionCode and email, that is still a FormControl (but store an array). You need don't confussed with a FormArray. A FormControl can store an array, an object, an string, a number, etc.. And it's no so strange store an array, e.g. the mat-select multiple store an array too
As always we work with formArrays we declare a getter to manage in the .html
get answers()
{
return this.myForm.get('answer') as FormArray
}
Well, the form is a bit complex
<form [formGroup]="myForm">
<div formArrayName="answer">
<div *ngFor="let group of answers.controls;let i=index">
<p>{{users[i].code}}</p>
<div *ngFor="let user of users[i].email">
<input #check type="checkbox"
[value]="answers.at(i).value.email.indexOf(user.code)>=0"
(change)="onChange(i,user.code,check.checked)" >{{user.code}}<br>
</div>
</div>
</div>
</form>
See how we use [value]="answers.at(i).value.email.indexOf(user.code)>=0" Really it's not necesary if our "email" control is at first empy, but it possible we want in a future use this form to show the result, and our "eamil" can value, e.g. "[option1]"
Another thing to take account is that I use a template reference variable #check and send to the function OnChange check.checked -so we received in the function a boolean-
Our function OnChange get more simple
onChange(index:number, code: string, isChecked: boolean) {
const control=this.answers.at(index).get('email')
if (isChecked && control.value.indexOf(code)<0)
control.setValue([...control.value,code]
.sort((a:string,b:string)=>
this.users[index].email.findIndex(x=>x.code==a)>
this.users[index].email.findIndex(x=>x.code==b)?1:-1)
)
if (!isChecked && control.value.indexOf(code)>=0)
control.setValue(control.value.filter(x=>x!=code))
}
I like check if exist or not before remove/add the element
See that we need "sort" the response when the check is true -else we received the response, e.g. [option2,option1] -If our requeriments don't required we can remove the sort-
The stackblitz

What you need to do is to add emailCode as FormArray instead of FormControl, this way you will be able to check whether the questionCode already exists, and if yes, you can append to emailCode the option you checked.
The only things to change are on your onChange method
First you array variable, you need to add FormArray instead of FormControl
let array = new FormGroup({
questionCode: new FormControl(email),
emailCode: new FormArray([])
});
Then create a FormControl for your checked option
let codeOption = new FormControl(code)
And finally, in your if condition, check if the questionCode already exist to just append your formControl to it, or to create a new object.
if (isChecked) {
if (emailFormArray.controls.some(obj => obj.get('questionCode').value == email)){
(<FormArray>emailFormArray.controls.find(obj => obj.get('questionCode').value == email).get('emailCode')).push(codeOption);
}else{
(<FormArray>array.get('emailCode')).push(codeOption)
emailFormArray.push(array)
}
}
To be more clear I have modified your stackblitz to fit with your needs
I have not modified the else condition to remove the options on your FormArray but you just need to copy the if condition to get the index of the code element on your FormArray of emailCode.

You can make a function that traverses every answer and returns an object in your desired schema.

Related

Angular 6 : Validators.pattern not working properly

I have a Validators.pattern for a field, and that's the only validation I have on my form.
Normal usage is working. It validates the field properly however the issue occurs when you try to copy and paste.
If you copy and paste on the field repeatedly on the field, it is like the validity of the field is being toggled( submit button is disabled if form is invalid )
Issue also occurs when I populate a the data from other source like search or auto-suggest.
buildForm(obj) {
this.form = this.fb.group({
field: [obj['field'] || '', Validators.pattern(/MY_REG_EX_HERE/g)],
id: [obj['id'] || ''],
});
}
I am not really sure about the main cause of the issue but as a workaround, I created custom validator with same REGEX. I will post it here and might help someone.
import { AbstractControl, ValidationErrors, ValidatorFn } from '#angular/forms';
export function customValidator(control: AbstractControl) {
if (control.value !== '') {
const isValid = (/MY_REG_EX_HERE/g).test(control.value)
return isValid ? null : { customValidator: true };
} else {
return null;
}
}
I had a similar problem. I studied it in detail and found out the reason. Like me, you are using the /g modifier. which has a side effect if the regex is assigned to a variable. This modifier indicates the continuation of the search in the string. To understand the "unexpected" behavior, take a look at the screenshot.
You can find out more information here

React - iterating through JSON object

I am new to Javascripting and React JS coding. I have this JSON object-
var json1 = {"facilities":{"facility":[{"facilityCode":"J0LN","facilityId":"1","facilityName":"J0LN","npid":"1295718450","pid":"123457","providerState":"Alabama"},{"facilityCode":"K0NS","facilityId":"2","facilityName":"K0NS","npid":"9696969669","pid":"111111","providerState":"Alaska"},{"facilityCode":"J0LN1","facilityId":"3","facilityName":"J0LN1","npid":"111111111","pid":"221133","providerState":"Alabama"},{"facilityCode":"0987654321","facilityId":"4","facilityName":"0987654321","npid":"0987654321","pid":"235675","providerState":"Alabama"},{"facilityCode":"7776667676","facilityId":"5","facilityName":"7776667676","npid":"7776667676","pid":"236576","providerState":"Alabama"},{"facilityCode":"979797977","facilityId":"6","facilityName":"979797977","npid":"979797977","pid":"325347","providerState":"Alabama"},{"facilityCode":"9898989898","facilityId":"7","facilityName":"9898989898","npid":"9898989898","pid":"989898","providerState":"Alabama"},{"facilityCode":"121212","facilityId":"8","facilityName":"121212","npid":"1212120022","pid":"121212","providerState":"Connecticut"},{"facilityCode":"141414","facilityId":"9","facilityName":"141414","npid":"1414140022","pid":"141414","providerState":"Delaware"},{"facilityCode":"887766","facilityId":"10","facilityName":"887766","npid":"8877660022","pid":"887766","providerState":"Delaware"},{"facilityCode":"212121","facilityId":"11","facilityName":"OP-212121-OP","npid":"2121210022","pid":"212121","providerState":"Maryland"},{"facilityCode":"717171","facilityId":"12","facilityName":"IP-Qrtly-717171","npid":"7171710022","pid":"717174","providerState":"Alabama"},{"facilityCode":"RMC","facilityId":"13","facilityName":"RMC","npid":"1","pid":"676767","providerState":"Alabama"},{"facilityCode":"WCC","facilityId":"14","facilityName":"WCC","npid":"2","pid":"454676","providerState":"Alabama"},{"facilityCode":"FC","facilityId":"15","facilityName":"FN","npid":"1992813240","pid":"123456","providerState":"Alabama"},{"facilityCode":"VCC","facilityId":"16","facilityName":"VCC","npid":"1213121312","pid":"122312","providerState":"Alabama"},{"facilityCode":"AAAAA","facilityId":"17","facilityName":"AAAAA","npid":"3","pid":"112233","providerState":"Alabama"},{"facilityCode":"AAAAB","facilityId":"18","facilityName":"AAAAB","npid":"4","pid":"334455","providerState":"Alabama"},{"facilityCode":"AAAAC","facilityId":"19","facilityName":"AAAAC","npid":"5","pid":"556677","providerState":"Alabama"},{"facilityCode":"AAAAD","facilityId":"20","facilityName":"AAAAD","npid":"6","pid":"778899","providerState":"Alabama"},{"facilityCode":"AAAAE","facilityId":"21","facilityName":"AAAAE","npid":"7","pid":"616161","providerState":"Alabama"},{"facilityCode":"AAAAF","facilityId":"22","facilityName":"AAAAF","npid":"8","pid":"626262","providerState":"Alabama"},{"facilityCode":"AAAAG","facilityId":"23","facilityName":"AAAAG","npid":"9","pid":"717171","providerState":"Alabama"},{"facilityCode":"AAAAH","facilityId":"24","facilityName":"AAAAH","npid":"10","pid":"727272","providerState":"Alabama"},{"facilityCode":"AAAAI","facilityId":"25","facilityName":"AAAAI","npid":"11","pid":"757575","providerState":"Alabama"},{"facilityCode":"AAAAJ","facilityId":"26","facilityName":"AAAAJ","npid":"12","pid":"767676","providerState":"Alabama"},{"facilityCode":"AAAAK","facilityId":"27","facilityName":"AAAAK","npid":"13","pid":"818181","providerState":"Alabama"},{"facilityCode":"AAAAL","facilityId":"28","facilityName":"AAAAL","npid":"14","pid":"828282","providerState":"Alabama"},{"facilityCode":"AAAAM","facilityId":"29","facilityName":"AAAAM","npid":"15","pid":"858585","providerState":"Alabama"},{"facilityCode":"AAAAN","facilityId":"30","facilityName":"AAAAN","npid":"16","pid":"868686","providerState":"Alabama"},{"facilityCode":"AAAAO","facilityId":"31","facilityName":"AAAAO","npid":"17","pid":"919191","providerState":"Alabama"},{"facilityCode":"AAAAP","facilityId":"32","facilityName":"AAAAP","npid":"18","pid":"929292","providerState":"Alabama"},{"facilityCode":"AAAAQ","facilityId":"33","facilityName":"AAAAQ","npid":"19","pid":"959595","providerState":"Alabama"},{"facilityCode":"AAAAR","facilityId":"34","facilityName":"AAAAR","npid":"20","pid":"969696","providerState":"Alabama"},{"facilityCode":"UNIQUE","facilityId":"35","facilityName":"UNIQUE","npid":"21","pid":"123456","providerState":"Alabama"}]}};
I am setting the state of my data here and binding it (not sure why I am doing this, but I see everyone doing it as part of their ajax calls)
var stateSet = function(data) {
this.setState({data: json1});
};
stateSet.bind(this); // attempt to mock an AJAX call here, by assuming we have already obtained the JSON object.
return(
<table>
<tbody>
<tr>
<th>Facility Code</th>
<th>Facility ID</th>
<th>Facility Name</th>
<th>NPID</th>
<th>PID</th>
<th>Provider State</th>
</tr>
{
this.state.data.map(function(facility, key) {
// I'm not sure if the control is entering this function, and I don't understand why
return (
<tr>
<td>{facility.facilityCode}</td>
<td>{facility.facilityId}</td>
<td>{facility.facilityName}</td>
<td>{facility.npid}</td>
<td>{facility.pid}</td>
<td>{facility.providerState}</td>
</tr>
);
})
}
</tbody>
</table>
);
As I've mentioned in the code as part of the comment, I don't think the control is entering this.state.data.map(function(facility, key) { function and I don't understand why.
You're not targeting the facility array correctly.
It should be this.state.data.facilities.facility.map, and you may also set your json1 to state in your constructor directly like so
constructor() {
super();
var json1 = {
facilities: {
facility: [
{
facilityCode: "J0LN",
facilityId: "1",
facilityName: "J0LN",
npid: "1295718450",
pid: "123457",
providerState: "Alabama"
} ....
]
}
};
this.state = {
data: json1
};
}
Working Snippet
You path to array in incorrect, also add a check on availability of this.state.data so that it doesn't fail if data is not available,
{this.state.data && this.state.data.facilities.facility.map(function(facility, key) {}}
You re are use a map function on an object that has only on prop, this is not possible. The only prop the json object has is facilities
var stateSet = function(data) {
this.setState({data: json1});
};
why are you using function(date), this is not needed. Just give function(), because i think the stateSet is firstly not called anywhere so it doesn t get triggerd and you re expecting a prop in that function.
From your question, it is clear that you are trying to access the facility array inside the facilities object.
You can achieve this as:
this.state.data.facilities.facility.map((elementOffFacilityArray,index) =>
{
// do whatever you would like to do with individual elements of the array.
})
But at the time when this map function will execute, it might be the case that this facility array may be empty which will result in error whic already has been pointed out by #nrgwsth. Thus, you will have to keep the check and the above expression for this case will become as :
{
this.state.data &&
this.state.data.facilities &&
this.state.data.facilities.facility ?
this.state.data.facilities.facility.map((elementOffFacilityArray,index) =>
{
// do whatever you would like to do with individual elements of
the array.
})
:
''
}

VueJS - trouble understanding .$set and .$add

I am trying to build an array of objects in VueJS and I am running into some issues with .$set and .$add.
For example, I need the following structure when adding new items/objects:
{
"attendees": {
"a32iaaidASDI": {
"name": "Jane Doe",
"userToken": "a32iaaidASDI",
"agencies": [
{
"name": "Foo Co"
}
]
}
}
}
New objects are added in response to an AJAX call that returns JSON formatted the same as above. Here is my Vue instance:
var vm = new Vue({
el: '#trainingContainer',
data: {
attending: false,
attendees: {}
},
methods: {
setParticipantAttending: function(data)
{
if (data.attending)
{
this.attendees.$add(data.userToken, data);
} else {
this.attendees.$delete(data.userToken);
}
}
}
});
This only works if I start with attendees: {} in my data but when I try attendees.length after adding an attendee, I receive undefined. If I use attendees: [], the new object does not appear to be added. And lastly, if I use .$set(data.userToken, data) it does not add in the 'token':{data..} format required.
What could be the issue here? What is the correct way to use $.add when starting with an empty array of objects?
UPDATE
I found that it works if I set attendees: {} and then, when adding a new object,
if (data.userToken in this.attendees) {
this.attendees.$set(data.userToken, data);
} else {
this.attendees.$add(data.userToken, data);
}
Not sure if there is a better way to accomplish this.
If you set attendees to an empty object ({}) it will not have a length attribute. That attribute is on Arrays.
If you set attendees to an empty array ([]) then you need to use $set (or really, I think you want .push()) – $add is intended for use on objects not on arrays.
I'm not quite following your last question – could you add more context?
EDIT 2:
The answer below was for Vue 1.x. For Vue 2.x and greater use:
this.$set(this.attendees, data.userToken, data);
More information here: https://v2.vuejs.org/v2/api/#Vue-set
EDIT:
Responding to your update, you should be able to just use $set in all cases. I.e. just do this:
this.attendees.$set(data.userToken, data);
As of version 0.6.0, this seems to be the correct way:
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
http://vuejs.org/guide/reactivity.html#Change_Detection_Caveats

Filter JSON data based on current URL route

The problem
I'm trying to filter json data and display only a portion of it on an Angular page, based on the page's current URL.
In detail
I have a list of 100 JSON objects, and each one looks like this:
{
"name": "Evangeline Perreault",
"age_1": 1,
"total_age": 1,
"photo_small": "img/400/001_400.jpg",
"photo_medium": "img/800/001_800.jpg",
"photo_large": "img/1200/001_1200.jpg",
"photo_extralarge": "img/1600/001_1600.jpg",
"video": 67443664,
"id": 1,
"quote": "test quote here and here",
"type": 1
},
The 'type' attribute is what I want to use to filter out the subsets of my data. With that in mind, I tried to setup my URL structure to tie the type attribute here to my url. Here is my route:
angular.module('100_Ages', ['mydirectives', 'ngResponsiveImages']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/100_Ages/nav/:personType', {templateUrl: 'partials/person-list.html', controller: NavListCtrl}).
otherwise({redirectTo: '/100_Ages'});
}]);
So, I have pointed the route to the 'type' field in my JSON and I tried writing a controller to tie the two together.
function NavListCtrl($scope, $routeParams, $http) {
$http.get('person.json').success(function(data) {
angular.forEach(data, function(person) {
if (person.type == $routeParams.personType)
$scope.person = person;
});
});
}
And here is my partial template:
<div class="nav_outer"><img class="nav_img" ng-src="{{person.photo_small}}" ng-alt="{{person.name}}" /></div>
I expected this to display all the matching images for the URL type I'm on. So, if I'm on "/100_Ages/nav/3", I expected all the images (roughly 10 pictures) from the objects with a type of "3" to display. However, it only displayed the last object with a type of "3".
So, I tried an ng-repeat like so:
<div class="nav_outer" ng-repeat="person in persons"><img class="nav_img" ng-src="{{person.photo_small}}" ng-alt="{{person.name}}" /></div>
I expected that to loop through and show all the matching images, but that made nothing at all show up.
I think my problem has to do with the angular.forEach, but I'm not sure how else to tie my JSON type to the page's typeid.
Thanks for any suggestions.
The ng-repeat should work if you push each item into an array. (Also, you are referring to a 'persons' object in the ng-repeat, which doesn't exist according to code provided). So, try this:
$http.get('person.json').success(function(data) {
$scope.persons = [];
angular.forEach(data, function(person) {
if (person.type == $routeParams.personType)
$scope.persons.push(person);
// or alternatively - this.push(person), with the optional 3rd param of $scope.persons (I don't really understand that, but whatever...)
});
});
Now with the array populated, your ng-repeat="person in persons" should work.
UPDATE:
If the success object was already an array of objects, then just set the scope object to the array - no need to iterate through them:
$http.get('person.json').success(function(data) {
$scope.persons = data;
})

Populate Dojo's datagrid with JsonRest and custom json arrays

I have a grid that I am creating drawing off a JSON data source that is formatted like this:
[{"user":{"username":"foo","url":"bar"}},
[{"product":{"name":"banana","price":"85"}},
{"product":{"name":"peach","price":"66"}},
{"product":{"name":"strawberry","price":"78"}}
]
]
But I cannot figure out how to tell datagrid to use the contents of the products to populate the datagrid. Here is my datagrid code:
<script>
require(["dojo/store/JsonRest"], function (JsonRest) {
myStore = new JsonRest({ target: 'myurl', handleAs: 'json'
});
});
require(["dojox/grid/DataGrid", "dojo/data/ObjectStore", "dojo/domReady!"
], function (DataGrid, ObjectStore) {
grid = new DataGrid({
store: dataStore = new ObjectStore({ objectStore: myStore }),
structure: [
{ name: "Procuct", field: "name", width: "200px" }
]
}, "grid3");
grid.startup();
});
</script>
<div id="grid3"></div>
I do not get any error, but I cannot see that the grid gets populated.
It is a similar question to THIS, but the data structure is a bit different.
I think it has something to do with your json structure.
The first part of your jsonArray is an object, the second an array:
[object,ArrayOfProducts]
How should DataGrid find the necessary data if you hide it in an array within an array & then inside the attribute product.
Try passing something simple via json like:
[{"name":"banana","price":"85"},
{"name":"peach","price":"66"},
{"name":"strawberry","price":"78"}]
Have you tried grid.renderArray(dataStore) to populate the grid with the conent ?
An option is to append a new property to the json object prior to dataStore.query() call. This can be accomplished with dojo/aspect. See article for other examples.
aspect.before(dataStore, "query", function(items) {
items.forEach(function(item) {
//Do something here. I'll combine two properties.
item.newProperty = item.propertyValueA + "-" item.propertyValueB;
return item;
});
return items;
});
When dataStore.query() is called, the function above is called above. This results in a new property be added to the json object. In the example above, the newProperty is a concatenation of propertyValueA and propertyValueB.
This may allow you to manipulate the json as needed.