Knockout JS Checkbox List not binding - html

I have an issue where my checkbox list is not binding when loading data from the server.
So I have a table where you select a client when I click the edit button a bootstrap modal window popup with all the information fields such as first name, last name etc.
Now a couple of these fields are checkbox lists. Everything is being populated with the correct data except these two fields. Below is the current code of one of the lists.
<div class="row">
<div class="col-25">
<label for="referralSource">Referral Source</label>
</div>
<div class="col-75">
<ul class="kolist" data-bind="foreach: $root.sources">
<li>
<input type="checkbox" data-bind="
checkedValue: id,
checked: $root.currentClient.ReferralSourceIDs
" />
<label style="margin: 0px; padding: 0px;" data-bind="
text: source,
attr: {for: source}
"></label>
</li>
</ul>
</div>
</div>
This code should bind the data from the currentClient which is in the viewmodel.
self.currentClient = ko.observable();
On the edit button being clicked the currentClient is filled with the correct data.
editClient = function (editingClient) {
self.currentClient(editingClient);
}
The data we receive from the database fills up the ReferralSource class.
function ReferralSource(id, source) {
this.id = ko.observable(id);
this.source = ko.observable(source);
return this;
}
clientsLoad = function () {
$.ajax({
contentType: 'application/json',
type: "GET",
url: baseUrl + 'api/Client/LoadAll',
dataType: 'json',
context: this,
success: function (data, textStatus, jqXHR) {
self.clients($.map(data, function (item) {
var referralSources = new Array();
for (var i = 0; i < item.ReferralSourceIDs.length; i++) {
referralSources.push(
new ReferralSource(
item.ReferralSourceIDs[i].ReferralSourceID,
item.ReferralSourceIDs[i].Source
)
);
}
return new Client(
item.FirstName,
item.LastName, /* etc */
referralSources
);
}));
}
});
}
So with all that being said the load data is working fine the edit on select is being populated with the correct data but the only issue is that the check boxes are not being selected on bind. Can anyone see something I am missing or am I doing something wrong. If you need more info please ask, I will be trying to get this to work in the meantime.
EDIT: So I found a way to get it working and it has to deal with this part of the code.
<input type="checkbox" data-bind="checkedValue: id, checked: $root.currentClient.ReferralSourceIDs" />
It does not like $root.currentClient.ReferralSourceIDs. I removed this out of the class and put it into the view model and it started to work. So it looks like this now $root.referralSources. I have no idea why but it works that way. So if anyone has any other solutions please let me know for now I will stick with this.

Related

Trouble Keeping Form Inputs Saved After Failed Submission in Nodejs Express Project Using Ajax

I am currently working on a Nodejs Project and I am trying to grab the information inputted into a form to redisplay in the form after a failed submit occurs. However, I am confused as to how exactly to perform this. From researching around, I've learned that accomplishing this through an Ajax request would be my best option. I have looked at other posts as well as Googled around but have had no luck. Would anyone be able to assist me? This is the link to the project repo: https://github.com/halsheik/RecipeWarehouse.git. I have also posted any relevant code that may or may not be updated in this repo.
$(document).ready(function(){
$("#createRecipeForm").submit(function(e) {
$.ajax({
type: "POST",
url: "/recipes/createRecipe",
data: $(this).serialize(), // serializes form input
success: function(data){
console.log(data);
},
error: function(data){
console.log(data);
}
});
console.log(data);
});
});
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Homemade</title>
<!-- Required program scripts -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
<!-- Style Sheets-->
<link rel="stylesheet" href="/styles/navBarStyle.css">
<link rel="stylesheet" href="/styles/myRecipesStyle.css">
<link rel="stylesheet" href="/styles/createRecipeStyle.css">
</head>
<body>
<!-- Background image -->
<img id="background" src="/images/foodBackground.jpg" alt="">
<div id="newRecipeContainer">
<div id="closeButtonContainer">
<div id="backButton"><a id="back" href="/recipes/myRecipes">← My Recipes</a></div>
</div>
<form id="createRecipeForm" action="/recipes/createRecipe" method="POST" enctype="multipart/form-data">
<label id="formSubHeading">Create Your Homemade Recipe</label>
<div id="recipeNameContainer">
<label id="recipeNameLabel">Title</label>
<input id="recipeNameInput" type="text" name="recipeName">
</div>
<div id="recipeImage">
<label id="recipeImageLabel">Add An Image of Your Meal</label>
<input id="recipeImageInput" type="file" accept="image/*" name="recipeImage"/>
<label id="recipeImageInputLabel" for="recipeImageInput" name="recipeImage">Choose A File</label>
</div>
<div id="recipeDescription">
<label id="recipeDescriptionLabel">Description</label>
<textarea id="recipeDescriptionInput" name="recipeDescription" cols="30" rows="10" maxlength="2000"></textarea>
</div>
<div class="ingredientsContainer">
<label id="ingredientsLabel">Ingredients</label>
<button id="addIngredientButton" type="button" #click="addIngredientForm">Add Another Ingredient</button>
<div class="allIngredients" v-for="(ingredient, ingredientIndex) in ingredients">
<label class="ingredientLabel">{{ ingredientIndex + 1 }}.)</label>
<input class="ingredientInput" type="text" name="ingredients" v-model="ingredient.ingredient">
<button class="deleteIngredientButton" type="button" v-if="ingredientIndex > 0" #click="deleteIngredientForm(ingredientIndex)">X</button>
</div>
</div>
<div class="directionsContainer">
<label id="directionsLabel">Directions</label>
<button id="addDirectionButton" type="button" #click="addDirectionForm">Add Another Direction</button>
<div class="allDirections" v-for="(direction, directionIndex) in directions">
<label class="directionLabel">{{ directionIndex + 1 }}.)</label>
<input class="directionInput"type="text" name="directions" v-model="direction.direction">
<button class="deleteDirectionButton" type="button" v-if="directionIndex > 0" #click="deleteDirectionForm(directionIndex)">X</button>
</div>
</div>
<div id="createRecipeButtonContainer">
<button id="createRecipeButton" type="submit">Create Recipe</button>
</div>
</form>
</div>
</body>
<script src="/controls/newRecipeControl.js"></script>
</html>
Again, thanks for any help.
EDIT 1:
Errors from using jquery code
EDIT 2:
My current code. I console.log in order to check that the form info is being received. Before moving it into the form submit call, it would not receive any of the form data. Currently, it does not redisplay the form info upon failed submissions.
$(document).ready(function(){
$("#createRecipeForm").submit(function(e) {
const data = {
title: $('#recipeNameInput').val(), // this syntax is for grabbing input data from jquery
image: $('imageNameInput').val(), // where the # sign indicates an id is being used to grab the value
description: $('#recipeDescriptionInput').val(),
ingredients: $('#ingredientInput').val(),
directions: $('#directionInput').val(), // in the cases of ingredients and directions, you are using Vue's `v-model` but I am going to assume you have text id's of `ingredientInput` and `directionInput` for them respectively
};
console.log(data);
localStorage.setItem('data', JSON.stringify(data)); // data is assumed to be an array or an object
$.ajax({
type: "POST",
url: "/recipes/createRecipe",
data: $(this).serialize(), // serializes form input
success: function(data){
localStorage.removeItem('data') // clear the data from localStorage as it won't be necessary again
// do another thing
},
error: function(data){
// throw or return error message
}
});
});
if (localStorage.getItem('data') !== null) { // The getItem(key) method must return the current value associated with the given key. If the given key does not exist in the list associated with the object then this method must return null.
const data = JSON.parse(localStorage.getItem('data')); // parse the retrieve JSON object from localStorage. Data should be exactly the way you put it earlier
$('#recipeNameInput').val(data.title); // grab the title form input and populate it with the data.title value.
$('#imageNameInput').val(data.image);
$('#recipeDescriptionInput').val(data.description);
$('#ingredientInput').val(data.ingredients);
$('#directionInput').val(data.directions);
}
});
EDIT 3:
You could use localStorage to set form details before the request is made and then clear the request if the request is successful or get the data if the request fails.
Try this
$(document).ready(function(){
// You have form inputs of title, image, description, ingredients, directions
// Grabbing the input from the form will be something like this
const data = {
title: $('#recipeNameInput').val(), // this syntax is for grabbing input data from jquery
image: $('#recipeNameInput').val()), // where the # sign indicates an id is being used to grab the value
description: $('#recipeDescriptionInput').val()),
ingredients: $('#ingredientInput').val()),
directions: $('#directionInput').val()), // in the cases of ingredients and directions, you are using Vue's `v-model` but I am going to assume you have text id's of `ingredientInput` and `directionInput` for them respectively
}
localStorage.setItem('data', JSON.stringify(data)) // data is assumed to be an array or an object
$("#createRecipeForm").submit(function(e) {
$.ajax({
type: "POST",
url: "/recipes/createRecipe",
data: $(this).serialize(), // serializes form input
success: function(data){
localStorage.removeItem('data') // clear the data from localStorage as it won't be necessary again
// do another thing
},
error: function(data){
// throw or return error message
}
});
});
});
You can find out more about getting form field values using jQuery here
Then, onLoad of the form, you check if there is data in localStorage.
$( document ).ready(function() {
if (localStorage.getItem('data') !== null) { // The getItem(key) method must return the current value associated with the given key. If the given key does not exist in the list associated with the object then this method must return null.
const data = JSON.parse(localStorage.getItem('data'); // parse the retrieve JSON object from localStorage. Data should be exactly the way you put it earlier
data = {
title: ....., //all fields will be populated with data from the previous attempt
image: .....,
description: ....,
ingredients: ....,
directions: .....
}
$('#recipeNameInput').val(data.title) // grab the title form input and populate it with the data.title value.
// Do the same with the rest of the form inputs
} else {
// continue to fresh form in a situation where the previous form request were successful
}
});
You can find out more about populating form fields using jQuery here

Vue.js edit post by id

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.)

trying to implement live search in a input box

i'm trying to implement live search in a textbox, the list of options coming from the server, if the item is not available it should add dynamically, like if i type "gre" and selects that, it should added to the list or it should show in the top of the lists
Codeply :
https://www.codeply.com/go/d6WaXok32m
as specified in the code, the element 'entranceExamSearch' input box contails list of items
This code will give you some idea.
HTML:
<input type="text" id="searchPro" />
Your all the dynamic result will be show inside the div
<div id="searchLog"></div>
JQuery:
$("#searchPro").autocomplete({
source: function(request,response) {
$.ajax({
url: "php.func.php",
type: "POST",
data: { term: request.term },
success: function (searchData) {
$('#searchLog').html(searchData);
}
})
}
});
PHP: php.func.php
$find = "SELECT *
FROM tbl_products
WHERE (`product_name` LIKE '%".$_REQUEST['term']."%')";
$resset = $conn->query($find);
while ($res = mysqli_fetch_object($resset)) {
$findRes[] = $res;
}
if (!empty($findRes)) {
foreach ($findRes as $ResultSet) {
echo "<tr><td>".$ResultSet->product_name."</td>";
}
}else{
echo "<p>No Result Found for keyword <b>".$_REQUEST['term']."</b>..<p><hr/>";
}
Here is the link: [JQuery Autocomplete][1]
This is for your basic reference code i have use in my project. you can modify as per your need. Hope this will help you.
For more https://jqueryui.com/autocomplete/

MVC 5 prevent page refresh on form submit

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"
);
}

passing data with JSON

What I want to be abl
e to do is passing the form data to a php file and then having the results passed back into app so that the user isnt directly accessing the php file at any point.
This is what I came up with but I cant get it too pass the data. I used chrome with -disable-web-security. It always returns false so I guess the data isnt being passed to the php file. Any help would be great. Also. when it forwards to the results page, it goes blank after a few seconds. thank you.
HTML
<form id="form" method="POST" data-ajax="false" data-transition="pop" data-direction="reverse">
<fieldset>
<label for="name" class="ui-hidden-accessible">Name</label>
<input type="text" name="name" id="name" value="" class="required" placeholder="Name"/>
<label for="email" class="ui-hidden-accessible">E-Mail</label>
<input type="email" name="email" id="email" value="" class="required" placeholder="E-Mail"/>
<label for="memory" class="ui-hidden-accessible">Memory</label>
<textarea name="memory" name="memory" id="memory" class="required" placeholder="Your Memory..."></textarea>
<label for="submit" class="ui-hidden-accessible">Submit</label>
<input type="submit" name="submit" id="submit" value="SEND">
</fieldset>
</form>
JS
$(document).on('pagebeforeshow', '#formPage', function(){
$(document).on('click', '#submit', function() { // catch the form's submit event
if($('#name').val().length > 0 && $('#email').val().length > 0 && $('#memory').val().length > 0){
var that = $(this),
contents = that.serialize();
// Send data to server through ajax call
// action is functionality we want to call and outputJSON is our data
$.ajax({
url: 'http://www....',
dataType: 'json',
type: 'post',
data: contents,
async: true,
beforeSend: function() {
// This callback function will trigger before data is sent
$.mobile.showPageLoadingMsg(true); // This will show ajax spinner
},
complete: function() {
// This callback function will trigger on data sent/received complete
$.mobile.hidePageLoadingMsg(); // This will hide ajax spinner
},
success: function(data) {
console.log(data);
},
error: function (request,error) {
// This callback function will trigger on unsuccessful action
alert('Network error has occurred please try again!');
}
});
} else {
alert('Please fill all nececery fields');
}
return false; // cancel original event to prevent form submitting
});
});
PHP
header('Content-type: text/javascript');
$json = array(
'success' => false,
'result' => 0
);
if(isset($_POST['name'], $_POST['email'], $_POST['memory'])){
$name = $_POST['name'];
$email = $_POST['email'];
$memory = $_POST['memory'];
$json['success'] = true;
$json['result'] = $name;
}
echo json_encode($json);
You are not serializing the form data correctly and the result is that the contents variable is empty.
Change this code:
var that = $(this),
contents = that.serialize();
To this:
//var that = $(this), // <-- delete this line
contents = $('#form').serialize();
YOU ALSO NEED TO FIX ..
You haven't realized it yet but you have created a multiple click binding issue by placing your click handler in the bagebeforeshow event. In order to prevent that from occuring you need to
Change this code:
$(document).on('pagebeforeshow', '#formPage', function(){
To this:
$(document).on('pageinit', '#formPage', function(){
This way your $(document).on('click', '#submit', function() { is only ever bound once regardless of how many times a user leaves and returns to the '#formPage' page
EDITED
No, the data submitted to your backend PHP program via ajax is not json encoded. It is standard HTTP POST data and is accessed via $_POST (or $_REQUEST).
I have your code (with the changes I outlined in my answer above) working on my server. I have placed the two files I setup to test your code in a pastbin for your reference:
The php file:
(edit the path to the included javascript file for your environment)
sandbox_ajax_form.php
The javascript file:
(edit the path that the form data is sent to)
sandbox_ajax_form.js