I am currently working in a component which consists in a reactive form, and every field in the form returns an array of objects, and thet array of objects is different for every field. This is important because, when the form is filled, I need to create an URL in order to call an API with that data.
I have already done 2 methods that generate that URL, but they only work when the input is a text field, or when the data for that field is another type other than an array of objects. But all my inputs are multiselect using PrimeNG, so they return an array of objects. I show one input, but all of them are similar.
component.html
<span>Field 1: </span>
<span>
<p-multiSelect
[options]="options"
defaultLabel="Placeholder"
optionLabel="optionLabel"
display="chip"
formControlName="formControlName1"
>
</p-multiSelect>
</span>
The previous code returns this:
formControlName1: Array(2)
0: {foo: 'bar 1'}
1: {foo: 'bar 2'}
length: 2
[[Prototype]]: Array(0)
What I have tried so far are those two methods:
component.ts
onSubmit(form: any) {
const tree = this.router.createUrlTree([], { queryParams: form });
console.log(this.serializer.serialize(tree));
}
generateUrl() {
for (const control in this.myForm.controls) {
const val = this.myForm.controls[control].value;
if (val !== null) {
this.stringFinal += `${control}=${val}&`;
}
}
console.log(this.stringFinal);
}
Those two methods return mostly the same, but as I sais previously, they work when the input data is a text, not an array.
So my question is, how to access the the array of objects, and obtain every single data for the foo field.
I managed to make it work:
generateUrl() {
for (const control in this.myForm.controls) {
const val = this.myForm.controls[control].value;
if (val !== null) {
val.forEach((element: any) => {
this.stringFinal += `${control}=${element}&`;
});
}
}
console.log(this.stringFinal);
}
Related
I have two differents arrays with those models
export interface ProfileMaterialCategory{
id?: string
materialCategory_Name?: string
materialCategory_Id: string
quantity: string
profileId?: string
}
And
import { Category } from "./material-asset-model";
export interface UserAssetsCount {
category: Category
userAssetsCount: number;
}
In my .ts file I'm trying to create a method for getting a combined records for all matching materialCategory_Id / category.Id.
The returned array must have this syntax
[category.name:string, userAssetsCount:string, quantity:string]
I tried with filter and includes but it his not working, or I don't take the problem correctly.
This is the arguments that the method takes.
GetMatch(profileAssets: ProfileMaterialCategory[], userAssets: UserAssetsCount[]): Object[] {
var filteredArray = userAssetsAllowed.filter(
as
setAllowed => userAssetsCount.some(
assetCount => assetCount.category.id.toString() == assetAllowed.materialCategory_Id)
return filteredArray
}
When I filter, I receive an element of the array when the condition is true, but it's an element from one of the array, I can't take value from the other.
So, I've succed in one way to do what I needed.
First I created a new DTO model:
export interface UserAssetMatch {
category: Category,
allowed: string,
owned: number
}
Then I make a foreach() loop on one array, and use a filter() on the second array. I keep the matching elements in an intermediate array.
If there is something in the intermediate array, I push the informations that I need in the DTO.
GetMatch(userAssetsAllowed: ProfileMaterialCategory[], userAssetsCount: UserAssetsCount[]): UserAssetMatch[] {
var matchArray: UserAssetMatch[] = [];
userAssetsCount.forEach(userAsset => {
var profileAssets = userAssetsAllowed.filter(userAssetAllowed => userAsset.category.id.toString() == userAssetAllowed.materialCategory_Id);
if (profileAssets.length > 0) {
matchArray.push({
category: userAsset.category,
owned: userAsset.userAssetsCount,
allowed: profileAssets[0].quantity
})
}
});
return matchArray;
I don't think it is a good way to do, but it works. If someone have a better/faster/cleaner way to do, please tell me :)
In my react app I need to return a line which will be created based on a list.
Here is the object,
searchCriteria: {
op_company: "039",
doc_type: "ALL"
}
and in my UI, i need to show it as a paragraph with bold values. So the hard coded code would be like below
<p>Download request for op_company: <b>{searchCriteria.op_company}</b>, doc_type: <b>{searchCriteria.doc_type}</b></p>
But the object(searchCriteria) will be changed based on the user request. So I tried like below.
const getSearchCriteria = (criteria) => {
let searchCriteria = []
searchCriteria.push('Download request for')
Object.keys(criteria).forEach((key) => {
if(criteria[key] !== '') {
searchCriteria.push(` ${key}: ${criteria[key]},`)
}
});
return searchCriteria;
}
return (
<p>
{getSearchCriteria(searchCriteria).map((item) => <span key = {item}>{item}</span>)}
</p>
);
here i'm getting the expected output. But I can't get the value as bold (highlighted). Is there another way to directly deal with html elements?
I have an object:
{ //birthdaysObject
'2000':{
'January':
[{
name: 'Jerry'
},
{
name: 'Chris'
}]
},
'2001':{
'February':
[{
name: 'John'
}]
}
When I go to update the redux store it is replacing the entire year (eg. '2000') object with the new one that I send to my reducer.
How can I push the the nested array of objects without replacing the entire year object?
My reducer currently looks like:
return Object.assign({}, state, {
...state,
birthdays: Object.assign({}, state.birthdays, {
...state.birthdays,
...birthdays
})
});
Where ...birthdays would be another object in the same format as the first code snippet.
I am also open to suggestions about the structure of my data, normalizing, etc.
Any help would be appreciated.
The object keys in the birthdaysObject are unknown and are assigned when iterating through a separate object. I've tried kolodny/immutability-helper however the $merge function is returning the same results as what my reducer is already doing.
I had the same problem some time ago.
Follow the way I done it.
You have an object, but I think you should have an array of objects.
I also have different names on variables, but this should not be a problem to understand the logic
//do a copy of the array first
let newSubscriptions = state.customer.master.subscriptions.slice();
//for the value you want to change, find it's position in the array first
const indexInSubscriptions = newSubscriptions.map(function(item) {
return item.id;
}).indexOf( action.id);
//get the child you want to edit and keep it in a new variable
const under_edit_subscription = state.customer.master.subscriptions[indexInSubscriptions];
//go again over the array and where is the value at the index find above, replace the value
newSubscriptions = newSubscriptions.map((item, i) =>
i === indexInSubscriptions ? under_edit_subscription : item
)
//add the whole array into the state
return {
...state,
customer: {
...state.customer,
master: {
...state.customer.master,
subscriptions : newSubscriptions
}
}
}
In one component I can filter my array using the following:
// Array of product objects
const result = products.filter(p => p.name.includes('val'));
and value of products remains same as the first value but filtered value stores in result.
But in the following code, filter() filters array of strings itself:
// Array of strings
const result = strs.filter(s => s.includes('val'));
The question is how can I filter strings and return result without modifying the strs itself?
Note: I tried with array.filter(function() { return res; }); but didn't make any change.
It returns the filtered ones and don't change the actual array. You are doing something wrong
const strs = ['valval', 'bal', 'gal', 'dalval'];
const result = strs.filter(s => s.includes('val'));
console.log(strs);
console.log(result);
First thing we need to know is, if we filter our list we loose our original data
products: any[] = [
{
"productId": 1,
"productName": "foo-bar",
"price": 32.99
}
]
and can't get it back without re-getting the data from it's source so we have to make another list to store the filtered list.
filteredProduce: any[];
Next if you are working to show a list of filtered product on a grid or something like this we need a way to know when the user changes the filter criteria. we could use event binding and watch for key presses or value changes, but an easier way is to change our _listFilter property into a getter and setter, like this
get listFilter: string {
return this._listFilter;
}
set listFilter(value:string) {
this._listFilter= value;
}
next we want to set our filteredProducts array to the filtered list of products like this
set listFilter(value:string) {
this._listFilter= value;
this.filteredProducts = this._listFilter? this.performFilter(this._listFilter) : this.products;
}
in preceding code we are using js conditional operator to handle the posibility that _listFilterstring is empty, null or undefined.
Next we use this.performFilter(this._listFilter) to filter our list.
performFilter(filterBy: string): any[] {
filterBy = filterBy.toLocaleLowerCase();
return this.products.filter((product: any) =>
product.productName.toLocaleLowerCase().indexOf(filterBy) !== -1);
}
Finally we should assign the main list of products to the filteredProducts and _listFilter to what we want.
constructor() {
this.filteredProducts = this.products;
this._listFilter= 'foo-bar';
}
last step is to change our template to bind to our filteredProducts property.
I'm trying to get an idea how can I force Play Scala framework form mapper to save null values in array property.
Example. Request body (print out of snippet below):
AnyContentAsJson({
"entities":["ENI","GDF Suez","Procter & Gamble"],
"entityValues":[null,"42",null]
})
Resulting value of entityValues property after binding:
List(Some(42.0))
But I want to see:
List(None, Some(42.0), None)
Code snippet of controller:
def actionX = Action {implicit request =>
println(request.body)
TaskForm.form.bindFromRequest.fold(
formWithErrors => {
BadRequest("error")
},
taskData => {
println(taskData.entityValues)
}
)
}
Form class with mapping:
case class TaskForm(entities: List[String],
entityValues: List[Option[Double]]) { }
object TaskForm {
val map = mapping(
"entities" -> list(text),
"entityValues" -> list(optional(of(doubleFormat)))
)(TaskForm.apply)(TaskForm.unapply)
val form = Form(
map
)
}
I also tried some combinations of optional and default mapping parameters, but a result is still the same.
Using 0 or any another numeric value instead of null is not a case.
Does anyone have any ideas how to implement such form behaviour?
Thanks in advance for your time and attention.
It looks like you're sending JSON to a form endpoint. While this will work for simple JSON structures, you get no control over how it is done and hence get problems like the one you're seeing.
I'd be explicit about being a JSON-endpoint, and then you can define your own Reads[Option[Double]] that works precisely how you want it to:
First, define the implicits at the Controller level; here's where we get to control the null-handling; it ends up being pretty easy:
implicit val optionalDoubleReads = new Reads[Option[Double]] {
def reads(json: JsValue) = json match {
case JsNumber(n) => JsSuccess(Some(n.toDouble))
case JsString(n) => JsSuccess(Some(n.toDouble))
case JsNull => JsSuccess(None) // The important one
case _ => JsError("error.expected.jsnumber")
}
}
implicit val taskReads = Json.reads[TaskForm]
With that done, we modify your Action to require JSON (using parse.json). The function itself remains remarkably similar to the original form-binding fold:
def actionX = Action(parse.json) { implicit request =>
println(request.body)
request.body.validate[TaskForm].fold(
jsonErrors => {
BadRequest(s"Error: $jsonErrors")
},
taskData => {
println(taskData.entityValues)
Ok(taskData.entityValues.toString)
}
)
}