JS — how to append Array of Objects with files to FormData? - javascript

I'm developing admin dashboard with Vue, Laravel & Axios.
And I have some problems with trying to upload files to api.
I replaced some real data to type of data
On Client-side I have Array of Objects:
[
{
id: random_generated_id,
img: {obj},
img_is_fullwidth: boolean,
site_url: string,
description: string
},
{
id: random_generated_id
img: {obj},
img_is_fullwidth: boolean,
site_url: string,
description: string
}
...
]
Objects can be added to Array:
addObject() {
this.array.push({
id: new Date().valueOf(),
img: null,
img_is_fullwidth: null,
site_url: null,
description: null
})
}
<div v-for="(content, index) in array" :key="content.index">
<input type="file" :ref="'content' + content.id" v-on:change="handleFileUpload(content.id)">
<label class="checkbox">
<input type="checkbox" v-model="content.img_is_fullwidth">
is fullwidth?
</label>
<input type="text" v-model="content.site_url" required>
<input type="text" v-model="content.description" required>
</div>
<button #click.prevent="addContent">Add content</button>
And I'm using random ids to attach image file to object by:
<input class="file-input" type="file" :ref="'content' + content.id" v-on:change="handleFileUpload(content.id)">
handleFileUpload(itemId) {
var itemIndex = this.array.findIndex(i => i.id === itemId)
this.array[itemIndex].img = this.$refs['content' + itemId][0].files[0]
}
So the problem is when I'm trying to post this data to api with axios,
I get error:
Trying to get property 'img' of non-object
It's happened cause I'm using JSON.stringify() and image object transforms to image array:
let fd = new FormData()
fd.append('array', JSON.stringify(this.array))
Part of Laravel Controller:
$array = json_decode($request->array, true);
foreach ($array as $content) {
$newContent = new WorkContent;
$contentImg = $content->img;
$contentName = uniqid();
$contentExt = $contentImg->getClientOriginalExtension();
$contentPath = $contentImg->storeAs('works/'.$request->client.'/'.$request->slug, $contentName.'.'.$contentExt);
$newContent->img_url = $contentPath;
if ($content->img_is_fullwidth == 'true') {
$newContent->img_is_fullwidth = true;
} else if ($content->img_is_fullwidth == 'false') {
$newContent->img_is_fullwidth = false;
}
$newContent->site_url = $content->site_url;
$newContent->description = $content->description;
$newContent->save();
$work->contents()->attach($newContent);
}
So, the question is:
Any ideas how to solve the problem?

Found solution by myself:
$contentArray = json_decode($r->contents, true);
$contentImgs = $r->contentImgs;
$imgIndex = 0;
for ($i = 0; $i < sizeof($contentArray); $i++) {
if($contentArray[$i]['img'] !== null) {
$contentArray[$i]['img'] = $r->contentImgs[$imgIndex];
}
}
foreach ($contentArray as $content) {
$newContent = new WorkContent;
$contentImg = $content['img'];
if ($contentImg !== null) {
$contentName = uniqid();
$contentExt = $contentImg->getClientOriginalExtension();
$contentPath = $contentImg->storeAs('works/'.$r->client.'/'.$r->slug, $contentName.'.'.$contentExt);
$newContent->img_url = $contentPath;
}
if ($content['img_is_fullwidth'] == 'true') {
$newContent->img_is_fullwidth = true;
} else if ($content['img_is_fullwidth'] == 'false') {
$newContent->img_is_fullwidth = false;
}
$newContent->site_url = $content['site_url'];
$newContent->description = $content['description'];
$newContent->work_id = $w->id;
$newContent->save();
}

Related

Error "getData is not a function" when building form from existing data on formbuilder

I'm currently working with form builder and am running into following error
fb.actions.getData is not a function
Whenever I'm initiating the form builder from existing form data and then trying to save the form again afterwards (e.g. after making changes to the form.)
This is the code I'm using to build the (multi-page) form.
const result = <?php echo $questiondata->json_question;?>;
let length = result.length;
var stepLen = length;
var res = result;
for (let i = 1; i <= stepLen; i++) {
var tabId = "step-" + i;
const $newPageTemplate = $(document.getElementById("new-page"));
const $newPage = $newPageTemplate.clone().attr("id", tabId).addClass("fb-editor");
const $newTab = $('#add-page-tab').clone().removeAttr("id");
const $tabLink = $("a", $newTab).attr("href", "#" + tabId).text("Step " + i);
$newPage.insertBefore($newPageTemplate);
$newTab.insertBefore('#add-page-tab');
$fbPages.tabs("refresh");
$fbPages.tabs("option", "active", 0);
fbInstances.push($newPage.formBuilder());
$(tabId).formBuilder().promise.then(function(fb) {
let formadata = res[i - 1];
fb.actions.setData(formadata);
});
}
//--------json form data update-------------
$(document.getElementById("save-all")).click(function() {
let allData = fbInstances.map((fb) => {
console.log(fb.actions.getData()); // This line is throwing the error
return fb.actions.getData(); // This line is throwing the error
});
saveFormData(allData);
});
I'm not sure I have enough information. Would you please include the HTML?
I see you used this technique: https://formbuilder.online/docs/formBuilder/promise/
Have you tried this technique?
https://formbuilder.online/docs/formBuilder/actions/getData/
It's possible the data isn't available yet and the setData() call isn't working, but I don't think that's the case because you used technique #1. If the data isn't available yet, you could try this first and then do your setData() call afterwards:
var formBuilder = $(fbEditor).formBuilder();
All formbuilder code is here. i am sharing all code like view page code , render code and save code. render 4 tabbed different json form but after save getting only last array 4 time.
view page code
<form method="POST" id="form-builder-pages" action="{{
url('question/updatequestion', $questiondata->id) }}">
#csrf
<div class="col-md-12 text-center"><button id="save-all" type="button"
class="btn btn-primary">Save</button></div>
<input type="hidden" name="formid" value="{{ $questiondata->id }}">
<textarea id="jsondata" name="json_question" style="display:none"></textarea>
<div class="col-md-12 p-3">
<input id="form_title" type="text" class="form-control #error('form_title') is-invalid #enderror" name="form_title" readonly value="{{ $questiondata->form_title }}" required autocomplete="form_title" autofocus>
<span id="msg"></span>
</div>
<ul id="tabs">
<!-- <li>Page 1</li> -->
<li id="add-page-tab">+ Page</li>
</ul>
<div id="page-1" class="fb-editor"></div>
<div id="new-page"></div>
</form>
enter image description here
Get result here
[[{"type":"textarea","required":true,"label":"message","className":"form-control","name":"textarea-1655308420860-0","access":false,"subtype":"textarea"}],[{"type":"textarea","required":true,"label":"message","className":"form-control","name":"textarea-1655308420860-0","access":false,"subtype":"textarea"}],[{"type":"textarea","required":true,"label":"message","className":"form-control","name":"textarea-1655308420860-0","access":false,"subtype":"textarea"}]]
<script>
jQuery(($) => {
"use strict";
var $fbPages = $(document.getElementById("form-builder-pages"));
var addPageTab = document.getElementById("add-page-tab");
var fbInstances = [];
$fbPages.tabs({
beforeActivate: function (event, ui) {
if (ui.newPanel.selector === "#new-page") {
return false;
}
}
});
addPageTab.addEventListener(
"click",
(click) => {
const tabCount = document.getElementById("tabs").children.length;
const tabId = "page-" + tabCount.toString();
const $newPageTemplate = document.getElementById("new-page");
const $newTabTemplate = document.getElementById("add-page-tab");
const $newPage = $newPageTemplate.cloneNode(true);
$newPage.setAttribute("id", tabId);
$newPage.classList.add("fb-editor");
const $newTab = $newTabTemplate.cloneNode(true);
$newTab.removeAttribute("id");
const $tabLink = $newTab.querySelector("a");
$tabLink.setAttribute("href", "#" + tabId);
$tabLink.innerText = "Page " + tabCount;
$newPageTemplate.parentElement.insertBefore($newPage, $newPageTemplate);
$newTabTemplate.parentElement.insertBefore($newTab, $newTabTemplate);
$fbPages.tabs("refresh");
$fbPages.tabs("option", "active", tabCount - 1);
fbInstances.push($($newPage).formBuilder());
},
false
);
// ----render json form data is working fine
const result = <?php echo $questiondata->json_question;?>;//json form data
let length = result.length;
var stepLen= length;
var res = result;
for (let i = 1; i <= stepLen; i++) {
var tabId = "step-" + i;
var $newPageTemplate = $(document.getElementById("new-page"));
var $newPage = $newPageTemplate.clone().attr("id",tabId).addClass("fb-editor");
var $newTab = $('#add-page-tab').clone().removeAttr("id");
var $tabLink = $("a", $newTab).attr("href", "#" + tabId).text("Step " + i);
$newPage.insertBefore($newPageTemplate);
$newTab.insertBefore('#add-page-tab');
$fbPages.tabs("refresh");
$fbPages.tabs("option", "active", 0);
let $newInstance = $newPage.formBuilder();
$newInstance.promise.then(function (fb) {
fbInstances.push(fb);
let formadata=res[i - 1];
fb.actions.setData(formadata);
});
}
//update json form array -------
$(document.getElementById("save-all")).click(function () {
const allData = fbInstances.map((fb) => {
console.log(fb.actions.getData());
return fb.actions.getData();
});
let jsondata = JSON.stringify(allData);
console.log(jsondata); // error here
});
});
</script>
console.log here
[[{"type":"textarea","required":true,"label":"message","className":"form-control","name":"textarea-1655308420860-0","access":false,"subtype":"textarea"}],[{"type":"textarea","required":true,"label":"message","className":"form-control","name":"textarea-1655308420860-0","access":false,"subtype":"textarea"}],[{"type":"textarea","required":true,"label":"message","className":"form-control","name":"textarea-1655308420860-0","access":false,"subtype":"textarea"}]]

jQuery/javascript get form data just like a post array

I have an HTML form and I want to get all its data in the same structure as a $_POST would. So if I have some inputs like this:
<input type="text" name=r[1][0] value="x"><input type="text" name=r[1][1] value="y">
I would like to get and object like this:
obj = {r:{1:{0:x,1:y}}}
Can you please tell me there is any way I could do this? I tried serializeArray().. and other "custom" solutions with no success.
Thank you! :)
1 weeks ago, i had almost same problem, i developed a function which can convert input to a JavaScript object.
It's maybe more then what you need, but you can take a look in this :
var myObj = {};
function addValueToObj(objet, nom) {
var pathBrackets = "";
var regex = /\[([0-9]+)\]$/;
nom = nom.split("=");
var path = nom[0].split('.');
val = nom.slice(1).join("=");
for (var i = 0, tmp = objet; i<path.length-1; i++) {
pathBrackets += path[i]+".";
if (path[i].match(regex)!=null) {
var valindex = path[i].match(regex)[1];
var index = path[i].match(regex).index;
var newPath = path[i].substring(index, 1-path[i].length);
if (typeof tmp[newPath] == "undefined") {
tmp = tmp[newPath] = [];
tmp = tmp[valindex] = {};
} else {
if (typeof tmp[newPath][valindex] == "undefined") {
tmp = tmp[newPath][valindex] = {};
} else {
tmp = tmp[newPath][valindex];
}
}
} else {
if (typeof tmp[path[i]] == "undefined") {
tmp = tmp[path[i]] = {};
} else {
tmp = tmp[path[i]];
}
}
}
tmp[path[i]] = val;
}
$(document).ready(function() {
$.each($("input"), function() {
addValueToObj(myObj, $(this).attr('name')+"="+$(this).val());
})
console.log(myObj);
})
and the HTML :
<form>
x :<input type="text" name="r.1.0" value="x">
y :<input type="text" name="r.1.1" value="y">
</form>
Result :
{
"r":{
"1":{
"0":"x",
"1":"y"
}
}
}
Hope it can help you.
Here the JSFiddle

how to loop through array with javascript

I its basic but I am new to javascript. I am trying to loop through the array and match the objects that == my key.
this is what i am using right now, it works but i am only matching the first object that matches, sometimes there will be multiple objects that match.
Here is what i have now
var chartSeries = chartService.getSeries();
var marker.options.subdivision.id = 1345
var matchingSeries = Enumerable.From(chartSeries).Where('x => x.id == "' + marker.options.subdivision.id + '"').ToArray();
var series = {
id: matchingSeries[0].id,
name: matchingSeries[0].name,
data: matchingSeries[0].data,
lineWidth: 5
};
I need to include a for loop to match all objects.
var subIdSeries = [];
var subId = marker.options.subdivision.id;
var series = {
id: matchingSeries[0].id,
name: matchingSeries[0].name,
data: matchingSeries[0].data,
lineWidth: 5
};
for (var i = 0; i < chartSeries.length; i++) {
if (subId == chartSeries.id) {
push.subIdSeries(subId)
}
}
Change
if (subId == chartSeries.id) {
push.subIdSeries(subId)
}
to
if (subId == chartSeries[i].id) {
subIdSeries.push(subId)
}
Without seeing the whole script, from what you have so far, I can suggest:
if (subId == chartSeries[i].id) {
subIdSeries.push(subId)
}

Values are not being saved to the array

I am pretty new to javascript and jquery. I currently have a xml file that I'm trying to parse by using jquery and javascript, but for some reason the values that I store on the array are not being saved.
var categories = new Array(); // Array for the categories
var data = {
categories: []
};
var sources = [
{
src:'',
title: '',
desc: ''
}];
var i = 0;
$.get('fakeFeed.xml', function (info) {
$(info).find("item").each(function () {
var el = $(this);
var categoryName = el.find('category').text();
var p = categories.indexOf(categoryName);
sources[i] = [];
sources[i].src = el.find('media\\:content, content').attr('url');
sources[i].title = el.find("title").text();
sources[i].desc = 'Moscone Center';
if( p == -1) {
categories.push(categoryName);
var category = {
name: categoryName,
videos: []
};
}
i++;
});
});
If i do console.log(categories) it prints all the categories on the array but if I do console.log(categories.length) I keep getting 0...
console.log(categories.length); // This should be outputting 5 but I keep getting 0 for the size.
for (var i=0; i<categories.length; i++) {
var category = {
name: categories[i],
videos: []
};
}
I appreciate any help that anybody can give me. Thanks
$.get function is asynchronous so you should try putting the logging inside the callback function.
$.get('fakeFeed.xml', function (info) {
$(info).find("item").each(function () {
....
});
console.log(categories.length);
});

MVC - Ajax post the contents inside all div tags that exists inside the view?

I am using MVC3 in my project.
I have a view with around 2-12 div tags it depends on how many questions there is, if there is 5 questions there is 5 div tags that looks like an answer form. all these div tags are inside one form.
Each div tag have a hiddenfield, textarea and a DropDownList. values inside those fields are used by ajax post that takes the values inside the fields and posts it to my controller.
So far I am able to post the first div tag with my code, but the rest of the div tags aint getting posted. What I am looking for is to be able to post all the div tags one by one when "save all" button is clicked. Also all the div tags have the same class name: "wizard-step2". They all also have a ID which is unique and the value of the ID is the QuestionID taken from the database.
Here is my jquery code for the post:
$("saveall").click(function() {
$('#loading2').show();
setTimeout(function () { $("#loading2").hide(); }, 400);
var $step = $(".wizard-step2"); // show all steps
var Comment = $step.find(".Comment").val();
var QuestionID = $step.find(".QuestionID").val();
var Grade = $step.find(".Grade").val();
var data =
{
Comment: Comment,
QuestionID: QuestionID,
Grade: Grade
};
$.post('#Url.Action("AnswerForm", "AnswerNKI")', data, function () {
});
});
The following code will only save the first div tag but not the rest.
this is my HTML:
#{
int nr = 1;
foreach (SelectedQuestionViewModel items in Model.AllSelectedQuestions)
{
<div class="wizard-step2" id="#items.QuestionID">
<br/>
<br/>
<p>#(nr++). #items.SelectedQuestionText <span class="b">Betyg:
#{
var selectListItems = (new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).Select(i => new SelectListItem { Text = i.ToString(), Value = i.ToString(), Selected = (items.Grade.HasValue && i == items.Grade.Value) });
#Html.DropDownList("selectetListItems", selectListItems, "n/a", new { #class = "Grade" })
}</span></p>
<div class="editor-field">
#Html.TextArea("Comment", items.Comment, new { #id = "selectstyle3", #class = "Comment" })
</div>
<br/>
<br/>
#Html.Hidden("QuestionID", items.QuestionID, new { #id = "SelectedQuestions", #class = "QuestionID" })
<br/>
</div>
}
}
Any help is appreciated.
Thanks in advance!
Use the .each() function to iterate over the divs, and send an AJAX post for each individual one. Without seeing the HTML structure I can only guess based on what you already had, but I think the following should work:
$("saveall").click(function() {
$('#loading2').show();
setTimeout(function () { $("#loading2").hide(); }, 400);
$(".wizard-step2").each(function(index, step) {
var Comment = step.find(".Comment").val();
var QuestionID = step.find(".QuestionID").val();
var Grade = step.find(".Grade").val();
var data = {
Comment: Comment,
QuestionID: QuestionID,
Grade: Grade
};
$.post('#Url.Action("AnswerForm", "AnswerNKI")', data, function () {
});
});
});
Try this one... it will collect all your divs into one single array, and using ajax post will post the data...
$.fn.serializeObject = function () {
var o = {};
var a = this.serializeArray();
$.each(a, function () {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
$(document).ready(function () {
$("#Submit").click(function () {
var QuestionAnswerArray = [];
var QuestionAnswerLength = $(".wizard-step2").length;
$(".wizard-step2").each(function (i) {
var test = $(this).find("select,textarea, input").serializeObject()
QuestionAnswerArray.push(test);
if ((i + 1) == QuestionAnswerLength) {
$.ajax({
type: 'POST',
url: '/../AnswerNKI/AnswerForm',
data: JSON.stringify(QuestionAnswerArray),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (return_flag) {
if (return_flag == true) {
alert("Question and Answers Saved Succesfully!");
} else {
alert("Error Occured");
}
}
});
}
});
});
});
and in your controller...
[HttpPost]
public ActionResult AnswerForm(Answers[] answers)
{
foreach (var item in answers)
{
// do whatever you want here
}
return View();
}
I had to do a for loop this is the correct answer:
var $step = $(".wizard-step2"); // get current step
for (var i = 0; i < $step.length; i++) {
var allsteps = $(".wizard-step2");
var Allsteps = $(allsteps[i]);
var Comment = Allsteps.find(".Comment").val();
var QuestionID = Allsteps.find(".QuestionID").val();
var Grade = Allsteps.find(".Grade").val();
var data =
{
Comment: Comment,
QuestionID: QuestionID,
Grade: Grade
};
$.post('#Url.Action("AnswerForm", "AnswerNKI")', data, function () {
if (Comment != null && Grade > 0) {
$('a[data-id="' + QuestionID + '"]').removeClass("unfinished");
$('a[data-id="' + QuestionID + '"]').addClass("completed");
} else {
$('a[data-id="' + QuestionID + '"]').removeClass("completed");
$('a[data-id="' + QuestionID + '"]').addClass("unfinished");
}
});
}

Categories