Submit Javascript Object with Array of Object inside to the API - javascript

Good day everyone,
I need a help on how to submit an object with array of object inside and the array of object should be from a checkboxes I've made.
This is my sample checkbox
<input type="checkbox" name="user.preferences" value="Hard" />
<input type="checkbox" name="user.preferences" value="Soft" />
<input type="checkbox" name="user.preferences" value="Small" />
My data javascript looks like this:
user:{
preferences: []
}
When I alert theuser using JSON.stringify, I can able to see a result something like this.
{"preferences": ["Soft","Small"]}
But the problem is, the api that I'm using needs a format like this:
{
"preferences": [
{
"preference": "Hard"
},
{
"preference": "Soft"
},
// so on and so forth
]
}
Please somebody help me. Thank you

You should .map each string in preferences to an object with that preference as its key/value:
const user = {
"preferences": ["Soft", "Small"]
};
user.preferences = user.preferences
.map(preference => ({ preference }));
console.log(user);

Related

Angular HttpClient - accessing value buried in response data

I am accessing an online API and want to use the text value to populate a ngb-typeahead dropdown. There is a working example on the Angular Bootstrap website using Wikipedia, but the returned data from the Wikipedia API is different to the data I am getting from a geocoding API. The data I get is returned in this format:
{
"suggestions": [
{
"text": "23 Queen Charlotte Drive, Aotea, Porirua, Wellington, 5024, NZL",
"magicKey": "dHA9MCNsb2M9NDMwNzcyNzQjbG5nPTMzI2huPTIzI2xicz0xMDk6NDg1NDQwMzU=",
"isCollection": false
},
{
"text": "23 Queen Mary Avenue, Epsom, Auckland, 1023, NZL",
"magicKey": "dHA9MCNsb2M9NDMwNDY4MjUjbG5nPTMzI2ZhPTE0NDE3OTIjaG49MjMjbGJzPTEwOTo0ODU0NDMyNA==",
"isCollection": false
},
I have been trying to access text in response data with the following:
return this.http
.get<any>(GIS_URL, {params: GIS_PARAMS.set('text', term)}).pipe(
map(response => response.suggestions)
);
I have also read the Angular tutorial here on dealing with response data, but the difference in the example is that they are getting an array of Hero's whereas I am getting an object containing an array of suggestions.
The typeahead looks like:
HTML
<fieldset class="form-inline">
<div class="form-group">
<label for="typeahead-http">Search for a wiki page:</label>
<input id="typeahead-http" type="text" class="form-control mx-sm-3" [class.is-invalid]="searchFailed" [(ngModel)]="model" [ngbTypeahead]="search" placeholder="Wikipedia search" />
<small *ngIf="searching" class="form-text text-muted">searching...</small>
<div class="invalid-feedback" *ngIf="searchFailed">Sorry, suggestions could not be loaded.</div>
</div>
</fieldset>
<hr>
<pre>Model: {{ model | json }}</pre>
Full code on StackBlitz is here.
I am new to Angular, so a verbose answer would be great.
You need to specify resultFormatter and inputFormatter on the typeahead input (refer to Typeahead).
Explanation
Your search method in the service returns a list of suggestion Objects which each look like:
{
isCollection: ...
magicKey: ...
text: ...
}
However by default the typeahead control expects a list of strings, hence it displays your objects as [Object object].
You need to tell the typeahead control how to determine a string value from your object, you do this via resultFormatter and inputFormatter.
These inputs take a function, which has the object as an input and the string display value as its output.
formatter below is that function, it will be called for each item displayed in the list. If you expand it to a normal function you can put a breakpoint in it and see it being called in this manner.
Solution
<input id="typeahead-http" ... [inputFormatter]="formatter" [resultFormatter]="formatter"/>
TypeScript file:
formatter = (item:any) => item.text as string;
Updated StackBlitz
https://stackblitz.com/edit/so-typeahead?file=src%2Fapp%2Ftypeahead-http.ts
Follow-up questions
item in the formatter:
Consider:
formatter = (item:any) => item.text as string;
is shorthand for:
function format(item: any){
return item.text as string;
}
They typeahead control/directive iterates the items returned by search(..) and calls this method which each one. The results are displayed in the select list.
map(response => response.suggestions)
The response from the service is an object like:
{ // object
suggestions:
[
{ ..., text: 'Place 1' },
{ ..., text: 'Place 2' }
]
}
That is an object containing a list named suggestions. The typeahead expects a list only, so the map transforms the object containing list => list only.
Does the formatter that you have defined do both input and result?
Yes, as it is assigned to both [inputFormatter] and [resultFormatter] in the template.
Alternative answer
The mapping is done entirely in the service:
return this.http
.get<any>(GIS_URL, {params: GIS_PARAMS.set('text', term)}).pipe(
map(response => response.suggestions.map(suggestion => suggestion.text)),
);
Each response object is mapped to the list of suggestions. Each suggestion is mapped (using JavaScript map) to its text value.
You can use this solution provided you don't need access to any of the other suggestion properties outside of the service.

Svelte Each loop is not updated while object changed dynamically

When i'm trying to change data dynamically of the each loop variable is not refreshing the DOM elements. How I can redraw the each loop values
<script>
// default messages object
let messages =[{
"name":"John",
"message":"Hi",
"checked": false
},
{
"name":"Anthony",
"message":"welcome",
"checked": true
},
{
"name":"David",
"message":"thank you",
"checked": false
}]
//click function to change the values dynamically
function test(){
messages.map(ob=>{
ob.checked = true;
})
console.log(messages)
}
</script>
template code
<div>
{#each messages as m}
<div>{m.checked}
<input type="checkbox" bind:checked={m.checked}>
{m.name}
</div>
{/each}
<br>
<button on:click={()=>{test()}}> check all values</button>
</div>
link for snippet: https://svelte.dev/repl/887e7e2de4114ec1bb3625c264b9a62c?version=3.24.1
Any help would be appreciated! Thank you :)
You can't update an array/object just by changing its attribute in svelte.
A simple rule of thumb: the name of the updated variable must appear on the left hand side of the assignment.
Updating Arrays and Objects
map returns a new array, in your case you got a new array from undefined, you need assign the result and correctly return the new object
function test(){
messages = messages.map(obj => ({
...obj,
checked: true
}));
}

Using v-bind and v-on instead of v-model vuejs

I was using v-model to handle inputs in a form, I had to change it to bind props values, at first input was like
<input type="text" class="form-control" placeholder="" v-model="username">
and now it looks like
<input type="text" class="form-control" placeholder="" v-bind:value="modelData.username" v-on:input="username = $event.target.value">
modelData is coming in props. and it has username.
when Using model, in data, i had defined
data: () => {
return {
username: ""
}
},
and It was working fine, but after changing it to v-bind and v-on,
My question is how I can now get the value of username in methods? as in past, I was getting it as this.username to get the value and also clear it as well but now how I can get username in
methods: {}
I have a button to save input
<button class="btn btn-secondary" type="button" #click="validateFormAndSave($event)">Save</button>
When validateFormAndSave get called I can have this.username right now I cannot get the value? But the Vue Doc says v-model and v-bind:value& v-on:input are the same?
UPDATE:
There can be and cannot be some value already there in username, as it being filled with props value, So v-on:input="username = $event.target.value" don't get the already written value but the only new one you entered? or edit it? Why is it so? is there any method for just to get what anyone typed in there or already been typed?
UPDATE:
This is getting very ambiguous. So for now.
1. I can set v-bind:value, But I cannot get it in methods without editing it.
2. I cannot set this.username = "" and it will not be removed from input as well.
3. #input only get what you newly typed not what already in there
v-model is just syntax sugar for =>
<input :value="modelValue" #input="modelValue = $event.target.value"/>
If you want something else, it's very easy to do. Just change the update side to onInput:
<input
class="form-control"
:value="username"
#input="username = $event.target.value"
>
Then
data: () => {
return {
username: ""
}
},
mounted()
{
this.username = this.modelData.username;
},
methods:{
consoleUsername() {
console.log(this.username);
}
}
The best possible solution can be when you are getting your data from props and also loading it a form for v-models.
Using watch feature of Vue component.
First I added props as
export default {
props: ["vendorModelData"],
and then I pass it through the watch to v-model
watch: {
vendorModelData() {
this.updatePropsValue(this.vendorModelData)
console.log("data updated")
}
},
in this way, it always loads differently when Props get changed. This way I got be using v-model as well as load data from props to it.
I found it useful for me.

Vue.js 2 - retrieve form elements from server and render CRUD

I'm Vue.js newbie and my task is:
make an ajax call (GET) to server, using RESTful API (Laravel on background)
retrieve a (JSON) list of Form CRUD items in array (like checkbox, input text, textarea...) with their properties (value, checked, custom classes...)
render CRUD form with these form items maybe using Vue's loop
I'm wondering if it could be rendered using components somehow. But I don't know the correct way.
Frankly, I exactly don't know how to solve this problem with Vue.js - rendering items from array and each item has it's own markup and properties (checkbox has it's own, textbox, select, textarea...).
I'm building a web application based on CRUD operations and I'm trying to write universal components. The easiest way is to do a special component with hard-written sub-components for each subpage, but I don't like this way if not needed.
Thank you!
EDIT: I don't have much code yet, but this is where I am...
<script>
// ./components/CrutList.vue
export default {
mounted() {},
data() {
return {
items: []
}
},
props: ['resource'],
methods: {
getItems() {
var resource = this.$resource('api/'+this.resource+'{/id}');
resource.get({}).then(function(items){
if(items.body.status == 'success'){
this.items = items.body.items;
}
}).bind(this);
},
deleteItem(item) {
// perform CRUD operation DELETE
alert('delete action');
}
}
}
</script>
My idea is using CrudList component to CRUD listing...
<crud-list resource="orders">
In laravel I do something like this:
return response()->json([
'status' => 'success',
'items' => [
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'label' => "Checkbox č.1",
'name' => 'checkbox1'
]
],
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'label' => "Checkbox č.2",
'name' => 'checkbox2'
]
],
[
'itemComponent' => 'checkbox',
'props' => [
'checked' => true,
'name' => 'checkbox3'
]
],
],
]);
...it's very simplified, but it's just example of what I'm doing.
Now the problem is:
take the 'itemComponent' part from the returned array item (this is in a loop),
if it's a checkbox, take (for example) Checkbox.vue component, fill it with properties ('props' part of the array item)
I read about slots, but it's not what I'm looking for. Is there something I can use for dynamic components?
Check out this jsFiddle working example for dynamic forms:
https://jsfiddle.net/mani04/kr8w4n73/1/
You can do it easily by using a lot of v-ifs for each and every form element type you might get from server. It is a bit cumbersome but I can't find any other way.
In the above example, I have the form structure as follows:
var formItems = [{
input_type: "text",
input_label: "Login",
values: {
value: "your_name#example.com"
}
},
{...},
{...}];
Once you have that data, then it is a matter of iterating through formItems, checking input_type and activating the relevant form control.
Here is how my dynamic form template looks like, for the above input:
<div v-for="formItem in formValues">
<div v-if="formItem.input_type == 'text'">
<input type="text" v-model="formItem.values.value">
</div>
<div v-if="formItem.input_type == 'password'">
<input type="password" v-model="formItem.values.value">
</div>
<div v-if="formItem.input_type == 'checkbox'">
<input type="checkbox" v-model="formItem.values.checked">
{{formItem.values.label}}
</div>
</div>
My jsFiddle example uses form-horizontal from bootstrap, and I am also able to display the labels well. If I put that in the example above, it will get cluttered and will not let you see how it works.
Hope it helps! You can change the formItems data structure to meet your needs, and modify the template accordingly.

Adding to document array in Mongo from Angular Controller

I am having an issue inserting a new JSON object into an array of JSON objects in MongoDB from my Angular Controller.
A simple concept of what I am trying to do is for this schema:
var ThingSchema = new mongoose.Schema({
id: Number,
items: [{
item_id: Number,
item_name: String,
item_price: Number
}]
});
And to add it to my mongodb in a the Mongo console I can use:
db.things.update(
{ "_id": ObjectId("579b7860b168c80c1fe8a32a")},
{ $push: {
"items": {
"item_id" : 134,
"item_name" : "sampleItem",
"item_price" : 234.00
}}
})
However, I'm not sure how I can translate that over to an http request from AngularJS. I used Yeoman to scaffold my app and am more interested in getting a functional prototype right now. In my Angular Controller, I am using this function
addNewItem(thing, newItem) {
this.$http.put('/api/things/' + thing._id, { 'items': newItem})
// on success clear the fields
.then( response => {
console.log(response);
alert(this.newItem);
thing.items.push(newItem);
newItem = {};
});
}
When I call this function I add it to my array that I have instantiated, but I cannot access the actual MongoDB even though a 200 response code is returned.
And in my HTML file I have
<form novalidate class="condition-form">
<fieldset>
<input type="number" ng-model="newItem.item_id" name="" placeholder="Enter item id">
<input type="text" ng-model="newItem.item_name" name="" placeholder="Enter item name">
<input type="number" ng-model="newItem.item_price" name="" placeholder="Enter item price">
<input type="submit" ng-click="$ctrl.addNewItem(thing, newItem)" value="Add">
</fieldset>
</form>
I'm really at a loss for how I can translate this mongodb call to my MEAN stack application. If it helps I am using Babel with EMCAScript 6. Any help means a lot!
Thanks
At the backend of it,when the control reaches the things function in API there you will have to use a mongoDB driver(like Mongodb,Mongoose) for interaction with mongo shell. The save function if you are using mongoose will look something like this:
Things.update({'id':req.params.thing._id},{ $push : {items : req.body.items }},function (err,updated) {
if(err){
console.log(err);
} else {
output = {'msg':'Updated Successfully'};
}
return res.json(output);
});

Categories

Resources