Pushing object to observableArray - javascript

New to knockoutjs and I was following the tutorial on Loading and saving data, but I am having a little trouble. When I push my object to an array the array is empty. I am also using a form. Here is my code,
function Quiz(data) {
this.quiz_name = ko.observable(data.newQuizName);
this.quiz_type = ko.observable(data.newQuizType);
}
function QuizViewModel() {
var self = this;
self.quizzes = ko.observableArray([]);
self.newQuizName = ko.observable();
self.newQuizType = ko.observable();
self.addQuiz = function () {
self.quizzes.push(new Quiz({quiz_name: this.newQuizName(), quiz_type: this.newQuizType()}))
console.log(ko.toJSON(self.quizzes));
};
}
ko.applyBindings(new QuizViewModel());
and this is my HTML
<form name="quizzes" id="new-form-quizzes" data-bind="submit: addQuiz" style="display:none">
<div class="form-group">
<label for="quiz-name">Quiz Name</label>
<input type="text" class="form-control" id="quiz-name" aria-describedby="quiz name"
data-bind="value: newQuizName"
placeholder="Quize Name"/>
</div>
<div class="form-group">
<label for="quiz-type">Quiz Type</label>
<input type="text"
class="form-control"
id="quiz-type"
data-bind="value: newQuizType"
placeholder="Quiz Type"/>
</div>
<button type="submit">Save</button>
</form>
Not sure what I am doing wrong as both newQuizName and newQuizType do have values. Any help would be much appreciated.

You should pass an object (or an array) containing observables as an argument to ko.toJSON. Not the observable itself. So, you need to change your code to:
ko.toJSON(self.quizzes());
ko.toJSON internally calls ko.toJS. The latter method goes through the object and converts each observable to the value of that observable.
After making this change you'll find that there's another problem. The newly added Quiz object has undefined properties. This is because you are passing an object with quiz_name and quiz_type to the Quiz constructor function. But you are accessing newQuizName property from the data parameter. So change your code to:
function Quiz(data) {
this.quiz_name = ko.observable(data.quiz_name); // these 2 props must be changed
this.quiz_type = ko.observable(data.quiz_type);
}
function QuizViewModel() {
var self = this;
self.quizzes = ko.observableArray([]);
self.newQuizName = ko.observable();
self.newQuizType = ko.observable();
self.addQuiz = function() {
self.quizzes.push(new Quiz({
quiz_name: this.newQuizName(),
quiz_type: this.newQuizType()
}));
console.log(ko.toJSON(self.quizzes()));
};
}
ko.applyBindings(new QuizViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<form name="quizzes" id="new-form-quizzes" data-bind="submit: addQuiz">
<div class="form-group">
<label for="quiz-name">Quiz Name</label>
<input type="text" class="form-control" id="quiz-name" aria-describedby="quiz name" data-bind="value: newQuizName" placeholder="Quize Name" />
</div>
<div class="form-group">
<label for="quiz-type">Quiz Type</label>
<input type="text" class="form-control" id="quiz-type" data-bind="value: newQuizType" placeholder="Quiz Type" />
</div>
<button type="submit">Save</button>
</form>
<!--Table to display the added quizzes-->
<table data-bind="if: quizzes() && quizzes().length > 0">
<thead>
<th>Quiz Name</th>
<th>Quiz Type </th>
</thead>
<tbody data-bind="foreach: quizzes">
<tr>
<td data-bind="text:quiz_name"></td>
<td data-bind="text:quiz_type"></td>
</tr>
</tbody>
</table>

Related

Knockoutjs checked binding function call issue

I am trying to call a function when the check box is checked and set the field values accordingly. The checkbox and the address fields are like below
<div class="form-check">
<input class="form-check-input position-static" type="checkbox" id="SameAsShippingAddress" value="SameAsShippingAddress" data-bind="checked: sameAsShippingAddress" />
<label>Check this box if Shipping Address and Billing Address are the same.</label>
</div>
<div class="row">
<div class="col-md-6">
<h4>Shipping Address:</h4>
<div class="form-group required">
<label for="EmailCompetitor" class="control-label">Email:</label>
<input type="email" maxlength="150" id="EmailCompetitor" name="EmailCompetitor" class="form-control" data-bind="value: emailCompetitor" required />
</div>
<div class="form-group required">
<label for="FirstNameCompetitor" class="control-label">First Name:</label>
<input type="text" maxlength="150" id="FirstNameCompetitor" name="FirstNameCompetitor" class="form-control" data-bind="value: firstNameCompetitor" required />
</div>
</div>
<div class="col-md-6">
<h4>Billing Address:</h4>
<div class="form-group required">
<label for="EmailCompetitor_Billing" class="control-label">Email:</label>
<input type="email" maxlength="150" id="EmailCompetitor_Billing" name="EmailCompetitor_Billing" class="form-control" data-bind="value: emailCompetitor_Billing" required />
</div>
<div class="form-group required">
<label for="FirstNameCompetitor_Billing" class="control-label">First Name:</label>
<input type="text" maxlength="150" id="FirstNameCompetitor_Billing" name="FirstNameCompetitor_Billing" class="form-control" data-bind="value: firstNameCompetitor_Billing" required />
</div>
</div>
I want the check box boolean value captured seperately as well as when check box is checked it needs to call the function. So in js I have like
var orderRequestFormViewModel = function () {
var self = this;
self.currentPage = ko.observable(1);
self.referringPage = ko.observable();
...............
self.sameAsShippingAddress = ko.observable().extend({ required: false });
..........
self.sameAsShippingAddress = function () {
if (this.checked) {
$("#EmailCompetitor_Billing").val($("#EmailCompetitor").val());
$("#FirstNameCompetitor_Billing").val($("#FirstNameCompetitor").val());
} else {
$("#EmailCompetitor_Billing").val("");
$("#FirstNameCompetitor_Billing").val("");
}
}
But when the checkbox is checked/unchecked this function is not being called at all
I tried put the breakpoint but the function is not being it. New to this JS world, any help is greatly appreciated
Hey I made you a little snippet to show you that it works perfectly fine when using an observable and checked binding, when checkbox is checked, it is "true" if not it´s "false"
To make custom stuff like setting .val() of other input just use a computed function, it will be called whenever isChecked changes
var model = function () {
var self = this;
self.isChecked = ko.observable(false);
self.doStuffWhenChecked = ko.computed(function(){
if(self.isChecked()){
$('#textinput').val("whatever");
}else{
$('#textinput').val("");
}
},this);
}
var vm = new model();
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<label>Check this: </label>
<input type="checkbox" data-bind="checked: isChecked">
<span data-bind="visible: isChecked()"> Only show when checked </span>
<input type="text" id="textinput" >

Angular JS filter Search

I want to retain the selected check boxes as is even when I am
changing my search query. Initially I am posting some query in search
and selecting one of the resulted values, Now if I change my search
query, then New values will be my result. But I want to retain the
checkbox selected for the previous values...
`
//Demo of Searching and Sorting Table with AngularJS
var myApp = angular.module('myApp',[]);
myApp.controller('TableCtrl', ['$scope', function($scope) {
$scope.allItems = getDummyData();
$scope.resetAll = function()
{
$scope.filteredList = $scope.allItems ;
$scope.newEmpId = '';
$scope.newName = '';
$scope.newEmail = '';
$scope.searchText = '';
}
$scope.add = function()
{
$scope.allItems.push({EmpId : $scope.newEmpId, name : $scope.newName, Email:$scope.newEmail});
$scope.resetAll();
}
$scope.search = function()
{
$scope.filteredList = _.filter($scope.allItems,
function(item){
return searchUtil(item,$scope.searchText);
});
if($scope.searchText == '')
{
$scope.filteredList = $scope.allItems ;
}
}
$scope.resetAll();
}]);
/* Search Text in all 3 fields */
function searchUtil(item,toSearch)
{
/* Search Text in all 3 fields */
return ( item.name.toLowerCase().indexOf(toSearch.toLowerCase()) > -1 || item.Email.toLowerCase().indexOf(toSearch.toLowerCase()) > -1 || item.EmpId == toSearch
)
? true : false ;
}
/*Get Dummy Data for Example*/
function getDummyData()
{
return [
{EmpId:2, name:'Jitendra', Email: 'jz#gmail.com'},
{EmpId:1, name:'Minal', Email: 'amz#gmail.com'},
{EmpId:3, name:'Rudra', Email: 'ruz#gmail.com'}
];
}
.icon-search{margin-left:-25px;}
<br /> <br />
<div ng-app="myApp">
<div ng-controller="TableCtrl">
<div class="input-group">
<input class="form-control" ng-model="searchText" placeholder="Search" type="search" ng-change="search()" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-search"></span>
</span>
</div>
<table class="table table-hover data-table sort display">
<thead>
<tr>
<th class="EmpId"> <a href="" ng-click="columnToOrder='EmpId';reverse=!reverse">EmpId
</a></th>
<th class="name"> Name </th>
<th class="Email"> Email </th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in filteredList | orderBy:columnToOrder:reverse">
<td><input type="checkbox" name="test" />{{item.EmpId}}</td>
<td>{{item.name}}</td>
<td>{{item.Email}}</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-xs-3">
<input type="text" ng-model="newEmpId" class="form-control" placeholder="EmpId">
</div>
<div class="col-xs-3">
<input type="text" ng-model="newName" class="form-control" placeholder="Name">
</div>
<div class="col-xs-4">
<input type="email" ng-model="newEmail" class="form-control" placeholder="Email">
</div>
<div class="col-xs-1">
<button ng-click="add()" type="button" class="btn btn-primary">
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
</div>
</div> <!-- Ends Controller -->
</div>
`Fiddle
Try to add ng-model="item.selected" to your checkbox tag
<td><input ng-model="item.selected" type="checkbox" name="test" />{{item.EmpId}}</td>
Works for me, hope it helps.
Looks like this is happening because you are resetting the items here:
if($scope.searchText == '')
{
$scope.filteredList = $scope.allItems ;
}
and allItems doesn't tell anywhere if the checkbox needs to be selected on not. I would suggest you to update the code where you are creating the checkboxes, something like:
<td><input type="checkbox" name="test" ng-model=item.selected ng-checked=item.selected/>
Note that I have updated the item to have a 'selected' field which will tell if that item is selected or not(default could be false). While creating the checkbox I have linked the model using ng-model=item.selected
Updated fiddle at http://jsfiddle.net/3a3zD/194/

knockout - cannot apply bindings multiple times to the same element

I need to fill my trip table with data from two sources: from server using GET when the page loads (to have user's archive trips) and from observable values (when a user adds a new trip). Can I somehow merge those two scripts so that they apply bindings only once? Right now I get an error: You cannot apply bindings multiple times to the same element
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Add a trip</h3>
</div>
<div class="panel-body">
<div class="form-group">
<label for="from">From</label>
<input type="text" class="form-control" id="from" name="from" placeholder="From" data-bind="value: from">
</div>
<div class="form-group">
<label for="to">To</label>
<input type="text" class="form-control" id="to" name="to" placeholder="To" data-bind="value: to">
</div>
<a class="btn btn-primary btn-lg" role="button" data-bind="click: add()" >Add</a>
</div>
</div>
<div class="panel panel-default">
<div class=panel-heading>Your trips</div>
<table class=table>
<thead>
<tr>
<th>From</th>
<th>To</th>
</tr>
</thead>
<tbody data-bind="foreach: records">
<tr>
<td data-bind="text: from"></td>
<td data-bind="text: to"></td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript" src="js/knockout-3.4.0.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script type="text/javascript">
var AppViewModel = function() {
this.from = ko.observable();
this.to = ko.observable();
this.records = ko.observableArray();
};
var model = new AppViewModel();
model.add = function() {
model.records.push({
from: model.from(),
to: model.to()
});
//sending data to server
var data =
{
from : this.from(), to : this.to(), date : this.date(), price : this.price(), freeSeats : this.freeSeats()
}
alert(data);
$.post("/data", data, function(response)
{
})
}
ko.applyBindings(model);
</script>
<script>
function tripModel() {
this.records = ko.observableArray([]);
$.getJSON("/usersTrips", function(data) {
self.records(data);
})
}
ko.applyBindings(new tripModel());
</script>
Give the relevant elements IDs and then apply the models to only those DOM elements. For example,
Html:
<div id="add-trip" class="panel panel-default">
<div id="your-trips" class="panel panel-default">
And the binding:
ko.applyBindings(model, document.getElementById("add-trip"));
ko.applyBindings(new tripModel(), document.getElementById("your-trips"));
JSFiddle Example:
https://jsfiddle.net/hellobrett/49xaqj46/1/
JSFiddle example going the other direction:
https://jsfiddle.net/hellobrett/49xaqj46/2/
Reference:
http://knockoutjs.com/documentation/observables.html
In case you’re wondering what the parameters to ko.applyBindings do,
The first parameter says what view model object you want to use with the declarative bindings it activates
Optionally, you can pass a second parameter to define which part of the document you want to search for data-bind attributes. For example, ko.applyBindings(myViewModel, document.getElementById('someElementId')). This restricts the activation to the element with ID someElementId and its descendants, which is useful if you want to have multiple view models and associate each with a different region of the page.

Clear form after submit

I am submitting a form - and adding the contents to an array, however whenever the item is added to the array, it is still bound to the form.
I would like to add the item, clear the form. Something like jquery's reset();
Here's my template:
<div class="col-xs-12" ng-controller="ResourceController">
<div class="col-md-4">
<h3>Name</h3>
</div>
<div class="col-md-8">
<h3>Description</h3>
</div>
<form class="form-inline" role="form" ng-repeat="item in resources">
<div class="form-group col-md-4">
<input type="text" class="form-control" value="{{ item.name }}"/>
</div>
<div class="form-group col-md-7">
<input type="text" class="form-control" value="{{ item.description }}"/>
</div>
</form>
<form class="form-inline" role="form" name="addResourceForm" ng-submit="addResource()">
<div class="form-group col-md-4">
<input type="text" class="form-control" name="name" ng-model="name" placeholder="Name"/>
</div>
<div class="form-group col-md-7">
<input type="text" class="form-control" name="description" ng-model="description" placeholder="Description"/>
</div>
<div class="form-group col-md-1">
<button type="submit" class="btn btn-default">Add</button>
</div>
</form>
</div>
And my controller:
(function(){
var app = angular.module('event-resources', []);
app.controller('ResourceController', function($scope){
$scope.addResource = function(){
$scope.resources.push(this);
}
var defaultForm = {
name : '',
description: ''
};
$scope.resources = [
{
name: 'Beer',
description: 'Kokanee'
},
{
name: 'Pucks',
description: 'Black Round Things'
}
]
});
})();
Use angular.copy() to copy the item data to the resources array, and then you can safely clear the item data. The angular.copy() makes a deep copy of the object, which is what you want.
Alternately, here is a simpler method, which doesn't use any extra method calls:
$scope.addResource = function() {
$scope.resources.push({
name: $scope.name, // recreate object manually (cheap for simple objects)
description: $scope.description
});
$scope.name = ""; // clear the values.
$scope.description = "";
};
$scope.addResource = function(){
$scope.resources.push(angular.copy(this));
$scope.name="";
$scope.description=""
}
Push the copy to the resources array and change name and description back to ""

onchange can't find function

I'm trying to make an input change value depending on another inputs value.
What I've come up with so far is this. But when running the onchange commmand I get an error that updateCity doesn't exist. Even thou it's there. Am I doing something wrong, is pretty new to coding with javascript.
<div class="form-inline">
<div class="form-group">
<label class="sr-only" for="zip">Indtast postnummer</label>
<input class="form-control" type="text" name="register_zip" id="zip" placeholder="Indtast postnummer" value="#city.zip" onchange="updateCity(this.value)" />
</div> <!-- class="form-group" -->
<div class="form-group">
<label class="sr-only" for="city">Indtast by</label>
<input class="form-control" type="text" name="register_city" id="city" placeholder="Indtast by" value="#city.name" />
</div> <!-- class="form-group" -->
</div>
<script>
function updateCity(city_zip) {
var city = findCity(city_zip);
if(city != null){
document.getElementById("register_city").value = city_name;
}
};
function findCity(zip){
var cities = [];
#{
foreach (var c in cities)
{
<text>
cities.push({zip: #c.zip, name: #c.name});
</text>
}
}
for(var i=0;i<cities.length;i++){
if(cities[i].zip == zip){
return cities[i]
}
}
};
</script>

Categories

Resources