AngularJS - How to submit an array of checkbox selections dynamically - html

I'm building a really simple form with angular. I have an array of objects, like this:
data: [{id: 1, name: "Peter"},{id: 2, name: "Paul"}...]
And I need to build some checkboxes off of that data. A user can select multiple values. Then, when they hit submit, I should send an array of id numbers off to the DB, formatted like this:
{nameIds: [1,2]}
And I'm having a hell of a time building that array. I've been following other S.O. examples, but something isn't clicking. This is what I have right now:
<label ng-repeat="name in vm.nameArray">
<input type="checkbox" ng-model="name.selected"> {{name.name}}
</label>
<button ng-click="vm.submit()" class="btn btn-primary margin-10-tb">NEXT</button>
and in my controller:
vm.nameArray = response.data.data;
vm.selectedNames = [];
vm.nameIds = [];
vm.submit = function() {
vm.selectedNames = vm.nameArray.filter(function(name){
if (name.selected) return name.id;
});
vm.selectedNames.forEach(function(name){
vm.nameIds.push(name.id);
});
};
Maybe you've already guessed, but the issue here is that when a user deselects a name they've previously added, vm.nameIds does not update. The id for the deselected name persists in the array.
Any tips on how to best create this dynamic array for submission would be much appreciated.

You can first search for selected items and finally take its id property:
vm.selectedNames = vm.nameArray.filter(function(name){
return name.selected;
}).map(function(value){
return value.id;
});
The data always will be updated.

You can also further reduce the complication if you add "isChecked" property to the data. Please find plunker below:
I have added the logic to add "isChecked" property to the data as well.
Note: Please go with #SergioEscudero's answer if you want to go with your current logic.
http://plnkr.co/edit/f1Eq2OGKTuCLgI6KXn1b?p=preview
HTML:
<html>
<head>
<title>Directive Practice</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body ng-app="myApp">
<h1>HEADER</h1>
<div ng-controller="myCtrl">
<label ng-repeat="name in nameArray">
<input type="checkbox" ng-model="name.isChecked"> {{name.name}}
</label>
<button ng-click="submitnew()" class="btn btn-primary margin-10-tb">NEXT</button>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="app.js"></script>
</body>
</html>
JS:
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl',function($scope) {
$scope.data = [{id: 1, name: "Peter"},{id: 2, name: "Paul"},
{id: 3, name: "John"},{id: 4, name: "David"}]
$scope.data.forEach(function(data) {
//$scope.data[key].isChecked=false;
data.isChecked=false;
})
$scope.nameArray=$scope.data;
$scope.submitnew = function() {
$scope.nameArray.forEach(function(data) {
if(data.isChecked)
console.log(data.id);
})
}
})

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

how to add the value written inside checkbox by user?

I wrote this code for add and remove Textboxes using AngularJS, but I want to add the value of checkbox which will written by user and display it on same page so what can I do it?
The code below will display 1 textbox and 3 buttons, if I click on append then it will add one more text box on page and when I click on remove then it will remove last checkbox.
And I want to add functionality for example if I append 4 checkboxes, and every checkbox I insert some values as number, then I click on add button then it will return the addition of values which I inserted before and return it on same page. so what should i write in code
var app = angular.module('abc', []);
app.controller('myCtrl', function($scope) {
$scope.array=[1];
});
<html ng-app="abc" ng-controller="myCtrl">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-repeat="a in array">
Textbox {{a}} <input type="text" id="{{a}}">
</div>
<button ng-click="array.push(array.length+1)">Append</button>
<button ng-click="array.pop(array.length-1)">Remove</button>
<button>Add</button>
</body>
</html>
You can create a calcSum function in the scope, which calculates the sum of the entered values and stores the sum in the scope. Then you can call that function with ng-click.
Note that I changed your array to directly contain the values entered in the textbox. This way you can directly calculate the sum of the array without having to loop through the inputs manually.
var app = angular.module('abc', []);
app.controller('myCtrl', function($scope) {
$scope.array=[0];
$scope.calcSum = function() {
$scope.sum = $scope.array.reduce(function(total, num) {
return total + num;
})
}
});
<html ng-app="abc" ng-controller="myCtrl">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<body>
<div ng-repeat="a in array track by $index">
Textbox {{$index}} <input type="number" ng-model="array[$index]">
</div>
<button ng-click="array.push(0)">Append</button>
<button ng-click="array.pop(array.length-1)">Remove</button>
<button ng-click="calcSum()">Add</button>
<div>{{sum}}</div>
</body>
</html>

how to provide a external (Serverside)search box to our Kendo UI grid?

how to provide a external search box to our Kendo UI grid that Search in Sever Side?
function onSearch()
{
var q = $("#txtSearchString").val();
var grid = $("#kGrid").data("kendoGrid");
grid.dataSource.query({
page:1,
pageSize:20,
filter:{
logic:"or",
filters:[
{field:"ID", operator:"contains",value:q},
{field:"Title", operator:"contains",value:q},
]
}
});
}
$("#btnSearch").kendoButton({
click:onSearch
})
$("#kGrid").kendoGrid({
dataSource:{
type:"odata",
transport:{
read:"ContactType/GetContactTypes"
},
schema:{
data:function(data){
return data.d.results
},
total:function(data){
return data.d.__count
},
},
serverPaging:true,
serverFiltering:true,
pageSize:20
},
height:550,
pageable:true,
columns:[
'CustomerID',
'CompanyName',
'ContactName',
'ContactTitle',
'Address',
'City',
'PostalCode',
'Country'
]
})
}
$(document).ready(onReady);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2014.2.903/js/kendo.all.min.js"></script>
</head>
<body>
<div>
<div>
<div>
Search By ID/Title
<input class=k-textbox type=text id="txtSearchString" placeholder="enter search text..." />
<button id="btnSearch">Search</button>
</div>
<br><br>
<div id="kGrid"></div>
</div>
</div>
</body>
</html>
public ActionResult GetContactTypes()
{
using (var context = CommerceChamberContext.GetContext())
{
var data = context.ContactTypes.Select(x => new { x.ID, x.Title }).ToList();
int count = data.Count;
var jsonData = new { total = count, data };
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
}
I have this code, but it does not Filter anything...
I like to have a text box and a button to initiate the search in server side.
You aren't doing anything with the query parameters in your server action(GetContactTypes).
If you look at what is posted if the browser dev tools when you click your Search button you will see something like this in the query string parameters:
$callback:jQuery112305717723066421754_1473786887565
$inlinecount:allpages
$format:json
$top:20
$filter:(substringof('SearchForThis',ID) or substringof('SearchForThis',Title))
Because you have configured the grid to use serverFiltering: true, it is your responsibility to incorporate the $filter(and $top, and $skip) parameter into your query with an appropriate WHERE clause that uses the passed $filter values.
As it stands right now, your server action is being passed the filter but you are returning your entire ContactTypes list.
Do you want to use client-side filtering?
Server filtering = you do the filtering on the server and return only the matching rows.
Client filtering = kendo will do the filtering in javascript with the dataSource it already has(will not call your server action)

Two way data binding with Polymer.Templatizer

I am trying to get two way data-binding between a host element and a template in Polymer using templatizer. For example if I am trying to keep two input boxes in-sync:
<html>
<body>
<my-element>
<template >
<input type="text" value="{{test::change}}" />
<div>The value of 'test' is: <span>{{test}}</span></div>
</template>
</my-element>
<dom-module id="my-element">
<template>
<input type="text" value="{{test::change}}" />
value:
<p>{{test}}</p>
<div id="items"></div>
<content id="template"></content>
</template>
</dom-module>
<script>
Polymer({
is: 'my-element',
test: {
type: String,
value: "a"
},
behaviors: [ Polymer.Templatizer ],
_forwardParentProp: function(prop, value) {debugger},
_forwardParentPath: function(path, value) {debugger},
_forwardInstanceProp: function(inst, prop, value) {debugger},
_forwardInstancePath: function(inst, path, value) {debugger},
ready: function() {
this._instanceProps = {
test: true
};
var templates = Polymer.dom(this.$.template).getDistributedNodes();
template = templates[1];
this.templatize(template);
var itemNode = this.stamp({ test: this.test});
Polymer.dom(this.$.items).appendChild(itemNode.root);
}
});
</script>
</body>
</html>
In the above code I hit the debugger in the _forwardInstanceProp but not any of the others. Why is this? Inside _forwardInstanceProp I can access my-element and manually update the test property. Is there a better way to do this? I also could add an observer on my-element to the test property and then propagate any changes in my-element to the template. Is there a better way to do that? I am just trying to understand what all four of these methods do and when/why they should be used.
It beats my why I can never get neither _forwardParentPath nor _forwardParentProp to run. However, I know when the other two run :)
_forwardInstanceProp runs for direct properties of model passed to stamp and _instanceProps is initialized:
this._instanceProps = {
text: true
};
var clone = this.stamp({
text: this.text
});
_forwardInstancePath on the other hand runs when you pass nested objects to stamp:
var clone = this.stamp({
nested: {
text: this.text
}
});
See this bin for an example: http://jsbin.com/kipato/2/edit?html,js,console,output
In the stamped template there are two inputs bound to two variables which trigger instanceProp and instancePath. Unfortunately I've been unable to fix the error thrown when the latter happens.

AngularJS - Sharing Data between controllers from json

actually I get stucked since several days with following problem. I like to create a small app which loads data from a json file. The app should consist of 3 views !
Show a list of data
Edit view for changing current data
add view to store new data
Now I learned to use a service which provides data to each controller for each view.
But for the time my service works only with generated data within my variable thing.
How Can I change this that my service will provide data from .json file which may be edited and updated with any controller !
Thanks
Here is my code and plnker
<!doctype html>
<html ng-app="project">
<head>
<title>Angular: Service example</title>
<script src="http://code.angularjs.org/angular-1.0.1.js"></script>
<script>
var projectModule = angular.module('project',[]);
projectModule.factory('theService', function() {
return {
thing : [{"DATE" : "2014","IATA":"DUS","DUTY":"10:12"},
{"DATE" : "2015","IATA":"MIA","DUTY":"10:12"},
{"DATE" : "2017","IATA":"JFK","DUTY":"10:12"}]
};
/*
return {
thing:[function($http) {
return $http.get('data.json').then(function(response) {})
return response.data;
}]
};
*/
});
function FirstCtrl($scope, theService) {
$scope.thing = theService.thing;
$scope.name = "First Controller";
}
function SecondCtrl($scope, theService) {
$scope.someThing = theService.thing;
$scope.name = "Second Controller!";
}
</script>
</head>
<body>
<div ng-controller="FirstCtrl">
<h2>{{name}}</h2>
<div ng-repeat="show in thing">
<p>
<b>DATE </b>{{show.DATE}}
<b>IATA </b>{{show.IATA}}
<b>DUTY </b>{{show.DUTY}}
</p>
</div>
<div ng-controller="SecondCtrl">
<h2>{{name}}</h2>
<div ng-repeat="edit in someThing">
<p>
<input ng-model="edit.DATE"/>
<input ng-model="edit.IATA"/>
<input ng-model="edit.DUTY"/>
</p>
</div>
</div>
</body>
</html>
All you have to do is use $http service and return it:
getJson: function() {
return $http.get('data.json')
}
Then in your controller you use it like this:
service.getJson(function(data) {
$scope.thing = data;
})
To convert object to json you need to use angular.fromJson i angular.toJson
Angular Docs
After that you do:
$http.post('yourjson);
to replace your current json (save changes).
You should also redownload it (to have everything in sync) using $http.get as I described above.
I have found an example example. I hope it helps.