HTML Form Select Element Not Allowing Changes - javascript

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.

Related

How do you implement input( or form) dependent on a select menu(drop down list)?

I'm working on grading system and I'm currently working on the form that's deals with the user entering the students results now the form I have, has 2 drop-down list(classroom, students) that are dependent. The issue and where I'm stuck is
When the user select the classroom the second drop-down menu will only show the students in that class, I have already figure that out..the issue is I want the input fields for how much subject the student is doing to appear so that the user can enter the grades for each subject specific to that student in the class
Eg if I select classroom 1b and selected student Mary.. if Mary is doing 5 subjects then 5 input field should appear so that I can enter the mark for the subjects
Link with a video showing what I'm talking about video showing an examplehttps://drive.google.com/file/d/11FoCZyOBVdUhTcvCqA1Ke0fEgRmMVC-G/view?usp=drivesdk
Models.py
Class Classroom(models.Models): name = models.charfield()
Class marks (models.Models): classroom = models.foreignkey(Classroom) Grade = models.Floatfield()
Html form
<div class="container-fluid">
<form id="result-form" method="post">
{% csrf_token %}
<!-- Modal -->
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel"> {% block modal-title%} Add Result {% endblock%}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12" id="msg8" style="font-size: 2rem; color:rgb(255, 144, 47)"></div>
<div class="col-md-12 form-group p-2">
<label class="form-label">Class Name</label>
{% render_field form.room class+="form-control" %}
</div>
<div class="col-md-12 form-group p-2">
<label class="form-label">Exam Name</label>
{% render_field form.exam class+="form-control" %}
</div>
<div class="col-md-12 form-group p-2">
<label class="form-label">Student</label>
{% render_field form.student class+="form-control select2" %}
</div>
<div class="hidden" id="subject-fields"></div>
<div class="form-group mb-3 pt-2">
<button type="button" id="resBtn" class="btn btn-info" title="Add">Submit</button>
</div>
</div>
</div>
</form>
</div>
{% block script%}
{% endblock%
script
$(document).on('click', '#submit-btn', function(event){
var response_data = []
var subject_name= $('.course');
var subject_objs = $('.subject_id');
for(i=0;i<subject_name.length;i++){
var subject_id = $(subject_objs[i]).find('input').val();
var grade_input = {
"Marks": subject_id,
}
response_data.push(grade_input);
}
$.ajax({
type: "POST",
url: "{% url 'marks' %}",
data: response_data,
success: function(response){
alert("Success");
}
});
});
This is how your view should look like.
def question_choice_view(request):
if request.method == "POST":
question_choice_data = request.POST['data']
I am not a jQuery User. As far as i can see i would put a eventlistener on the student form via .addEventListener('change', (event)See here. This would fire a function every time something changes on the select option. With that you could also collect the selected option values of the classroom and student name and make a request to get the subject names for the chosen student. After successful response i would insert the subject fields via JavaScript in the DOM.
**
function createInput(item) {
// This function takes a item and creates a new input
var newLabel = ' <br><label for="$item-mark">$item:</label>'
var newInput = '<input type="text" id="$item-mark-id" name="$item-mark"><br><br>';
newLabel = newLabel.replaceAll("$item", item)
newInput = newInput.replaceAll("$item", item)
// combine into a single str
newInput = newLabel + newInput
var studInput = document.getElementById("student-id");
// insert element inputs after student
studInput.insertAdjacentHTML('afterend', newInput);
}
function cleanOldInputs(item) {
var oldELement = item + "-mark-id"
oldELement = document.getElementById(oldELement)
if (oldELement) {
// remove old label and input
oldELement.previousSibling.remove()
oldELement.remove()
} else {}
}
function getAPIcall() {
// This is what your API sends
var responsObject = ["writing", "creativity"];
// loop throug
responsObject.forEach(item => {
// if you already picked a student clean old inputs from DOM
cleanOldInputs(item)
// send to function for input creation
createInput(item)
})
}
// get the Student Input
var studentSelect = document.getElementById("student-id");
studentSelect.addEventListener("click", function() {
// Fire anything you like
getAPIcall()
});
<form action="/action_page.php">
<label for="student">Choose a student:</label>
<select name="student" id="student-id">
<option value="harry">harry</option>
<option value="ivy">ivy</option>
</select>
</form>
Quick and dirty**

Why am I getting the alert message for any buttons clicked? + Hints needed

Basically, I am trying to add the functionality where, upon clicking the blue button, the reading status would change to either <Yes> or <Not>. Any leads on that sense would be much appreciated. Secondary, I am lost on why at them moment, I am getting the alert when that button is clicked, The book is not being deleted (unless you click on the correct icon) but still... I can't figure our the flaw on my logic!!
JS Code:
// Book Class: Represents a Book
class Book {
constructor(title, author, pages, isRead) {
this.title = title;
this.author = author;
this.pages = pages;
this.isRead = isRead;
}
}
// UI Class: Handle UI Tasks
class UI {
static displayBooks() {
const books = Store.getBooks();
books.forEach((book) => UI.addBookToList(book));
}
static addBookToList(book) {
const list = document.querySelector("#book-list");
const row = document.createElement("tr");
row.innerHTML = `
<td>${book.title}</td> </button>
<td>${book.author}</td>
<td>${book.pages}</td>
<td><button class="btn btn-sm btn-primary">${book.isRead}</button></td>
<td>X</td>
`;
list.appendChild(row);
}
static deleteBook(el) {
if (el.classList.contains("delete")) {
el.parentElement.parentElement.remove();
}
}
static showAlert(message, className) {
const div = document.createElement("div");
div.className = `alert alert-${className}`;
div.appendChild(document.createTextNode(message));
const container = document.querySelector(".container");
const form = document.querySelector("#book-form");
container.insertBefore(div, form);
// Vanish in 3 seconds
setTimeout(() => document.querySelector(".alert").remove(), 3000);
}
static clearFields() {
document.querySelector("#title").value = "";
document.querySelector("#author").value = "";
document.querySelector("#pages").value = "";
document.querySelector("#isRead").value = "";
}
}
// Store Class: Handles Storage
class Store {
static getBooks() {
let books;
if (localStorage.getItem("books") === null) {
books = [];
} else {
books = JSON.parse(localStorage.getItem("books"));
}
return books;
}
static addBook(book) {
const books = Store.getBooks();
books.push(book);
localStorage.setItem("books", JSON.stringify(books));
}
static removeBook(pages) {
const books = Store.getBooks();
books.forEach((book, index) => {
if (book.pages === pages) {
books.splice(index, 1);
}
});
localStorage.setItem("books", JSON.stringify(books));
}
}
// Event: Display Books
document.addEventListener("DOMContentLoaded", UI.displayBooks);
// Event: Add a Book
document.querySelector("#book-form").addEventListener("submit", (e) => {
// Prevent actual submit
e.preventDefault();
// Get form values
const title = document.querySelector("#title").value;
const author = document.querySelector("#author").value;
const pages = document.querySelector("#pages").value;
const isRead = document.querySelector("#isRead").value;
// Validate
if (title === "" || author === "" || pages === "" || isRead === "") {
UI.showAlert("Please fill in all fields", "danger");
} else {
// Instatiate book
const book = new Book(title, author, pages, isRead);
// Add Book to UI
UI.addBookToList(book);
// Add book to store
Store.addBook(book);
// Show success message
UI.showAlert("Book Added", "success");
// Clear fields
UI.clearFields();
}
});
// Event: Remove a Book
document.querySelector("#book-list").addEventListener("click", (e) => {
// Remove book from UI
UI.deleteBook(e.target);
// Remove book from store
Store.removeBook(
e.target.parentElement.previousElementSibling.previousElementSibling
.textContent
);
// Show success message
UI.showAlert("Book Removed", "success");
});
HTML Code:
!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My BookListApp</title>
<link
rel="stylesheet"
href="https://bootswatch.com/4/yeti/bootstrap.min.css"
/>
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.15.4/css/all.css"
integrity="sha384-DyZ88mC6Up2uqS4h/KRgHuoeGwBcD4Ng9SiP4dIRy0EXTlnuz47vAwmeGwVChigm"
crossorigin="anonymous"
/>
</head>
<body>
<div class="container mt-4">
<h1 class="display-4 text-center">
<i class="fas fa-book-open text-primary"></i>My
<span class="text-primary">BookList</span> App
</h1>
<form id="book-form">
<div class="form-group">
<label for="title">Title:</label>
<input type="text" id="title" class="form-control" maxlength="30" />
</div>
<div class="form-group">
<label for="author">Author:</label>
<input type="text" id="author" class="form-control" maxlength="20" />
</div>
<div class="form-group">
<label for="pages">Pages:</label>
<input
type="number"
id="pages"
class="form-control"
min="1"
max="10000"
/>
</div>
<div class="form-group">
<label for="isRead">Read:</label>
<select type="number" id="isRead" class="form-control">
<option value="" selected disabled hidden></option>
<option value="Yes">Yes</option>
<option value="No">No</option>
</select>
</div>
<input
type="submit"
value="Add Book"
class="btn btn-primary btn-block"
/>
</form>
<table class="table table-striped mt-5">
<thead>
<tr>
<th>Title:</th>
<th>Author:</th>
<th>Pages:</th>
<th>Read:</th>
<th></th>
</tr>
</thead>
<tbody id="book-list"></tbody>
</table>
</div>
<script src="./src/app.js"></script>
</body>
</html>
Thanks!
This onclick is too broad:
document.querySelector("#book-list").addEventListener("click",
It's triggered for every click anywhere in the books list/table. Not just on buttons. Anywhere. If you don't click on a button, JS breaks. If you hit the Yes/No button, JS doesn't break, but it tries to delete the book, kinda.
Broken jsfiddle: https://jsfiddle.net/do42Lkqn/
Solution: explicitly check for which button is clicked. Inside the click handler, you could do:
if (!e.target.closest('.delete')) return;
So if you didn't click a .delete element, it won't do anything.

Not Able To Display Multiple Things in an Array

I have made an array that stores what I have inputted into local storage which works like this
<form name="myform" action="" method="GET">
Event Name: <INPUT TYPE="text" NAME="name" VALUE="" id="input1"><br />
Event Date and Time: <INPUT TYPE="datetime-local" NAME="date" Value="" id="input2"><br />
Event Location: <INPUT TYPE="text" NAME="location" VALUE="" id="input3"><br />
Event Notes: <INPUT TYPE="text" NAME="notes" VALUE="" id="input4"><br />
<button onclick="storeValues(event)" type=submit>Submit</button>
</form>
<script>
function storeValues(e) {
e.preventDefault();
let storedEvents = JSON.parse(localStorage.getItem("Events")) || [];
const newEventDetails = {
name: document.getElementById('input1').value,
dateTime: document.getElementById('input2').value,
location: document.getElementById('input3').value,
notes: document.getElementById('input4').value
}
storedEvents.push(newEventDetails);
localStorage.setItem("Events", JSON.stringify(storedEvents));
console.log('storedEvents', storedEvents);
}
</script>
And Currently, I am able to display them like this however it only displays 1 at a time and if I add this code in again but trying to display something else in the array it won't display either.
<h2>All Upcoming Events</h2>
<h2 id='input1'> </h2>
<h2 id='input2'> </h2>
<h2 id='input3'> </h2>
<h2 id='input4'> </h2>
<!-- running script here will populate H2's with values from local storage -->
<script>
const renderEvent = (event) => {
document.getElementById('input1').textContent = event.name;
document.getElementById('input2').textContent = event.dateTime;
document.getElementById('input3').textContent = event.location;
document.getElementById('input4').textContent = event.notes;
};
const index = 0; // for example: display 5th event saved
const storedEvents = JSON.parse(localStorage.getItem("Events"));
if (!storedEvents) throw new Error('No events');
const event = storedEvents[index];
renderEvent(event);
</script>
Use a <ul> element, and add items to it in a loop.
<h2>All upcoming events</h2>
<ul id="events"></ul>
const storedEvents = JSON.parse(localStorage.getItem("Events"));
var html = '';
storedEvents.forEach({name, dateTime, location, notes}) => {
html += `<li><h3>${name}</h3><h3>${dateTime}</h3><h3>${location}</h3><h3>${notes}</h3></li>';
});
document.getElementById("events").innerHTML = html;

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

How to implement paging in "with" bindings in knockout

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/

Categories