Hard to describe. Here is a snippet.
(function() {
var PhoneNumber = function() {
this.name = ko.observable();
this.phone = ko.observable();
};
$('[data-mask="phone"]').inputmask({
mask: '(999)999-9999'
});
var vm = {
newNumber: ko.observable(new PhoneNumber()),
numbers: ko.observableArray([]),
};
console.log('vm', vm.numbers());
vm.numbersJson = ko.computed(function() {
return ko.toJSON(vm.numbers);
});
vm.newNumberJson = ko.computed(function() {
return ko.toJSON(vm.newNumber);
});
ko.applyBindings(vm);
//events
function addNewNumber() {
vm.numbers.push(vm.newNumber());
vm.newNumber(new PhoneNumber());
}
function removeNumber() {
var item = ko.dataFor(this);
var remVal = _.reject(vm.Numbers(), function(el) {
return el.name === item.name;
});
vm.Numbers(remVal);
};
$(document).on('click', '#btnAdd', addNewNumber);
$(document).on('click', '.btnRemove', removeNumber);
})();
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet" />
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://rawgit.com/RobinHerbots/jquery.inputmask/3.x/dist/jquery.inputmask.bundle.js"></script>
<table class="table table-condensed table-hover table-striped table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Phone</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach:numbers">
<tr>
<td><span data-bind="text:name"></span>
</td>
<td><span data-bind="text:phone"></span>
</td>
<td class="form-group">
<button class="btn btn-sm btnRemove btn-default" type="button"> <i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
<tfoot data-bind="with:newNumber">
<tr>
<td>
<input type="text" class="input-sm form-control" data-bind="textInput:name" />
</td>
<td>
<input type="text" class="input-sm form-control" data-mask="phone" data-bind="textInput:phone" />
</td>
<td>
<button class="btn btn-sm btn-default" id="btnAdd" type="button"><i class="fa fa-plus"></i>
</button>
</td>
</tr>
</tfoot>
</table>
<h2>Values</h2>
<p>Numbers: <span data-bind="text:numbersJson"></span>
</p>
<p>New Number:
<span data-bind="text:newNumberJson"></span>
</p>
Please note there is phone inputmask in place when DOM is ready. When we type in the Phone textbox, the input mask is effective.
But after the first record added into the array and the textbox is cleared, the input mask is lost.
Question: how can we keep the input mask on the textbox?
The easiest thing to do is to put mask-setting code in the addNewNumberroutine.
function setMask() {
$('[data-mask="phone"]').inputmask({
mask: '(999)999-9999'
});
}
vm.addNewNumber = function () {
vm.numbers.push(vm.newNumber());
vm.newNumber(new PhoneNumber());
setTimeout(setMask, 0);
};
I made a Fiddle. It would probably be better to do with a custom binding, but, as I said, this is easiest.
Now I've made a custom binding handler in another Fiddle. It saves on searching for elements to mask, because it knows what element it is on.
ko.bindingHandlers.phoneTextInput = {
init: function (element, valueAccessor, allBindings, data, context) {
ko.bindingHandlers.textInput.init(element, valueAccessor, allBindings, data, context);
$(element).inputmask({
mask: '(999)999-9999'
});
}
}
Related
I have 3 columns, one column total_column_price uses to display the calculation result of amount and device_price. How to achieve that?
The Table
{{ Form::open(['action' => 'TransactionsINController#store', 'method' => 'POST']) }}
<table class="table table-hover table-bordered">
<thead align="center">
<tr>
<th>Amount</th>
<th>Device Price</th>
<th><a href="#" class="btn btn-primary btn-sm addRow">
<i class="fa fa-plus"></i>
</a>
</th>
<th>Column Total Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="form-group">
{{ Form::number('amount[]', 'value', ['name' => 'amount[]']) }}
</div>
</td>
<td>
<div class="form-group">
{{ Form::number('device_price[]', 'value', ['name' => 'device_price[]']) }}
</div>
</td>
<td align="center">
<a href="#" class="btn btn-danger btn-sm remove">
<i class="fa fa-times"></i>
</a>
</td>
<td>
{{ Form::text('total_column_price', '') }}
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Total: </td>
<td style="border: none"><b class="total_price"></b></td>
<td style="border: none"></td>
</tr>
</tfoot>
</table>
{{ Form::button('<i class="far fa-save"></i> Submit', ['type' => 'submit', 'class' => 'btn btn-info'] ) }}
{{ Form::close() }}
This is the calculation I try to use. the purpose is when I make input in amount and device_price column the result will automatically appear in total_column_price.
The Script to Calculate
<script type="text/javascript">
$('tbody').delegate('.amount,.device_price','keyup',function(){
var tr=$(this).parent().parent();
var amount=tr.find('.amount').val();
var device_price=tr.find('.device_price').val();
var total_column_price=(amount*device_price);
tr.find(.total_column_price).val(total_column_price);
total_price();
});
function total_price(){
var total_price=0;
$('.total_column_price').each(function(i,e){
var total_column_price=$(this).val()-0;
total_price +=total_column_price;
});
$('.total_price').html(total_price+",00");
}
</script>
Everything seems to be good in your code the problem is way you are using the JQuery find function, you need to add quotes while entering the selector.
tr.find('.total_column_price').val(total_column_price);
Use the new event handle .on()
The .on() syntax is the new syntax that version 1.7 uses and it is meant to substitute .bind(), .delegate() and .live().
Another problem in your javascript was related to fetching the parent from on keyup in input box.
It should be like this
var tr = $(this).parent().parent().parent();
Try the code below:
$(function() {
$(document).on('keyup', 'input[name="amount[]"],input[name="device_price[]"]', function() {
var tr = $(this).parent().parent().parent();
var amount = tr.find('input[name="amount[]"]').val();
var device_price = tr.find('input[name="device_price[]"]').val();
var total_column_price = (amount * device_price);
total_price();
});
function total_price() {
var total_price = 0;
$('input[name="total_column_price[]"]').each(function(i, e) {
var total_column_price = $(this).val() - 0;
total_price += total_column_price;
});
$('.total_price').html(total_price + ",00");
}
})
I'm fairly new to Knockout and JavaScript in general, I am trying to figure out how to get this working I am trying to create a simple shopping list application using knockout.js I have it currently where it's adding the Item Name and quantity to the table however it's adding them both as separate rows instead of row and column.
HTML Table issue
var SimpleListModel = function(items) {
self = this;
self.items = ko.observableArray(items);
self.itemToAdd = ko.observable("");
self.quantityToAdd = ko.observable("");
self.deleteItem = function(item) {
self.items.remove(item);
return self.items;
}
self.addItem = function() {
if (self.itemToAdd() != "") {
self.items.push(self.itemToAdd());
self.itemToAdd("");
}
if (self.quantityToAdd() != "") {
self.items.push(self.quantityToAdd());
self.quantityToAdd("");
}
}.bind(this);
};
ko.applyBindings(new SimpleListModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<form data-bind="submit: addItem">
New item:<input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' /> Quantity:
<input data-bind='value: quantityToAdd, valueUpdate: "afterkeydown"' />
<button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
</form>
<p></p>
<table border="1">
<thead>
<th>Item</th>
<th>Quantity</th>
<th>Remove</th>
</thead>
<tbody data-bind="foreach:items">
<tr>
<td data-bind="text: $data"></td>
<td><input type="button" data-bind="click:$root.deleteItem" value="X"></input>
</td>
</tr>
</tbody>
</table>
the expected result needs to be:
Expected result image
That's because you're adding the item and the quantity to the items array as separate items. You'll want to use an object instead:
var SimpleListModel = function(items) {
self = this;
self.items = ko.observableArray(items);
self.itemToAdd = ko.observable("");
self.quantityToAdd = ko.observable("");
self.deleteItem = function(item) {
self.items.remove(item);
return self.items;
}
self.addItem = function() {
if (self.itemToAdd() && self.quantityToAdd()) {
self.items.push({ item: self.itemToAdd(), quantity: self.quantityToAdd() });
self.itemToAdd("");
self.quantityToAdd("");
}
};
};
ko.applyBindings(new SimpleListModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<form data-bind="submit: addItem">
New item:<input data-bind='value: itemToAdd, valueUpdate: "afterkeydown"' /> Quantity:
<input data-bind='value: quantityToAdd, valueUpdate: "afterkeydown"' />
<button type="submit" data-bind="enable: itemToAdd().length > 0">Add</button>
</form>
<p></p>
<table border="1">
<thead>
<th>Item</th>
<th>Quantity</th>
<th>Remove</th>
</thead>
<tbody data-bind="foreach:items">
<tr>
<td data-bind="text: item"></td>
<td data-bind="text: quantity"></td>
<td><input type="button" data-bind="click:$root.deleteItem" value="X"></input>
</td>
</tr>
</tbody>
</table>
ok so i have my view model
function viewModel(calendarData) {
var self = this;
self.Calendars = ko.mapping.fromJS(calendarData);
self.ViewedCalendar = {};//what can/should i set this to be on initial creation?
self.DisplayCalendar = function (calendar) {
self.ViewedCalendar = calendar;
};
};
I then have my html:
<div data-bind="visible: Calendars().length > 0">
<h2>You have <span data-bind="text: Calendars().length"></span> calendars</h2>
<table class="table">
<thead>
<tr>
<th>Calendars</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: Calendars">
<tr>
<td data-bind="text: Name"></td>
<td><span data-bind="text: Events().length"></span> events</td>
<td><a href='#' data-bind='click: $root.DisplayCalendar'>View</a></td>
</tr>
</tbody>
</table>
</div>
<div data-bind="visible: ViewedCalendar() !== null">
Your are viewing <span data-bind="text: ViewedCalendar.Name"></span><br />
</div>
What I am trying to achieve is when the user clicks View for a given calendar, that via DisplayCalendar() I set the property ViewedCalendar to be set to the given calendar.
This then shows my div that contains the label stating what calendar is being viewed.
This is all rough code at the minute just to get the basic functionality in place but I'm new to knockout so could use some help.
I'm getting TypeError: ViewedCalendar is not a function or ViewedCalendar is undefined.
Any help would be much appreciated.
The ViewedCalendar property needs to be an observable for knockout to reach to changes in it's value. You defined it like this:
self.ViewedCalendar = {};
Which is an (empty) object literal and not a function (as the error message correctly stated). What you need is:
self.ViewedCalendar = ko.observable(); // empty () give you an empty observable - no calendar selected yet
And then you can update it in your click handler with:
self.ViewedCalendar(calendar);
Here's a working full example:
function viewModel(calendarData) {
var self = this;
self.Calendars = ko.mapping.fromJS(calendarData);
self.ViewedCalendar = ko.observable();
self.DisplayCalendar = function (calendar) {
self.ViewedCalendar(calendar);
};
};
ko.applyBindings(new viewModel([{Name:'some calendar', Events: []}, {Name:'another calendar', Events: []}]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<div data-bind="visible: Calendars().length > 0">
<h2>You have <span data-bind="text: Calendars().length"></span> calendars</h2>
<table class="table">
<thead>
<tr>
<th>Calendars</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: Calendars">
<tr>
<td data-bind="text: Name"></td>
<td><span data-bind="text: Events().length"></span> events</td>
<td><a href='#' data-bind='click: $root.DisplayCalendar'>View</a></td>
</tr>
</tbody>
</table>
</div>
<div data-bind="with: ViewedCalendar">
Your are viewing <span style="font-weight: bold" data-bind="text: Name"></span><br />
</div>
Uncaught ReferenceError: Unable to process binding "foreach: function (){return Educations }"
Uncaught ReferenceError: Unable to process binding "foreach: function (){return WorkExperience }"
I couldn't figure out why the binding is failing.
i have the following two tables one for Education and other for Work Experience, They give the error when i'm trying to bind the both table in one view, If i remove the binding (JS + HTML code) it works fine
HTML:
<div id=divtable1 class="widget widget-simple widget-table">
<table id="Table1" class="table table-striped table-content table-condensed boo-table table-hover">
<thead>
<tr id="Tr1" class="filter">
<th>University<span class="required">*</span></th>
<th>Location <span class="required">*</span></th>
<th></th>
</tr>
</thead>
<tbody data-bind='foreach: Educations'>
<tr>
<td><input type="text" class='span11 required' data-bind="value: SchoolName" /></td>
<td><input type="text" class='span11 required' data-bind="value: Location" /></td>
<td><a href='#' data-bind='click: $root.removeEducation'>Delete</a></td>
</tr>
</tbody>
</table>
<button data-bind='click: $root.addEducation' class="btn btn-blue">Add Education</button>
</div>
<div id="divtable2">
<table id="Table2">
<thead>
<tr id="Tr2" class="filter">
<th>Employer Name<span class="required">*</span></th>
<th>EmployerAddress <span class="required">*</span></th>
<th></th>
</tr>
</thead>
<tbody data-bind='foreach: WorkExperience'>
<tr>
<td><input type="text" class='span11 required' data-bind="value: EmployerName" /></td>
<td><input type="text" class='span11 required' data-bind="value: EmployerAddress" /></td>
<td><a href='#' data-bind='click: $root.removeWorkExperience'>Delete</a></td>
</tr>
</tbody>
</table>
<button data-bind='click: $root.addWorkExperience' class="btn btn-blue">Add Work Experience</button>
</div>
Java Script:
<script type="text/javascript">
var Educations = function (educations) {
var self = this;
self.Educations = ko.mapping.fromJS(educations);
self.addEducation = function () {
self.Educations.push({"SchoolName": ko.observable(""), "Location": ko.observable("")});
};
self.removeEducation = function (education) {
self.Educations.remove(education);
};
};
var viewModel = new Educations(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Educations)));
ko.applyBindings(viewModel);
</script>
<script type="text/javascript">
var WorkExperience = function (workexperiences) {
var self = this;
self.WorkExperience = ko.mapping.fromJS(workexperiences);
self.addWorkExperience = function () {
self.WorkExperience.push({ "EmployerName": ko.observable(""), "EmployerAddress": ko.observable("")});
};
self.removeWorkExperience = function (workexperience) {
self.WorkExperience.remove(workexperience);
};
};
var viewModel = new WorkExperience(#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.WorkExperience)));
ko.applyBindings(viewModel);
</script>
Also I tried to bind Table1 but it didn't work
ko.applyBindings(viewModel, $('#Table1')[0]);
try adding this <pre data-bind="text: ko.toJSON($data, null, 2)"></pre> to your view. it will output the data that knockout contains in the current context.
Also you have one view and two view models that are trying to bind to it. create one viewmodel with both Educations and WorkExperience as properties.
something like
var vm = {
Educations : educationViewModel,
WorkExperience: workExperienceViewModel
}
ko.applyBindings(vm);
If you want to bind two view-models separately, you must define which section of your view to bind to. You do this by providing the element to ko.applyBindings as the second parameter.
ko.applyBindings(viewModel, document.getElementById("divtable1"));
I want to have multiple data-bindings on my view so my text box contains the right value and when the value changes it calls a function. Basically I want to use amplify.js local storage every time a value on my form changes.
Agency view
<section class="view">
<header>
<button class="btn btn-info btn-force-refresh pull-right"
data-bind="click: refresh">
<i class="icon-refresh"></i>Refresh</button>
<button class="btn btn-info"
data-bind="click: save">
<i class="icon-save"></i>Save</button>
<h3 class="page-title" data-bind="text: title"></h3>
<div class="article-counter">
<address data-bind="text: agency().length"></address>
<address>found</address>
</div>
</header>
<table>
<thead>
<tr>
<th>Agency Name</th>
<th>Category</th>
<th>URL</th>
<th>Number of employees</th>
</tr>
</thead>
<tbody data-bind="foreach: agency">
<tr>
<td>
<!--<input data-bind="value: agencyName" /></td>-->
<input data-bind="value: agencyName, onchange: test()"/>
<td>
<input data-bind="value: category" /></td>
<td>
<input data-bind="value: Url" /></td>
<td>
<input data-bind="value:numberOfEmployees" /></td>
</tr>
<tr>
<td>Activities</td>
<td>Declared Billings</td>
<td>Campaigned Billings</td>
</tr>
<tr>
<td>
<input data-bind="value: activities" /></td>
<td>
<input data-bind="value: declaredBillings" /></td>
<td>
<input data-bind="value: campaignBillings" /></td>
</tr>
</tbody>
</table>
</section>
Agency ViewModel
define(['services/datacontext'], function (dataContext) {
//var myStoredValue = amplify.store("Agency"),
// myStoredValue2 = amplify.store("storeExample2"),
// myStoredValues = amplify.store();
var agency = ko.observableArray([]);
var initialized = false;
var save = function (agency) {
return dataContext.saveChanges(agency);
};
var vm = { // This is my view model, my functions are bound to it.
//These are wired up to my agency view
activate: activate,
agency: agency,
title: 'agency',
refresh: refresh, // call refresh function which calls get Agencies
save: save
};
return vm;
function activate() {
if (initialized) {
return;
}
initialized = true;
return refresh();
}
function refresh() {
return dataContext.getAgency(agency);
}
function test() {
alert("test");
}
});
Every time I type a new value, for example
<input data-bind="value: agencyName, onchange: test()"/>
I want to fire the function test. I then want to store the view model latest data into local storage.
Does anyone know how to do multiple bindings for this?
You should use this binding:
<input data-bind="value: agencyName, event: { change: $parent.onAgencyNameChanged}"/>
Note that I used $parent to refer to the vm Object.
And add an handler to your viewModel.
var vm = {
....
onAgencyNameChanged: function(agency){
// do stuff
}
};
return vm;
Another solution could be to subscribe on the agencyName of all agencies. But I think this isn't suited to this case. After creating the vm you could do this :
ko.utils.arrayForEach(vm.agency(), function(a){
a.agencyName.subscribe(function(){
// do stuff
});
});
I hope it helps.
Try to subscribe your object manually for each element that you have to bind.
Have a look at the explanation and the example in the knockout documentation here.