I'm starting with vue.js and I was reading this question to help me loading some posts from DB with v-for.
Below each post there are Edit and Delete buttons. I can delete each post by its ID correctly. And I can open the input to edit post title correctly too.
But I cannot save input changes when I click on save button. It returns to the initial text.
And when I click to edit it opens all the inputs titles.
Is there a way to open the specific post title and keep the changes after save it?
<div id="app" class="row mb-50">
<div v-for="(item, index) in tours" v-bind:key="item.id" id="tours" class="col-md-12 mb-30">
<div class="tour-list">
<div class="tour-list-title">
<p>
<input type="text" ref="item.id" :value="item.title" :disabled="!editingTour"
:class="{view: !editingTour}" />
</p>
</div>
<div class="tour-list-description">
<p>
{{ item.description }}
</p>
</div>
<div class="tour-list-options">
<div class="row">
<div class="col-md-6">
<span>
<button #click="editingTour = !editingTour" v-if="!editingTour"
class="btn border btn-circle tour-list-edit-btn">Edit</button>
</span>
<span>
<button #click="save" v-if="editingTour"
class="btn border btn-circle tour-list-edit-btn">Save</button>
</span>
<span>
<button #click="editingTour = false" v-if="editingTour"
class="btn border btn-circle tour-list-delete-btn">Cancel</button>
</span>
<span>
<button #click="deleteTour(item.id, index)" v-if="!editingTour"
class="btn border btn-circle tour-list-delete-btn">Delete</buton>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
vue.js:
let app = new Vue({
el: '#app',
data: {
editingTour: false,
tours: null,
errored: false,
edited: false,
deleted: false,
item: {
title: null,
description: null
}
},
created: function () {
this.searchTour()
},
methods: {
searchTour: function () {
axios.post('getPosts.php', { "token": param }).then((response) => {
this.tours = response.data;
}).catch((error) => {
this.errored = error;
});
},
editTour: function (id) {
axios.post('editPosts.php', { "token": token, "tourID": id }).then((response) => {
this.edited = response.data;
}).catch((error) => {
this.errored = error;
});
},
deleteTour: function (id) {
if (confirm('Are You sure?')) {
const index = this.tours.findIndex(item => item.id === id);
if (~index) {
axios.post('deletePosts.php', { "token": token, "tourID": id }).then((response) => {
this.deleted = response;
this.tours.splice(index, 1);
}).catch((error) => {
this.errored = error;
});
}
}
},
save: function () {
this.item.title = this.$refs['item.id'].value;
this.editingTour = !this.editingTour;
console.log(this.item.title);
}
}
});
In console.log(this.item.title); is returning undefined.
I have changed ref="item.id" to ref="title" and this.item.title = this.$refs['item.id'].value; to this.item.title = this.$refs['title'].value; but it did not work.
You should use in your input v-model instead of ref it will bind your model with the value you are editing, in general in vue we avoid direct DOM manipulation when possible, like so:
<input type="text" ref="item.id" v-model="item.title" :disabled="!editingTour"
:class="{view: !editingTour}" />
Where calling your function e.g. editTour you can pass it the item (if it's in the template to save the updated version like so:
#click="editTour(item)"
You can use the v-model directive to create two-way data bindings on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type. Although a bit magical, v-model is essentially syntax sugar for updating data on user input events, plus special care for some edge cases.
Source : https://v2.vuejs.org/v2/guide/forms.html
Example:
<input v-model="description" placeholder="my description">
The above input value will then be binded to the description element of your data object and vice-versa - if one changes, the other is updated to the same value:
data:{
description: "default value"
}
So, when you DB request is ready you can update the value of the description within the DB method:
this.description=db.result.description
and the value of the input will also update.
Likewise, if the user changes the value of the input field, the value bound to the data element will be updated also. So, when saving back to DB:
db.update({description:this.description})
(note: the db methods here are for example purposes only. Replace with the relevant DB methods for your backend service.)
Related
I'm trying to set up a really simple page to render documents based on inputs. I have a form with some inputs on one page, and I want the data appear on the next page when submitted.
I'm still learning Next and I'm trying to use the pages/api to do this but I am not sure how exactly it works.
As of now when I submit the form it is redirecting me to a JSON page with the data, how can I make is so when I hit submit, it uses the JSON data to redirect to another page displaying it?
Here is the code:
index.js
export default function Home() {
return (
<>
<h1 Name Generator</h1>
<form action="/api/form" method="post">
<div>
<label for="name">Name</label>{" "}
<input
type="text"
id="name"
name="name"
placeholder="Enter name."
required
/>
</div>
<div>
<button style={{ display: "flex" }} type="submit">
Submit
</button>
</div>
</form>
</>
);
}
pages/api/form.js
export default function handler(req, res) {
// Get data submitted in request's body.
const body = req.body;
// Optional logging to see the responses
// in the command line where next.js app is running.
console.log("body: ", body);
// Guard clause checks for name,
// and returns early if they are not found
if (!body.subject || !body.teachers) {
// Sends a HTTP bad request error code
return res.status(400).json({ data: "name not found" });
}
// Found the name.
// Sends a HTTP success code
res.status(200).json({
data: {
name: `${body.name}`,
},
});
}
here is the result page I want it to render the data into and what I tried
result.js
import { data } from "../pages/api/form";
export default function Result() {
console.log(data);
return (
<>
<>
<h1>Name Generator</h1>
<hr></hr>
<div>
<h4>Name</h4>
<hr></hr>
{data.name}
</div>
</>
))
</>
);
}
When I submit I get JSON at
host/api/form
data: {
name: "name"
}
I am not sure what to try as I believe there is a simple way to do this that I am just missing
I have below code which calls save form
<div className="col-md-4">
<button onClick={saveCredit} className="btn btn-success">
Submit
</button>
I have onclick handler function as
const saveCredit = () =>{
//validate form
// call api to save form attributes
CreditTransactionDataService.create(data)
.then(response => {
setSubmitted(true);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
after successful save , I will show successful message as below.
{submitted ? (
<div>
<h4>You submitted successfully!</h4>
<button className="btn btn-success mr-2" onClick={newCreditTransaction}>
Add
</button><Link style={{ fontWeight: 'bold' }} className="btn btn-warning" to={"/creditTransactionList"}>
return to List
</Link>
</div>
)
but the problem is, my form is getting submitted twice, and creating duplicate records with same values.... couple of save options, i restricted with unique key column at database level, but few tables still need to handled at code level..
I´m unable to reproduce it in codepen, but one solution a little bit hacky could be check in the method if it is submitted already
const saveCredit = {
//Check if it is submitted
if(!submitted){
//validate form
// call api to save form attributes
CreditTransactionDataService.create(data)
.then(response => {
setSubmitted(true);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
}
This may not be the best but could do the job
Also a thing I did notice is that your saveCredit function not look like a function.
Why not declare as an arrow function? Like:
const saveCredit = () => { //Your code }
Your button doesn't need onClick event handler if it's responsible for submitting a certain form. You should add type="submit" to button and onSubmit to form tag itself.
You should go for this approach to handle submitting correctly (clicking of the button or hitting enter by the user are covered).
I am new at Vue, now I am creating simple search app (using Vue.js cdn).
I want to append suggestion bar which contains all user id's from fake JSON server, for example if I write into search bar 1, I want to append only user which id is 1, and then I click to that user id I want to send another request to receive only this user info.
I am stuck, how I can solve this?
var app = new Vue({
el: '#app',
data: {
message: '',
searchKey:'',
result:[]
},
methods:{
async getData() {
// GET request using fetch with async/await
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${this.searchKey}`);
const data = await response.json()
this.result = data
},
},
created(){
this.getData()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.14/vue.js"></script>
<div id="app">
<div class="search-area">
<div class="header-wrapper">
<h1>Tag Search</h1>
</div>
<div class="search-bar-custom">
<input placeholder="Search tags" v-model="searchKey" #keyup="getData" />
<div class="suggetions">
<ul class="suggestions" id="suggestions">
<li><h1>suggetion id</h1></li>
</ul>
</div>
</div>
</div>
</div>
You are on the right way, but, there are some issues about your logic here, for json-server you need to use the Operator _like (https://github.com/typicode/json-server#operators) to retrieve or filter data depending the column or property, so, your getData method must be like this:
async getData() {
// GET request using fetch with async/await
const response = await fetch(
`https://jsonplaceholder.typicode.com/users?name_like=${this.searchKey}`
);
const data = await response.json();
this.result = data;
},
You can change the property or column, in example username_like or id_like.
Finally, you need to show the results, so, change your template:
<ul class="suggestions" id="suggestions">
<h1 v-for="item in result" #mousedown="show(item)">
{{ item.id }} | {{ item.name }}
</h1>
</ul>
Pay attention on #mousedown="show(item)", when user click on some result, this action will display the data about user, for that, we need to create a new method called show and pass the item:
show(item) {
alert(JSON.stringify(item, null, 2));
}
You can look how it works here: https://codepen.io/riateam/pen/ExNrGOE?editors=1010
I'm trying to create a directive which takes some JSON data, and creates a form. Each of the inputs in the form are contained in a <div> wrapper. The wrapper also contains a <div> that appears when ng-show is used. The <div> using ng-show is for dynamically displaying errors.
HTML for the Directive:
<div class="input-wrapper" ng-repeat="inputData in contactCtrl.formInputData">
<input type="text" class="text-input" placeholder="{{ inputData.placeholder }}" ng-model="inputData.model" />
<div class="error-bubble" ng-show="inputData.showFunction">
</div>
</div>
HTML on the Page:
<div id="column-right">
<h2>
Send me an Email
</h2>
<contact-form></contact-form>
</div>
Directive Creation:
spaModule.directive("contactForm", function() {
return {
restrict: "E",
templateUrl: "partials/directives/contact-form.html"
}
});
JSON Data and a Validation Function:
this.formInputData = [
{
placeholder: "Name",
model: "contactCtrl.clientName",
showFunction: "!contactCtrl.validateName()"
},
{
placeholder: "Email",
model: "contactCtrl.email",
showFunction: "!contactCtrl.validateEmail()"
},
{
placeholder: "Subject",
model: "contactCtrl.subject",
showFunction: "!contactCtrl.validateSubject()"
}
];
this.validateName = function() {
if (this.clientName !== "") {
this.nameError = "Name is required!";
return true;
} else {
return false;
}
};
Not a single piece of this is working. The placeholder is not being rendered correctly, ng-show is not doing anything, and the ng-model is not working. I've tried reformatting this as normal HTML in my page and everything works flawlessly.
The issue appears to be declaring ng attributes with ng-repeat. What am I doing wrong?
yBrowser: IE9
Technologies: MVC5
I am mainly using Angular for everything on my page. (Single Page App).
But because I am working with IE9, I can't use FileAPI.. So, I decided to go with MVC's Form Actions to get HttpPostedFileBase in my controller methods to handle fileupload.
Html Code: (Is present in a modal)
#using (Html.BeginForm("UploadTempFileToServer", "Attachment", FormMethod.Post, new { enctype = "multipart/form-data", id = "attachmentForm" }))
{
<div>
<span id="addFiles" class="btn btn-success fileinput-button" ng-class="{disabled: disabled}" onclick="$('#fileUpload').click();">
<span>Add files...</span>
</span>
<input id="fileUpload" type="file" name="files" class="fileInput" onchange="angular.element(this).scope().fileAdded(this)" />
</div>
<div>
<span class="control-label bold">{{currentFilePath}}</span>
<input name="fileUniqueName" value="{{fileUniqueName}}" />
<input id="attachmentSubmit" type="submit" value="Upload File" />
</div>
}
MVC Controller:
public void UploadTempFileToServer(IEnumerable<HttpPostedFileBase> files, string fileUniqueName)
{
var folderPath = fileStorageFolder;
foreach (var file in files)
{
if (file.ContentLength > 0)
{
file.SaveAs(folderPath + fileUniqueName);
}
}
}
Question #1: Does anyone know of a way to send the HttpPostedFileBase data to the controller, without using form's submit action?
I don't mind using Jquery if need be. I have tried hijacking the form's submit action and that didn't work.
I tried sending the file control's data using non submit button event, but no luck there either.
If not:
Question #2 How do I prevent the page from going to /Attachment/UploadTempFileToServer after the execution of submit is completed?
To answer #2 (and assuming you're using jQuery):
$(document).on('submit', '#attachmentForm', function(event){
event.preventDefault();
// everything else you want to do on submit
});
For #1, unfortunately, unless a browser supports XMLHttpRequest2 objects (which I don't believe IE9 does), you can't send file data via ajax. There are plugins that let you submit the form to a hidden iframe, though. I think Mike Alsup's Form plugin has that ability: http://malsup.com/jquery/form/#file-upload
So, after much research and attempts. This is my solution:
Using https://github.com/blueimp/jQuery-File-Upload/wiki
HTML:
Earlier I was using a hidden file upload control and triggering its click via a span. But because of security issues a file input which is opened by javascript can't be submitted by javascript too.
<div class="col-md-7">
<div class="fileupload-buttonbar">
<label class="upload-button">
<span class="btn btn-success btnHover">
<i class="glyphicon glyphicon-plus"></i>
<span>Add files...</span>
<input id="fileUpload" type="file" name="files"/>
</span>
</label>
</div>
</div>
Javascript:
$('#fileUpload').fileupload({
autoUpload: true,
url: '/Attachment/UploadTempFileToServer/',
dataType: 'json',
add: function (e, data) {
var fileName = data.files[0].name;
var ext = fileName.substr(fileName.lastIndexOf('.'), fileName.length);
var attachment = {
AttachmentName: fileName,
Extension: ext
}
var fileUniqueName = id + ext;
//Sending the custom attribute to C#
data.formData = {
fileUniqueName: fileUniqueName
}
data.submit().success(function (submitData, jqXhr) {
attachment.Path = submitData.path;
//Add the attachment to the list of attached files to show in the table.
$scope.attachmentControl.files.push(attachment);
//Since this is not a direct angular event.. Apply needs to be called for this to be bound to the view.
$scope.$apply();
}).error(function (errorData, textStatus, errorThrown) {
});
},
fail: function (data, textStatus, errorThrown) {
}
});
C#:
public virtual ActionResult UploadTempFileToServer(string fileUniqueName)
{
//Getting these values from the web.config.
var folderPath = fileStorageServer + fileStorageFolder + "\\" + tempFileFolder + "\\";
var httpPostedFileBase = this.Request.Files[0];
if (httpPostedFileBase != null)
{
httpPostedFileBase.SaveAs(folderPath + fileUniqueName);
}
return Json(new
{
path = folderPath + fileUniqueName
},
"text/html"
);
}