Ok I'm messing around with code to help me understand javascript better and Ive been playing with backbone. I may be missing it but my validation here isnt working right. I either get a "you must fill in both fields or it just submits even if a field is blank.
The function of addModel is to add a row to my parent view and save it all in one function which is causing me issues
Form = Backbone.View.extend({ //form view
el: '.item-form',
initialize: function(){
},
events: {
'click #additem': 'addModel' // clicking on the add item button launches the addmodel function
},
addModel: function(){ // when the addmodel function fires it takes
var item = new Item({
"itemName": this.$("#item").val(),//value of form input placed into row template cell
"price": this.$("#price").val()});
// simple validation before adding to collection
if(!_.isEmpty("#item") && !_.isEmpty("#price")){
var items = new Items();
items.add(item);
$("#message").html("Please wait; the task is being added.");
item.save(null, {success:
//use the mongo id as the Item models id
function (item, response,options) {
item.id= item.attributes._id.$id;
item.attributes.id = item.attributes._id.$id;
new ItemsView({collection: items});
$("#message").html("");
}
});
this.$("#item").val('');//all this does is empty the field
this.$("#price").val('');//all this does is empty the field
} else {
alert('Please fill in both fields');
}
}
});
heres the html for reference
<body>
<table class="itemTable">
<thead>
<tr>
<th>Item</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody class="tableBody">
<script type="text/template" id="table-row">
<td><%= itemName %></td> <td><%= price %></td> <td> <button class="complete">Complete</button> <button class="remove">Remove</button></td>
</script>
</tbody>
</table>
<form class="item-form">
<input type="text" name="item" id="item" placeholder="Item"/> <!-- goes to itemName in the template for the body -->
<input type="text" name="price" id="price" placeholder="Price" /><!--goes to price in the template for the body -->
<button type="button" id="additem">Add</button>
</form>
<div id="message"></div
The validation should compare the values of the inputs not simply the id's.
if(!_.isEmpty(this.$("#item").val()) && !_.isEmpty(this.$("#price").val())){
Related
I've tried several ways of doing this with no success. Would love some advice!
Goal: I have a table where each row is an order, but where within that row, if changes need to be made, a div appears underneath (in red). This needs to show/hide when a button on that row is clicked/toggled (Button is: Make Changes)
Issue: I have all the buttons working apart from the make changes toggle. Tried the visible observable, but the closest I could get was toggling the div's visibility for the whole table, not per row.
//Class to represent a row in the table
function orderDetail(order, orderChange) {
var self = this;
self.order = ko.observable(order);
self.orderChange = ko.observable(orderChange);
}
//Overall viewmodel, plus initial state
function FoodViewModel() {
var self = this;
self.foodTypes = [
{ foodType: "Please Select"},
{ foodType: "Veg"},
{ foodType: "Meat"}
];
self.orders = ko.observableArray([
new orderDetail(self.foodTypes[0], self.foodTypes[0])
]);
// Add and remove rows
self.addOrder = function() {
self.orders.push(new orderDetail(self.foodTypes[0], self.foodTypes[0]));
}
self.removeOrder = function(order) { self.orders.remove(order) }
}
ko.applyBindings(new FoodViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th>Orders</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: orders">
<tr>
<td>
<div><select data-bind="options: $root.foodTypes, value: order, optionsText: 'foodType'" id="foodList"></select></div>
<div><select data-bind="options: $root.foodTypes, optionsText: 'foodType', value: orderChange" id="foodListChange" style="color: red;"></select></div>
</td>
<td>
<button class="button button2" >Make Changes</button>
</td>
<td>
<button class="button button1" href="#" data-bind="click: $root.removeOrder">Remove</button>
</td>
</tr>
</tbody>
</table>
<button data-bind="click: addOrder" class="button">Add Order</button>
Thanks in advance!
If you want that the user interface reacts to something in Knockout, make an observable.
In this case you want to display part of the UI conditionally (apparently to toggle an edit mode), so let's create:
an observable editMode that is either true or false, to store the UI state
a function toggleEditMode that toggles between the two states, to bind it to the button
an if: editMode and an ifnot: editMode binding, to show different parts of the UI accordingly
function OrderDetail(params) {
var self = this;
params = params || {};
self.order = ko.observable(params.order);
self.orderChange = ko.observable(params.orderChange);
self.editMode = ko.observable(true);
self.buttonCaption = ko.pureComputed(function () {
return self.editMode() ? "Done" : "Edit";
});
self.toggleEditMode = function () {
self.editMode(!self.editMode());
}
}
function OrderList(params) {
var self = this;
params = params || {};
self.foodTypes = ko.observableArray(params.foodTypes);
self.orders = ko.observableArray();
self.addOrder = function(foodType) {
self.orders.push(new OrderDetail());
}
self.removeOrder = function(order) {
self.orders.remove(order);
}
}
var vm = new OrderList({
foodTypes: [
{foodType: "Veg"},
{foodType: "Meat"}
]
});
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<thead>
<tr>
<th style="width: 150px;">Orders</th>
<th>Actions</th>
</tr>
</thead>
<tbody data-bind="foreach: orders">
<tr>
<td>
<div data-bind="ifnot: editMode">
<!-- ko with: order -->
<span data-bind="text: foodType"></span>
<!-- /ko -->
</div>
<div data-bind="if: editMode">
<select data-bind="
options: $root.foodTypes,
value: order,
optionsText: 'foodType',
optionsCaption: 'Please select…'
"></select>
</div>
</td>
<td>
<button class="button button2" data-bind="
click: toggleEditMode,
text: buttonCaption,
enable: order
"></button>
<button class="button button1" href="#" data-bind="
click: $root.removeOrder
">Remove</button>
</td>
</tr>
</tbody>
</table>
<button data-bind="click: addOrder" class="button">Add Order</button>
<hr>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
Notes
Don't make "Please Select" part of your food types. That's what the optionsCaption binding is for.
I've parameterized the viewmodels (see the params object). This will work better than hard-coding values or using long argument lists, especially if you want to use a mapping plugin later.
The "Done" button is disabled as long as no order is selected, via the enable: order binding, i.e. if the order property is empty, the enable binding will keep the button disabled.
The with: order binding serves a similar purpose. It will only display its contents when there actually is an order value to display. This will prevent rendering errors with incomplete OrderDetail instances.
As the title suggests, that is what im attempting to do, but are at a lost. My best attempt is the following:
This is the nodejs code that renders the HTML page and gets the user input from HTML
app.get('/search/species', (req,res) => {
diseaseModel.find({matched: current_matched}, function (err,disease){
res.render('species', {
diseaseList: disease,
});
});
})
app.post('/search/species', async(req,res) => {
var desc = req.body.desc;
await diseaseModel.updateMany({matched: current_matched, "$text":{"$search" : `\"${desc}\"`}}, {$inc: {matched: 1}});
current_matched ++;
res.redirect(302, '/search/species');
}
}
});
})
This is the HTML page that I uses to collect user input and return the filtered options:
<form action = "/search/species" method = "POST" id="submitMessage" class="field has-addons">
<input class="form-control input is-primary" name = "desc" id = "desc" type="text" style="width: 100%;" placeholder="Enter a Description" required/>
<button type="submit" class="button is-primary" id="send">Send</button>
</form>
</div>
</div>
</section>
<table class = "content-table" align="center" style="margin: 0px auto;">
<thead>
<tr>
<th>Name</th>
<th>Species</th>
<th>Vector</th>
<th>Agent</th>
</tr>
</thead>
<tbody>
<%diseaseList.forEach(disease =>{%>
<tr data-href= 'http://localhost:8080/info?id=<%=disease.id%>'>
<th><%=disease.diseaseName%></th>
<th><%=disease.species%></th>
<th><%=disease.vector%></th>
<th><%=disease.agent%></th>
</tr>
<% })%>
</tbody>
</table>
</div>
</section>
<script>
$(document).ready(function() {
$(document.body).on("click", "tr[data-href]", function() {
window.location.href = this.dataset.href;
})
});
But this doesn't refresh in real time, as in when i type a character, it doesn't start filtering. Would I be able to do this via AJAX? Also how would I list MongoDB data out in the table in alphabetical order?
So I am new to AngularJS and I am looking to create good code because my application is going to scale.
Now, I have a list of competences that I'll get from my API, and then I need to make a list of them with a checkbox, so the user will select/deselect from the list, and then submit that form.
So how can I achieve that? What should be in the ng-model of every checkbox? Should I create a form with all the values in set in false?
Here's my controller code now:
function Controller(Competence) {
var vm = this;
vm.competences = [];
function initController() {
Competence.getAll()
.then(function(data) {
vm.competences = data;
});
}
initController();
}
And here's my view code:
<table>
<thead>
<tr>
<th width="90%">Competence</th>
<th width="10%">Select</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="competence in vm.competences">
<td>{{ competence.name }}</td>
<td><input type="checkbox"></td>
</tr>
</tbody>
</table>
So far your code looks good. To set checkbox values, just add ng-model="competence.selected" to each checkbox input element like this:
<input type="checkbox" ng-model="competence.selected">
Now when you select the checkbox it will set competence.selected to true, and when you deselect it the value will be false.
Form Submission
Wrap your table in a <form> with an ng-submit attribute, and create a function to submit the form:
<form ng-controller="MyCtrl as vm" ng-submit="vm.submitForm()">
<!-- your table here ... -->
<input type="submit" value="Submit">
</form>
In your controller:
vm.submitForm = function(){
vm.competences.forEach(function(competence){
if(competence.selected) console.log(competence);
// or POST the competences using the $http service ...
})
}
See JSFiddle also: Checkbox Demo
trying to figure out how to set up the radio button bindings in a table in knockout. I would like when the radio button on the table is selected. the entire selected record is available in the model. not quite sure how to set up the binding on the radio input. I assume I need to use $parent and a function for the value binding?
here is the fiddle. (the selected record does not do anything right now I would like it to be populated when the radio button is selected)
https://jsfiddle.net/othkss9s/5/
HTML
<table>
<thead>
<tr>
<th>Select</th>
<th>First</th>
<th>Last</th>
<th>Dept</th>
</tr>
</thead>
<tbody data-bind='foreach: employees'>
<tr>
<td><input type="radio" name="employees"></td>
<td data-bind='text: first'></td>
<td data-bind='text: last'></td>
<td data-bind='text: dept'></td>
</tr>
</tbody>
</table>
<div data-bind="with: selectedRow">
<h2>
Selected Record
</h2>
<p>
First: <span data-bind="first" ></span>
</p>
<p>
Last: <span data-bind="last" ></span>
</p>
<p>
Dept: <span data-bind="dept" ></span>
</p>
</div>
JS
function employee(first, last, dept) {
this.first = ko.observable(first);
this.last = ko.observable(last);
this.dept = ko.observable(dept);
}
function model() {
var self = this;
this.employees = ko.observableArray("");
this.selectedRow = ko.observable({});
};
var myViewModel = new model();
$(document).ready(function() {
ko.applyBindings(myViewModel);
myViewModel.employees.push(
new employee("Bob","Jones", "Hr")
);
myViewModel.employees.push(
new employee("Sarah","Smith", "It")
);
myViewModel.employees.push(
new employee("John","Miller", "It")
);
});
You have to perform two steps to get your code working.
First, apply bindings to radio buttons:
<input type="radio" data-bind="checked: $root.selectedRow, checkedValue: $data" />
checkedValue should contain actual value that corresponds the current radio button. In this case we refer $data variable that is whole employee object rather than simple (scalar) value.
checked binding should refer to an observable containing currently selected employee.
Second, correct the line where the selectedRow property is defined:
this.selectedRow = ko.observable();
Do not pass empty object as default value. Otherwise with binding won't work correctly.
By fixing the syntax of bindings in your second block that displays selected employee you will get something like this.
I have got a assignment to implement knockout js to my application. I have a table like
<table>
<tr>
<th>
Name
</th>
<th>
Category
</th>
<th>
Price
</th>
<th></th>
</tr>
<tr>
<td>
Iphone
</td>
<td>
SmartPhone
</td>
<td>
50000
</td>
</tr>
</table>
There are three textbox for creation of this field.
<div id="create">
<input data-bind="value: Name" id="name"/>
<input data-bind="value: Category" id="category"/>
<input data-bind="value: Prize" id="prize"/>
</div>
When I am typing on this textboxes i want to show this on the table as a new tr.. How can I do this? DEMO
Reference Link
What you want to do is define a viewmodel that contains the data for an individual item, and another viewmodel that contains the rest of the interactions (list of items, how to add new ones, etc).
var Item = function (Name, Category, Price) {
var self = this;
self.Name = ko.observable(Name);
self.Category = ko.observable(Category);
self.Price = ko.observable(Price);
}
var ViewModel = function () {
var self = this;
self.ItemToAdd = ko.observable(new Item());
self.Items = ko.observableArray([]);
self.addItem = function (item) {
self.Items.push(item);
self.ItemToAdd(new Item());
}
};
var vm = new ViewModel();
vm.addItem(new Item('Iphone', 'SmartPhone', 50000));
ko.applyBindings(vm);
In your html, your table body will look like this:
<tbody data-bind="foreach: Items">
<tr>
<td data-bind="text: Name"/>
<td data-bind="text: Category"/>
<td data-bind="text: Price"/>
</tr>
</tbody>
what this does is loops through each item in the Itemlist and creates a <tr> for each one and binds the values of the Item object in the observableArray to the <td> elements.
to add new items to the table in your markup:
<div data-bind="with: ItemToAdd">
<input data-bind="value: Name" id="name"/>
<input data-bind="value: Category" id="category"/>
<input data-bind="value: Price" id="price"/>
<button data-bind="click: $parent.addItem">Add</button>
</div>
this sets the context of the div element to a new Item object, and when you click the Add button, it calls the parent context's (ViewModel) addItem function, and automatically passes the context item for the div element (ItemToAdd). Then its just a matter of pushing it on to the observableArray and the table will update with the new item.
Updated Fiddle: http://jsfiddle.net/BJQgw/4/
if this was a for-real application, you would perform some sort of validation prior to adding the item to the list (preferably using knockout-validation)