How to avoid using Object.assign? - javascript

I am running a function to get a list of categories and for each category, get an id and run another function to get the number of 'links' in that particular category I have got the id.
For that, I have the following code:
ngOnInit(): void
{
this.categories = this.categoryService.getCategories();
const example = this.categories.mergeMap((categor) => categor.map((myCateg) =>
{
this.categoryService.countCategoryLinks(myCateg.id)
.map(numlinks => Object.assign(myCateg,{numLinks: numlinks}))
.subscribe(valeur => console.log(valeur));
return myCateg.id
}));
example.subscribe(val => console.log("valeur2: "+val));
}
where getCategories() is:
getCategories(): Observable<any>
{
return this.category.find({where: {clientId: this.userApi.getCurrentId()}})
};
and countCategoryLinks() is:
countCategoryLinks(id: number): Observable<any>
{
return this.category.countLinks(id)
};
It appears, as shown in the screenshot below:
that numlinks is an Object. Of course this is done by Object.assign.
Is there a way to have the "count" inserted like categoryName ot clientId instead of an Object?
My goal is to be able to show all the values in a template:
<tr *ngFor="let category of categories | async; let id = index">
<td>
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect mdl-data-table__select"
for="row[3]">
<input type="checkbox" id="row[3]" class="mdl-checkbox__input"/>
</label>
</td>
<td class="mdl-data-table__cell--non-numeric"><a [routerLink]="['/category/links']"></a>{{
category.categoryName }}
</td>
<td class="mdl-data-table__cell--non-numeric">{{category.numLinks}}</td>
<td #category.id><i (click)="deleteCategory(id)" class="material-icons">delete</i></td>
</tr>

If I understand this correctly, then you can simply do this:
.map(numlinks => {
myCateg.numLinks = numlinks.count;
return myCateg;
})
Instead of
.map(numlinks => Object.assign(myCateg,{numLinks: numlinks}))
Therefore your code should become something like this:
{
this.categories = this.categoryService.getCategories();
const example = this.categories
.mergeMap((categor) => categor
.map((myCateg) => {
this.categoryService
.countCategoryLinks(myCateg.id)
.map(numlinks => {
myCateg.numLinks = numlinks.count;
return myCateg;
})
.subscribe(valeur => console.log(valeur));
return myCateg.id;
}));
example.subscribe(val => console.log("valeur2: " + val));
}

Change this line:
.map(numlinks => Object.assign(myCateg,{numLinks: numlinks}))
to:
.map(numlinks => Object.assign(myCateg, { numLinks: numlinks.count }))

Related

How to filter search input for two categories?

Following filter function computes the input of a search input field to only display elements, with a similar title like the search input:
const filteredNews = computed(() => {
if (state.filter) {
return props.news.filter(item => {
return state.filter.toLowerCase().split(" ").every(v => item.title.toLowerCase().includes(v))
});
} else {
return props.news;
}
})
search input field:
<input class="filter-input" type="search" placeholder="Suche" v-model="state.filter">
the elements are then displayed in a v-for loop:
<div class="news-gallery" v-for="(card, key) in filteredNews" :key=key>
// items...
</div>
Now I want to filter not only for title but also location. How would I need to change the filter function to achieve that?
Something like:
const filteredNews = computed(() => {
if (state.filter) {
return props.news.filter(item => {
return state.filter.toLowerCase().split(" ").every(v => {
item.title.toLowerCase().includes(v),
item.location.toLowerCase().includes(v)
})
});
} else {
return props.news;
}
})
I found a very simple way. Just change:
item.title.toLowerCase().includes(v)
to:
item.title.toLowerCase().includes(v) || item.location.toLowerCase().includes(v))

I'm coding for the function to see the Faculty's details and it shows number of majors of that faculty. How to count array with in template?

I'm coding for the function to see the Faculty's details and it shows number of majors of that faculty. I don't know how to count array majors with v-if and v-for. Please help me fix that. Many thanks.
Template tag
<tr>
<td>Sumary:
<strong v-for="major in majors">
<strong v-if="form.faculty_code==majors.major_faculty">
{{ major }}
</strong>
</strong>
</td>
</tr>
Script tag
data() {
return {
faculties:[],
majors:[],
form: new Form({
faculty_id:'',
faculty_code:'',
faculty_name:'',
faculty_status: '',
}),
};
},
mounted() {
this.fetchMajors();
this.fetchFaculties();
},
methods: {
fetchFaculties(page_url) {
let vm = this;
page_url = '../../api/admin/edu-faculty/faculty/'+this.currentEntries+'?page='+this.pagination.current_page;
fetch(page_url)
.then(res => res.json())
.then(res => {
this.faculties = res.data;
this.pagination = res.meta;
})
.catch(err => console.log(err));
},
fetchMajors(page_url) {
let vm = this;
page_url = '../../api/admin/edu-major/chuyen-nganh/major';
fetch(page_url)
.then(res => res.json())
.then(res => {
this.majors = res.data;
})
.catch(err => console.log(err));
},
}
To only get the amount of majors with the given faculty code you can use in your mounted() function
this.total.majors = 0;
this.majors.forEach((element) => {
element.major_faculty == this.faculty_code ? this.total_majors += 1 : ""
});
Thanks for watching and help me. I've found the solution for this.
Template tag
<tr>
<td>Sumary:
<strong>
{{ countA.length }}
</strong>
</td>
Script tag
computed: {
countA() {
return this.majors.filter(major => major.major_faculty==this.form.faculty_code);
}
},

object of array not update in state

I have form where user can add as much as he wants documents. Each document have several inputs.
And I'm trying to get each document inputs values and put it to state as array of objects.
State should look like:
[
{
id: 'UGN2WP68P1',
name: 'passport',
placeIssue: 'paris'
},
{
id: 'TD0KUWWIM6',
name: 'shengen visa',
placeIssue: 'paris'
}
...
]
So I write a function which is called on inputs change. He check are there object with same id, if there is no object with same id he creates new and add to array, if object exist with same id then he update object:
const addNewDocumentObj = (id, type, val) => {
// Get object with same id
let docObj = addedDocsArr.filter( el => el.id === id)
// If there is no object with same id, creates new one
if (docObj.length === 0) {
if (type === 'name'){
let obj = {
id: id,
docId: val.id
}
setAddedDocsArr(addedDocsArr.concat(obj))
} else if (type === 'placeIssue') {
let obj = {
id: id,
placeIssue: val
}
setAddedDocsArr(addedDocsArr.concat(obj))
}
// If object exist with same id then updates with new value and adds to array
} else {
if (type === 'name'){
let newObject = Object.assign(docObj, {name: val.id})
let newArray = addedDocsArr.filter(el => el.id !== id)
setAddedDocsArr(newArray.concat(newObject))
} else if (type === 'placeIssue') {
let newObject = Object.assign(docObj, {placeIssue: val})
let newArray = addedDocsArr.filter(el => el.id !== id)
setAddedDocsArr(newArray.concat(newObject))
}
}
}
But it doesn't work, and I can't understand why, maybe my logic is bad and there is better practise?
UPDATE:
In React debugger I noticed how state changes. If I add select document name, in state object looks like that:
{name: 'passport', id: 'UGN2WP68P1'}
If I enter document place of issue value. Then object changes and show data like that:
{placeIssue: 'paris', id: 'UGN2WP68P1'}
But result should be:
{name: 'passport', placeIssue: 'paris', id: 'UGN2WP68P1'}
So it looks like that object not updated but created new one
Maybe you need something like:
const addNewDocumentObj = (id, type, val) => {
// Get object with same id
let docObj = addedDocsArr.find(el => el.id === id)
// If there is no object with same id, creates new one
if (!docObj) {
docObj = { id, placeIssue: val }
// and pushes it to addedDocsArray
addedDocsArr.push(docObj)
}
if (type === 'name') {
docObj.name = val.id
} else if (type === 'placeIssue') {
docObj.placeIssue = val
}
setAddedDocsArr(addedDocsArr)
}
First of all, why are you using filter if you are actually try to find something in array? Just use find.
Second, if object with given id is already exists, there is no need to filter your array and then put that object back... Just find that object in array and update it! It is already in your array! Remember that Array contains references to your objects, so when you grab your object from the Array and edit it, your edit the same object that Array have.
Last one, Idk what logic your setAddedDocsArr function have. In my example I assume that the only thing it does is set its argument (newArray) to the variable named addedDocsArr. So instead of that, in situation where object with given id is not present, I just push it in old array.
Finished App:
Implementation of Handle submit:
const handleSubmit = (event) => {
event.preventDefault();
if (!uid) {
alert("Please enter the ID");
return;
}
let existingRecords = docs.filter((doc) => doc.id === uid);
if (!existingRecords.length) {
let newRecord = {
id: uid,
name: name,
issuePlace: place
};
setDocs([...docs, newRecord]);
setId("");
setName("");
setPlace("");
} else {
let unmodifiedRecords = docs.filter((doc) => doc.id !== uid);
if (name) {
existingRecords[0].name = name;
}
if (place) {
existingRecords[0].issuePlace = place;
}
unmodifiedRecords.push(existingRecords[0]);
setDocs(unmodifiedRecords);
setId("");
setName("");
setPlace("");
}
};
And Here is the full finished example:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [docs, setDocs] = useState([
{ id: "1", name: "passport", issuePlace: "delhi" }
]);
const [uid, setId] = useState("");
const [name, setName] = useState("");
const [place, setPlace] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
if (!uid) {
alert("Please enter the ID");
return;
}
let existingRecords = docs.filter((doc) => doc.id === uid);
if (!existingRecords.length) {
let newRecord = {
id: uid,
name: name,
issuePlace: place
};
setDocs([...docs, newRecord]);
setId("");
setName("");
setPlace("");
} else {
let unmodifiedRecords = docs.filter((doc) => doc.id !== uid);
if (name) {
existingRecords[0].name = name;
}
if (place) {
existingRecords[0].issuePlace = place;
}
unmodifiedRecords.push(existingRecords[0]);
setDocs(unmodifiedRecords);
setId("");
setName("");
setPlace("");
}
};
return (
<div className="App">
<form onSubmit={handleSubmit}>
<table>
<tr>
<td>
<label>ID: </label>
</td>
<td>
<input
value={uid}
onChange={(e) => {
setId(e.target.value);
}}
/>
</td>
</tr>
<tr>
<td>
<label>Name: </label>
</td>
<td>
<input
value={name}
onChange={(e) => {
setName(e.target.value);
}}
/>
</td>
</tr>
<tr>
<td>
<label>Isuue Place: </label>
</td>
<td>
<input
value={place}
onChange={(e) => {
setPlace(e.target.value);
}}
/>
</td>
</tr>
<tr>
<td>
<button type="submit">Submit</button>
</td>
</tr>
</table>
</form>
{docs.map((doc) => (
<div className="records">
<span>{"ID:" + doc.id + " "}</span>
<span>{"Name:" + doc.name + " "}</span>
<span>{"Issue Place:" + doc.issuePlace + " "}</span>
</div>
))}
</div>
);
}
Check out finished example with source code at: Codesandbox Link
I find a pretty easy way how to solve this problem. I read documentations of react forms and find multiple inputs idea React Forms
So I changed my code to:
// Update or add information of added documents inputs
const addNewDocumentObj = (id, e, name) => {
const newInput = addedDocsArr.map(el => {
if(id === el.id) {
if(name === 'name'){
el[name] = e.target.value
} else if (name === 'placeIssue'){
el[name] = e.target.value
}
}
return el
})
setAddedDocsArr(newInput);
}
// Add new document inputs
const addNewDocument = () => {
let blockId = randomNumber(10, true, false)
setAddedDocsArr([...addedDocsArr, {id: blockId, name: '', placeIssue: ''}])
}
And it works perfectly!

TypeError: this.receivedSummons.map is not a function Angular

I am trying to binding dynamic checkboxes on FormArray, when I reproduce on stackblitz its work, but show me error on my IDE, and I am using Array.prototype.map not the rxjs map and I got error in console.log:
core.js:9110 ERROR TypeError: this.receivedSummons.map is not a function
when console.log(this.receivedSummons), I got this,
when console.log(JSON.stringify(this.receivedSummons.data));, I got this,
[![console.log(JSON.stringify(this.receivedSummons.data));][2]][2]
this is what I tried:
Ts File
receivedSummons: SummonModel[];
selectedSummons: string;
checkboxForm: FormGroup;
get formReceivedSummons() {
return this.checkboxForm.get('receivedSummons') as FormArray;
}
formReceivedSummonsItems(i: number) {
return (this.formReceivedSummons.controls[i].get('items')) as FormArray;
}
constructor(
private inquiryStore: InquiryStoreService,
private formBuilder: FormBuilder
) { }
ngOnInit() {
this.checkboxForm = this.formBuilder.group({
receivedSummons: this.formBuilder.array([])
});
this.getReceivedSummons();
}
getReceivedSummons() {
this.inquiryStore.summons$.subscribe(receivedSummons => {
this.receivedSummons = receivedSummons;
this.addCheckboxes();
});
}
addCheckboxes() {
this.formReceivedSummons.setValue([]);
this.receivedSummons.map(checkbox => {
const group = this.formBuilder.group({
header: [checkbox.header.transactionId],
items: this.formBuilder.array([], [minSelectedCheckboxes(1)])
});
checkbox.data.items.map(items => {
(group.get('items') as FormArray).push(this.formBuilder.group({
name: [items.itemNo],
isChecked: ['']
}));
});
this.formReceivedSummons.push(group);
});
}
submitSelectedCheckboxes() {
console.log(this.checkboxForm.value);
}
}
function minSelectedCheckboxes(min = 1) {
const validator: ValidatorFn = (formArray: FormArray) => {
const totalSelected = formArray.controls
.map(control => control.value)
.reduce((prev, next) => (next ? prev + next : prev), 0);
return totalSelected >= min ? null : { required: true };
};
return validator;
}
Html File
<form [formGroup]="checkboxForm" (ngSubmit)="submitSelectedCheckboxes()">
<ng-container formArrayName="receivedSummons" *ngFor="let summon of formReceivedSummons.controls; let i = index">
<ng-container [formGroup]="summon">
<p>{{summon.value.header}}</p>
<ng-container formArrayName="items"
*ngFor="let item of formReceivedSummonsItems(i).controls; let j = index">
<ng-container [formGroup]="item">
<input type="checkbox" formControlName="isChecked"> {{item.value.name}}
</ng-container>
</ng-container>
</ng-container>
<div *ngIf="!summon.valid">At least one order must be selected</div>
</ng-container>
<br>
<button [disabled]="!checkboxForm.valid">submit</button>
</form>
this is what I reproduced using stackblitz, I could use some guidance and suggestion on how to solve this.Thanks
From the JSON you posted, looks like this.receivedSummons is not an array, .map works over an array, change your code as
this.receivedSummons.data.items.map(checkbox =

Rx timer state not updating in view in Cycle.js

I'm starting a timer when someone clicks a button that I intend to use as the opacity for some element. When I use do to trace the value I can see it spitting out to the console 40 times, but in the view the number stays put. Not sure where I'm going wrong here:
let intent = ({ DOM }) => ({
clickLogin$: DOM.select('.sign-in').events('click').map(ev => true)
})
let model = ({ clickLogin$ }) =>
Rx.Observable.combineLatest(
clickLogin$.startWith(false),
clickLogin$.map(x =>
Rx.Observable.timer(1, 1)
).switch().startWith(0).take(40),
(signingIn, fadeValue) => ({ signingIn, fadeValue })
)
let view = (state$) => {
return state$.do(
x => console.log(x.fadeValue)) // this fires |--1-2-3-4-5-6-7-8-->
.map(({ signingIn, fadeValue }) =>
div(`.app`, [
div([fadeValue]), // this value does not change
If(signingIn,
div(`.overlay`, {
style: {
backgroundColor: `rgba(0, 0, 0, 0.${fadeValue})` // nor does this
}
})
)
])
)
}
let main = (sources) => {
let view$ = view(model(intent(sources)))
return {
DOM: view$,
history: sources.History,
Props: sources.Props,
}
}
UPDATE: Turns out having a small error in hyperscript caused it strange behaviour. I didn't even include it in my example because I didn't think it was relevant.
div(`content`, [ `testing` ])
Simply changing the above to (adding indication of class)
div(`.content`, [ `testing` ])
Caused everything to magically work.
This is probably not a full answer, but it helps identifying the problem. I removed the If part of the view code generation, and added repeat, put that in tricycle and you can see that the fadeValue is generated sequentially as expected.
var Cycle = require('#cycle/core');
var CycleDOM = require('#cycle/dom');
var Rx = require('rx');
var makeDOMDriver = CycleDOM.makeDOMDriver;
var div = CycleDOM.div;
var sources = {
DOM: makeDOMDriver('.app')
};
let main = (sources) => {
let intent = ({ DOM }) => ({
clickLogin$: Rx.Observable.interval(3000).take(5).share()
})
let model = ({ clickLogin$ }) =>
Rx.Observable.combineLatest(
clickLogin$.startWith(false),
clickLogin$.flatMapLatest(function (x) {
return Rx.Observable.timer(200, 200);
}).take(10).repeat(),
(signingIn, fadeValue) => ({ signingIn, fadeValue })
)
let view = (state$) => {
return state$.do(
x => console.log(x.fadeValue)) // this fires |--1-2-3-4-5-6-7-8-->
.map(({ signingIn, fadeValue }) =>
div(`.app`, [
div([fadeValue]) // this value does not change
])
)
}
let view$ = view(model(intent(sources)))
return {
DOM: view$,
history: sources.History,
Props: sources.Props,
}
}

Categories