How to implement paging in "with" bindings in knockout - javascript

I have a requirements in my project where I need to put my selected item in the Modal and user can click next to show the next item.
I am using a with binding to display the content of selected in a form. I don't have an idea on how can I apply paging inside a "With" binding.
<div class="container" data-bind="with: itemForEditing">
<div id="riskRegisterForm" class="modal hide fade">
<div class="modal-header" style="background:#4bafef; height: 30px;">
<h5 style="color:#FFFFFF; font:16px Arial;">Item</h5>
</div>
<div class="modal-body" style="background:#fff">
<div>
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="itemName">Name</label>
<div class="controls">
<input type="text" id="itemName" data-bind="value: name" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="itemPrice">Price</label>
<div class="controls">
<input type="number" step=".01" id="itemPrice" data-bind="value: price" />
</div>
</div>
</form>
</div>
</div>
<div class="modal-footer">
<button type="button" data-dismiss="modal" class="btn" data-bind="click:$parent.revertItem">Cancel</button>
<button type="button" data-dismiss="modal" class="btn" data-bind="click:$parent.acceptItem">Update</button>
</div>
<span><a href=#>next</a></span>
<span><a href=#>prev</a></span>
</div>
</div>
when I click the next it should autmatically select the next records and put in the contorls. Here is the JsFiddle http://jsfiddle.net/ramon26cruz/Tt96J/6/

I've had a go at this. I changed my tack from above. Basically I created 2 method, a next and a prev. In the methods I find the index of the selected / editable object in the array and the either increment or decrement based on which method has been used. I then update the selected and editable property objects:
var Item = function(data) {
this.name = ko.observable();
this.price = ko.observable();
//populate our model with the initial data
this.update(data);
};
//can pass fresh data to this function at anytime to apply updates or revert to a prior version
Item.prototype.update = function(data) {
this.name(data.name || "new item");
this.price(data.price || 0);
};
var ViewModel = function(items) {
this.index = 0;
//turn the raw items into Item objects
this.items = ko.observableArray(ko.utils.arrayMap(items, function(data) {
return new Item(data);
}));
//hold the currently selected item
this.selectedItem = ko.observable();
//make edits to a copy
this.itemForEditing = ko.observable();
this.selectItem = this.selectItem.bind(this);
this.acceptItem = this.acceptItem.bind(this);
this.revertItem = this.revertItem.bind(this);
this.next = this.next.bind(this);
this.prev = this.prev.bind(this);
};
ko.utils.extend(ViewModel.prototype, {
//select an item and make a copy of it for editing
selectItem: function(item) {
this.selectedItem(item);
this.itemForEditing(new Item(ko.toJS(item)));
},
next:function(){
var pos = this.items.indexOf(this.selectedItem()) + 1;
if(pos > this.items().length - 1){pos = 0};
this.selectedItem(this.items()[pos]);
this.itemForEditing(new Item(ko.toJS(this.items()[pos])));
},
prev:function(){
var pos = this.items.indexOf(this.selectedItem()) - 1;
if(pos < 0){pos = this.items().length - 1};
this.selectedItem(this.items()[pos]);
this.itemForEditing(new Item(ko.toJS(this.items()[pos])));
},
acceptItem: function(item) {
var selected = this.selectedItem(),
edited = ko.toJS(this.itemForEditing()); //clean copy of edited
//apply updates from the edited item to the selected item
selected.update(edited);
//clear selected item
this.selectedItem(null);
this.itemForEditing(null);
},
//just throw away the edited item and clear the selected observables
revertItem: function() {
this.selectedItem(null);
this.itemForEditing(null);
}
});
ko.applyBindings(new ViewModel([
{ name: "Cheese", price: 2.50 },
{ name: "Pepperoni", price: 3.25 },
{ name: "Deluxe", price: 4.25 }
]));
Here's a link to my JS Fiddle.

One way to do it would be like this:
<div class="container" data-bind="with: itemForEditing">
<!-- ... -->
<span>next</span>
<span>prev</span>
</div>
and
ko.utils.extend(ViewModel.prototype, {
// offset the selected item by a certain amount (i.e. -1/+1 for next/prev)
offsetItem: function (by) {
var items = this.items(),
i = ko.utils.arrayIndexOf(items, this.selectedItem()),
newItem = (i > -1) ? items[i + by] : null;
if (newItem) {
this.selectItem(newItem);
}
},
prevItem: function () {
this.offsetItem(-1);
},
nextItem: function () {
this.offsetItem(1);
},
/* ... */
}
See it live http://jsfiddle.net/Tt96J/11/

Related

HTML Form Select Element Not Allowing Changes

I have a form that I am creating for my kids' school and am having issues with the drop-down list to choose the student name. The list is populated via JavaScript code, importing a student list from an associated google sheet. I added a console log for each option as they are created to show that they are in fact being created. Developer options on the link should reveal that.
Once I load the form I am unable to change the student identifier from the default option. I can't tell what I've done wrong. If I hard code the values into the HTML it works fine, but I want the teacher to be able to add and remove students via the spreadsheet because that is a more user-friendly and flexible implementation.
form.html code
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>
body {
background: rgb(244, 235, 234)
}
.outer-field {
border-radius: 15px;
background: white;
height: 150px;
margin: 10px;
padding: 20px;
}
.title {
padding-left: 2%;
font-weight: bold;
}
</style>
</head>
<body>
<div class="row">
<div class="col s8 offset-s2 offset-s2">
<!--Document Header-->
<div class="outer-field" style="height: 100px">
<h4>Golden Apple Reader Book Submissions</h4>
</div>
<!--Form to submit ISBN and autopopulate title and page count-->
<form id="myForm" onsubmit="event.preventDefault(); formSubmit(this) ">
<!--Creates ISBN entry field-->
<div class="outer-field">
<div class="title">Book ISBN</div>
<div class="col s8">
<div class="input-field">
<input type="text" id="ISBN" name="ISBN" class="UUID validate" form="myForm">
<label for="ISBN">ISBN</label>
</div>
</div>
<!--Creates button to check ISBN data-->
<button class="btn waves-effect waves-light" id="btn" style="margin-left: 3%" type="button" onclick="populateDetails(); return false;">Autofill Book Data From ISBN
<i class="material-icons right">send</i>
</button>
</div>
<!--Creates student name entry field-->
<div class="outer-field">
<div class="title">Student Name</div>
<div class="input-field col s12">
<select form="myForm" name="StudentID" id="StudentID" required>
<!--Add student IDs and names here-->
<!--<option value="212702">John</option>
<option value="212703">Henry</option>
<option value="003">003</option>-->
</select>
</div>
</div>
<!--Creates book title entry field-->
<div class="outer-field">
<div class="title">Book Required Information</div>
<div class="col s8">
<div class="input-field">
<input type="text" id="Title" name="Title" class="name" form="myForm" required>
<label for="Title">Book Title</label>
</div>
</div>
<!--Creates book page count entry field-->
<div class="col s4">
<div class="input-field">
<input type="number" id="PageCount" name="PageCount" class="pages" form="myForm" required>
<label for="PageCount">Book Page Count</label>
</div>
</div>
</div>
<!--Creates button to submit data-->
<button class="btn waves-effect waves-light" type="submit" name="action" style="margin-left: 3%" >Submit
<i class="material-icons right">send</i>
</button>
</form>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
M.FormSelect.init(document.querySelectorAll('select'));
//function to populate student list element
(function () {
google.script.run.withSuccessHandler(
function (selectList) {
var select = document.getElementById('StudentID');
for( var i=0; i<selectList.length; i++ ) {
//initial attempt commented here for troubleshooting
//var option = document.createElement('option');
//option.value = selectList[i][0];
//option.text = selectList[i][4];
var option = new Option(selectList[i][4], selectList[i][0]);
console.log(option);
select.add(option, undefined);
}
console.log(select)
}
).getSelectList();
}());
//Uses the ISBN to populate the book title and page quantity
function populateDetails(){
isbn=document.getElementById('ISBN').value;
//isbn=9781492680550;//for debugging only
var url = "https://www.googleapis.com/books/v1/volumes?country=US&q=isbn:" + isbn;
var obj
var title="No Entry Identified";
var pageCount=0;
var titleout=document.getElementById('Title');
var pageout=document.getElementById('PageCount');
//fetches URL data from google books API
fetch(url)
.then(res => res.json())
.then(data => obj = data)
.then(
function(settitle){
//Assigns title to variable and text field
title = obj.items[0].volumeInfo.title
titleout.value=title;
titleout.focus();
},
function(titlerror){
})
.then(
function(setpages){
//Assigns page count to variable and text field
pageCount = obj.items[0].volumeInfo.pageCount
pageout.value=pageCount;
pageout.focus();
},
function(pageerror){
})
//In the case that no entry is found in google books API, assigns default values to text fields and deconflicts the overlapping label and value fields
titleout.value=title;
titleout.focus();
pageout.value=pageCount;
pageout.focus();
}
//Submits form data to spreadsheet
function formSubmit (data) {
var dataToSubmit = {
studentID: data.StudentID.value,
title: data.Title.value,
pageCount: data.PageCount.value
}
//Provides a success message to the user
google.script.run.withSuccessHandler(function () {
myForm.reset()
M.toast({html: "Thank you! You have successfully submitted!"})
}).submitData(dataToSubmit)
}
</script>
</body>
</html>
code.gs code
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Submissions")
var ss2= SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Students")
var last=ss2.getLastRow();
var students=ss2.getRange(2,1,last-1,5).getValues();
function getSelectList() {
try {
return students;
}
catch(err) {
Logger.log(err);
}
}
function doGet() {
return HtmlService.createTemplateFromFile('form').evaluate().addMetaTag('viewport', 'width=device-width, initial-scale=1')
}
function submitData (data) {
ss.appendRow([new Date(),data.studentID, data.title, data.pageCount])
}
spreadsheet content:
Student ID Number
Student Name
Teacher Name
Grade
Concatenation
UNK1
John
TeacherA
K
Grade: K, Teacher: TeacherA, Name: John
UNK2
Henry
TeacherA
K
Grade: K, Teacher: TeacherA, Name: Henry
UNK3
Paige
TeacherA
K
Grade: K, Teacher: TeacherA, Name: Paige
UNK4
Raelyn
TeacherA
K
Grade: K, Teacher: TeacherA, Name: Raelyn
UNK5
Danielle
TeacherA
K
Grade: K, Teacher: TeacherA, Name: Danielle
UNK6
Olivia
TeacherA
K
Grade: K, Teacher: TeacherA, Name: Olivia
When I saw your script, I thought that there is a modification point. Please modify as follows.
From:
M.FormSelect.init(document.querySelectorAll('select'));
//function to populate student list element
(function () {
google.script.run.withSuccessHandler(
function (selectList) {
var select = document.getElementById('StudentID');
for( var i=0; i<selectList.length; i++ ) {
//initial attempt commented here for troubleshooting
//var option = document.createElement('option');
//option.value = selectList[i][0];
//option.text = selectList[i][4];
var option = new Option(selectList[i][4], selectList[i][0]);
console.log(option);
select.add(option, undefined);
}
console.log(select)
}
).getSelectList();
}());
To:
//function to populate student list element
(function () {
google.script.run.withSuccessHandler(
function (selectList) {
var select = document.getElementById('StudentID');
for( var i=0; i<selectList.length; i++ ) {
//initial attempt commented here for troubleshooting
//var option = document.createElement('option');
//option.value = selectList[i][0];
//option.text = selectList[i][4];
var option = new Option(selectList[i][4], selectList[i][0]);
console.log(option);
select.add(option, undefined);
}
console.log(select)
M.FormSelect.init(document.querySelectorAll('select')); // Moved.
}
).getSelectList();
}());
In this modification, M.FormSelect.init(document.querySelectorAll('select')) was moved.
I thought that (function () { and }()); might be not required to be used.

Observable inside Observable array that track two elements KO js

Array of 3 radio box and input text that are tied together, selection of radio would dynamically pop text that user can add on top of. I'm able to populate the text box, but can't track changes of both afterward as I need some sort of observable that trace both change and give me the total value of both (e.g. 'File: c:\temp\data.cs'
Here my HTML
<!-- ko foreach: includes -->
<div class="col-md-3 mt-3 offset-3 ">
<input class="col-md-2 " type="radio" value="file:" data-bind="checked: type, attr: { name: name }">File
<input class="col-md-2" type="radio" value="dir:" data-bind="checked: type, attr: { name: name }">Dirs
<input class="col-md-2" type="radio" value="glob:" data-bind="checked: type, attr: { name: name }">Glob
</div>
<div class=" col-md-6 pt-3 ">
<input type="text" class="form-control" required data-bind="attr: { name: name } , value: value">
</div>
<!-- /ko -->
</div>
<div class="col-sm-3 mt-4 mb-4 offset-4" id="firstPath">
<button type="button" class="form-control btn btn-info ml-3" data-bind="click: addInclude">Add new include line</button>
</div>
And my JS
self.includes = ko.observableArray([{
name: "package[][includes][0]",
type: "file:",
value: ko.observable()
}]);
self.addInclude = function () {
self.includes.push({
name: `package[][includes][${includeCounter++}]`,
type: `file`,
value: ko.observable("file")
});
};
How to make the value property in includes observable array track both radio selection + input text value for the related row ?
I tried Ko.Computed but the issue is when initialing the type in includes observable is not define yet for relative row (default value for type is file)
The type member should be observable. So, through subscribe, you can set the value of value each time type changes.
function myViewModel() {
var self = this;
self.includeCounter = 0;
self.includes = ko.observableArray([]);
self.addInclude = function () {
var type = ko.observable('file:');
var value = ko.observable('file:');
type.subscribe((newValue) => {
value(newValue);
}, self);
var item = {
name: `package[][includes][${self.includeCounter++}]`,
type: type,
value: value
};
self.includes.push(item);
};
self.addInclude();
}
var vm = new myViewModel();
ko.applyBindings(vm);

assign value to selected input box in a row using Angular js

row add using angular code. Input box model name is "code". when click this input box a model open and when any row select using angularjs then there is a problem when I assign value using $scope.code it will assign to all input box in but I want to assign that value by which ng-click performed.
<table class="table table-bordered table-hover table-condensed">
<tr style="font-weight: bold">
<td style="width:35%">Code</td>
</tr>
<tr ng-repeat="user in users">
<td>
<!-- editable username (text with validation) -->
<span ng-show="editDel">
#{{ user.name || 'empty' }}
</span>
#{{code}}
<input type="text" name="" ng-model="code" ng-show="saveCancel" ng-click="getCode()">
</td>
<td style="white-space: nowrap">
<!-- form -->
<form ng-show="saveCancel" class="form-buttons form-inline">
<button type="submit" class="btn btn-primary">
save
</button>
<button type="button" ng-click="isCancel()" class="btn btn-default">
cancel
</button>
</form>
<div class="buttons" ng-show="editDel">
<button class="btn btn-primary" ng-click="editUser($index)">edit</button>
<button class="btn btn-danger" ng-click="removeUser($index)">del</button>
</div>
</td>
</tr>
</table>
$scope.users = [];
$scope.saveCancel = false;
$scope.editDel = true;
$scope.saveUser = function(data, id) {
//$scope.user not updated yet
angular.extend(data, {id: id});
return $http.post('/saveUser', data);
};
// remove user
$scope.removeUser = function(index) {
$scope.users.splice(index, 1);
};
// add user add row
$scope.addUser = function() {
$scope.inserted = {
id: $scope.users.length+1,
name: '',
status: null,
group: null
};
// $scope.taxNamePopup();
$scope.users.push($scope.inserted);
};
$scope.getCode = function(){
// alert(input_id);
this.code = 'nameValue';//**this assin value to selected box**
// jQuery("#tax_modal").modal("hide");
var request = $http({
method: "get",
url: base_url+"/load-tax",
data: {
},
}).then(function successCallback(response) {
jQuery("#tax_list").modal("show");
$scope.tax_data1 = response;
}, function errorCallback(response) {
});
}
$scope.getSelected = function(id,code,desc,rate){
alert(code);
this.code = code;//**but i want to assign value to selected input box from here**
jQuery("#tax_list").modal("hide");
}
$scope.safeApply(function() {
$scope.editUser = function($id){
this.saveCancel = true;
this.editDel = false;
}
});
$scope.isCancel = function(){
this.saveCancel = false;
this.editDel = true;
}
it is because ng-model is codefor every input.
Change it with following code example
<input type="text" name="" ng-model="code_$index" ng-show="saveCancel" ng-click="getCode($index)">
Now above code will change the ng-model unique for each input field and retrieve the value in getCode on the basis of index.
the model should be different for each ng-repeat item
EDIT change from getCode(user.code) to getCode(user)
<input type="text" name="" ng-model="user.code" ng-show="saveCancel" ng-click="getCode(user)">
use the param how ever you want ;)
$scope.getCode = function(user){
angular.forEach($scope.users, function (value, key) {
if (value.id == user.id) {
$scope.users[key]=user;
}
});
}

Disable required attribute on one radio button

I have 3 radio buttons in a bootstrap modal and I want only 2 of them to be required in order to submit the form. I created a requiredCheck() function that's using a jQuery implementation so I'd rather not use this. What is the best way to disable the required attribute for one radio button, when using AngularJS?
HTML
<form name="jawn">
<div class="modal-header">
<h3 class="modal-title">Jawn</h3>
</div><!-- /modal-header -->
<div class="modal-body">
<ul>
<li ng-repeat="item in items">
<label for="optionsRadios">
<input type="radio" ng-model="$parent.data.status" name="optionsRadios" ng-value="item.id" required/ ng-click="requiredCheck()"> {{item.name}}
</label>
</li>
</ul>
<h4>Comment</h4>
<textarea ng-class="{error: thing.comment.$dirty && thing.comment.$invalid}" type="text" rows="3" cols="64" ng-model="data.comment" ng-minlength="4" ng-required></textarea>
<span class="error" ng-show="thing.comment.$error.required">required</span>
</div><!-- /modal-body-->
<div class="modal-footer">
<button type="submit" class="btn btn-warning" ng-click="ok()" ng-disabled="thing.$invalid">Part Jawn</button>
<button type="button" ng-click="cancel()" class="btn btn-default">Cancel</button>
</div>
</form>
JavaScript
angular.module('app')
.controller('ModalController', function($scope, $modalInstance, $timeout) {
'use strict';
$scope.item = 0;
$scope.items = [
"Zero": 0,
"Uno": 1,
"Dos": 2
];
$scope.requiredCheck = function () {
var $btnWarning = $('.btn-warning');
var $textarea = $('textarea');
$timeout(function() {
if($scope.data.status === item.Dos) {
$btnWarning.removeAttr('disabled');
$textarea.removeAttr('required');
} else if ($scope.data.status === 3) {
$btnWarning.prop('disabled',true);
$textarea.prop('required', true);
} else if ($scope.data.status === 3) {
$btnWarning.prop('disabled',true);
$textarea.prop('required', true);
}
},100);
};
$scope.ok = function () {
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
$modalInstance.close();
};
});
You should use angualrjs approach. As you have already specified ngRequired set true/false from controller.
From DOCs
ngRequired: Sets required attribute if set to true
HTML
<textarea ng-model="data.comment" ng-required="textRequired"></textarea>
Script
$scope.requiredCheck = function () {
if(condition){
$scope.textRequired = false;
}else{
$scope.textRequired = true;
}
};
Note: I have simplified the answer

Getting data from the server and using ng-options in select control is failing to show options

this my HTML
<div ng-app="timeTable" ng-controller="addCoursesCtrl">
<button class="btn btn-primary" ng-click="addNewCourse()">Add New Course</button><br/><br/>
<fieldset ng-repeat="choice in choices">
<div class="row">
<div class="col-md-6">
<select class="form-control" ng-model="choice.type" ng-options="s for s in coursetoAdd">
<option value="{{s.shortCut}}">{{s.name}}</option>
</select>
</div>
<div class="col-md-6">
<input type="text" placeholder="Enter Course Name" name="" class="form-control" ng-model="choice.course"/>
</div>
</div>
<br/>
</fieldset>
<button class="btn btn-primary" ng-click="convertAndSend()">Submit</button>
</div>
this the js
var timeTable = angular.module("timeTable",[]);
timeTable.controller("addCoursesCtrl", function ($scope,$http) {
$scope.choices = [{ course: '', type: '' }];
$scope.coursetoAdd ;
$http.get("/Semster/getSuggtedCourses").then(function (response) {
$scope.coursetoAdd = response.data;
});
$scope.addNewCourse = function () {
var newITemNo = $scope.choices.length + 1;
$scope.choices.push({ course: '', type: '' });
};
$scope.convertAndSend = function () {
var asJson = angular.toJson($scope.choices);
console.log(asJson);
$http.post('/Semster/Add', asJson);
};
});
this code bind an object {"course":...,"type":....} every time you click on add course ,and add input field dynamically , my problem is with select control,I'm getting the data from server and use it with ng-optin ,but all it shows it's just [object Object] in select option not the real value.
Assuming that the data returned from getSuggestedCourses is an array of objects, the ng-options selector:
s for s in courseToAdd
will bind s to each object in the array. You need to bind to the fields in the object like this
s.value as s.name for s in courseToAdd

Categories