Get element by name using knockout - javascript

I am creating input elements like below:
<input type="text" class="form-control" data-bind="value: attr: { name: Data[' + $index() + ']'}" />
I have another button which creates multiple inputs of above with its click.
And I also have my main button click as:
<input type="button" value="Check Data" class="btn btn-primary" data-bind='click: verify.bind($data, event)' />
In my knockout I have:
self.verify= function (data, event) {
//here I want the data that is entered in each of the inputs.
}
On the above button click I want to get the value of all the inputs. In JS I could have done elements by name and it would give me that element. But how can I get that to work here.
Updated code:
I have this in my HTML:
<div data-bind="foreach: { data: myData }">
<div class="form">
<div class="myClass">
<input type="text" class="form-control" data-bind="value: $data.textbox, attr: { name: 'MyData[' + $index() + '].Textbox'}" />
</div>
<div class="myClass">
<input type="button" value="Add More" class="btn btn-primary" data-bind="click: $parent.add"/>
</div>
</div>
</div>
When the user clicks Add More, it adds on more it adds one more text box.
Then at last I have a button as:
<div class="form">
<input type="button" value="Check Data" class="btn btn-primary" data-bind='click: checkData' />
</div>
When the user clicks on Check Data button I just need to some validation on the all the data entered in the textbox. The validation needs to be done on client side.
In my knockout I have:
this.add = ko.observableArray();
this.add = function () {
self.myData.push(
{
textbox: ""
});
};
this.checkData = function (){
//Here I want to get whats entered in all the textboxes
}

It's exceedingly likely that your entire approach is wrong.
Your HTML input elements don't need names.
Your viewmodel methods do not need to know anything about the HTML elements that display their values.
You do not need to bind your event handlers with special parameters.
The view (the HTML page) is used as a tool for modifying the viewmodel. All data you need for verification is in the viewmodel, given that you have made everything that the user can change an observable.
function Test() {
var self = this;
self.users = ko.observableArray([
{ name: ko.observable("John Doe") },
{ name: ko.observable("Jane Doe") }
]);
self.verify = function () {
var usernames = self.users().map(function (u) { return ko.unwrap(u.name) });
alert('You have entered these users\n\n' + usernames.join('\n'));
};
}
var vm = new Test();
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="foreach: users">
<input type="text" class="form-control" data-bind="value: name" /><br>
</div>
<button class="btn btn-primary" data-bind='click: verify'>Check Data</button>

Related

Get value of input field by class name, wherer class name is varible

I have many pair of text fields and submit button, with id to submit button and class to text field as same but different for each pair. So I want to pass the value entered in text field after button click to ajax function.
function update_rate(id){
// var price = document.getElementsByClassName(id)[0].innerHTML;
var price = document.getElementsByClassName(id);
console.log(price);
$.ajax({
type: "POST",
url: "update_rate.php", // Name of the php files
data: {subcategory : id , price: price},
success: function(res)
{
// console.log(html);
alert(res);
}
});
}
first pair:
<div class="form-group">
<input type="text" class="form-control" name="a" placeholder="Your task rate excl. taxes">
</div>
<button type="submit" id="a" onclick="update_rate(this.id)" class="btn btn-primary">Update</button>
<div class="form-group">
<input type="text" class="form-control" name="b" placeholder="Your task rate excl. taxes">
</div>
<button type="submit" id="b" onclick="update_rate(this.id)" class="btn btn-primary">Update</button>
But I can't get the value of text field into variable.
This can and should be done in a different and more simple manner. Connecting the buttons with their text fields by id and class is not a scalable solution and requires constant updating of the code to keep all the naming in sync. Just get the value of the text field that comes just prior to the button. This will be easier if you modify your HTML structure so that the text field and the button are in the same div together.
Don't use inline event handlers, instead separate your event handling
code into JavaScript.
Set up just a single event handler at a higher DOM element and handle
it when it bubbles up to that element. This is called Event
Delegation.
Use data-* attributes to store custom data in elements.
Also, don't use .getElementsByClassName() in 2020. Instead,
use .querySelector.
See comments below.
// Do your event handling in JavaScript, not with inline HTML event handling attributes
// Also, set up just one handler at a parent level of the items that might trigger
// the event (event delegation).
$(".wrapper").on("click", update_rate);
function update_rate(event){
// See if it was a submit button that got clicked
if(event.target.classList.contains("btn")){
// A submit button was pressed.
// Locate the nearest ancestor element that has the form-group class
// (event.target references the actual element that triggered the event).
let formGroup = event.target.closest(".form-group");
// and then, from there, find the first input (which is the one you want).
var input = formGroup.querySelector("input");
// The following code is already added to the success handler below and
// that's where it should be. It's only added here to be able to see the
// effect since the AJAX call won't run in Stack Overflow. The next 3 lines
// should be removed when used for real.
input.classList.add("hidden");
formGroup.querySelector("button").classList.add("hidden");
formGroup.querySelector("span").classList.remove("hidden");
console.log(input.value);
$.ajax({
type: "POST",
url: "update_rate.php", // Name of the php files
// Use the dataset API to extract the custom attribute on the input
data: {subcategory : input.dataset.category , price: input.value},
success: function(res){
alert(res);
// Hide the input and the button and show the updated message
input.classList.add("hidden");
formGroup.querySelector("button").classList.add("hidden");
formGroup.querySelector("span").classList.remove("hidden");
}
});
}
}
.hidden { display:none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="form-group">
<input type="text" class="form-control" data-category="a" placeholder="Your task rate excl. taxes">
<button type="submit" class="btn btn-primary">Update</button>
<span class="hidden">Updated</span>
</div>
<div class="form-group">
<input type="text" class="form-control" data-category="b" placeholder="Your task rate excl. taxes">
<button type="submit" class="btn btn-primary">Update</button>
<span class="hidden">Updated</span>
</div>
</div>
In the end, you have no id or unique class names to have to match up against each other, your HTML is more simplified, and you only have one event handler to set up.
Several things
var price = document.getElementsByClassName(id);
is plural and you need the value, so
var price = document.getElementsByClassName(id)[0].value; if you must
BUT it is not a class. It is a name
var price = document.getElementsName(id)[0].value; if you must
but if you have jQuery, why not use it?
Here I take the button ID and find the input by name
I also change to type="button" - you do not want to submit when you use Ajax
$(function() { // on page load
$("#container").on("click","[type=button]",function() { // click on type="button" you can use a Class here too
const id = $(this).attr("id");
const price = $("[name="+id+"]").val(); // get the input by name
console.log(id, price)
if (price) {
$.ajax({
type: "POST",
url: "update_rate.php", // Name of the php files
data: {
subcategory: id,
price: price
},
success: function(res) {
// console.log(html);
alert(res);
}
});
}
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
first pair:
<div class="form-group">
<input type="text" class="form-control" name="a" placeholder="Your task rate excl. taxes">
</div>
<button type="button" id="a" class="btn btn-primary">Update</button>
<div class="form-group">
<input type="text" class="form-control" name="b" placeholder="Your task rate excl. taxes">
</div>
<button type="button" id="b" class="btn btn-primary">Update</button>
</div>
As you're already passing the id in the function, using jQuery, you can do something like:
var selector = 'input[name="' + id + '"]' // Compose the selector string for jQuery
var value = $(selector).val() // Get input value

Use data of ng-repeat

I can display a table of users from my database on my web application using ng-repeat. I can add and delete directly from the web application but now I'm trying to update informations about those users. I would like to click on a button on the row of the user (each rows display informations for one user, 1 row = 1 user) when I clicked on this button I would like to make a form with input fields filled with actual values.
I can only get informations about my users by clicking on this button but I don't know how to "send" informations to this form.
My table of users :
<tr ng-repeat="user in users">
...
</tr>
But something like this is not working at all :
<form>
<label>Name</label>
<input type="text" id="up_name" ng-model="user.name"/>
<label>Age</label>
<input type="text" id="up_age" ng-model="user.age"/>
...
</form>
If you are using this synthax, your form have to be in your ngRepeat. It is not the best way to do it, as you will have a form for user.
I would suggest you something different. In your controller, set an edit() function:
$scope.edit = function(user) {
$scope.editedUser = user;
}
When clicking a user in your table, call the edit() function:
<tr ng-repeat="user in users" ng-click="edit(user)">
...
</tr>
You can now edit in the form the editedUser object:
<form ng-if="editedUser">
<label>Name</label>
<input type="text" id="up_name" ng-model="editedUser.name"/>
<label>Age</label>
<input type="text" id="up_age" ng-model="editedUser.age"/>
...
</form>
What you can do is the following :
<tr ng-repeat="user in users" ng-init="selectedUser = null">
<td> {{ user.name }}</td>... <td ng-click="selectedUser = user"> edit </td>
</tr>
<div ng-if="selectedUser">
<form>
<label>Name</label>
<input type="text" id="up_name" ng-model="user.name"/>
<label>Age</label>
<input type="text" id="up_age" ng-model="user.age"/>
...
</form>
</div>
I think that you are talking about a sort of master-detail ui pattern.
Here it is a public plunker that will solve that kind of problem
Insert the both input and span HTML directive in <td> and use ng-switch : ng-switch-when & ng-switch-default to display only one field.
<td class="sorting_1" ng-switch="mode">
<input type="text" class="form-control small" ng-switch-when="edit" id="edit" ng-model="edit.username">
<span ng-switch-default id="item.username">{{item.username}}</span>
</td>
You need to write a custom directive for it.Reason for writing custom directive is the value of ng-switch will associate with individual instead of global.
In the last <td> tag add : which will contain edit and update buttons:
<td ng-switch="mode">
<button class="btn btn-success btn-xs edit" ng-switch-when="edit" ng-
click="updateItem(edit, index)">
<i class="fa fa-floppy-o"></i>
</button>
<button class="btn btn-success btn-xs" ng-switch-default ng-
click="editItem(item)">
<i class="fa fa-pencil-square-o "></i>
</button>
</td>
JS
$scope.editItem = function(oldData) {
$scope.edit = angular.copy(oldData);
$scope.mode = "edit";
}
$scope.updateItem = function(data, index) {
$scope.$emit('update', data, index);
$scope.mode = "default";
}
The value of input-box will be updated using
$scope.edit = angular.copy(oldData); into editItem() function.
With the use of event emitters modify the main object.
$scope.$on('update', function(event, data, index) {
angular.copy(data, $scope.items[index]);
});
use angular.copy to deep clone value instead of passing value as a reference.
Check http://codepen.io/sumitridhal/pen/YVPQdW

Angularjs: Dynamic Input in a single array for row

I'm a newbie and I need some help for something you'll maybe find too easy.
I want to add with a single button different inputs at the same time.
I don't know how to manage.
This is what I'm doing, and I'm blocked.
I'll thank you your ideas.
So, my form shows each name and an input box for entering the age of that person (We don't know how many persons are in 'people').
And in the end, an 'submit' button that calls the showAll() function.
And then, I just want to show the content of the array in console.log
A Form like this:
Form with only 3 people
HTML:
<form name="myForm3">
<div class="form-group row" ng-repeat="person in people track by $index">
<label for="player" class="col-xs-3 col-form-label text-right">{{ person.name }}</label>
<div class="col-xs-2">
<input class="form-control" type="text" ng-model="person.age[$index]" required>
</div>
</div>
<button type="submit" class="btn btn-success btn-lg" ng-click="showAll()" >Show all</button>
</form>
controller.js:
$scope.person = [];
// POST
$scope.showAll = function() {
for (var i=0; i < person.length; i++){
console.log('person: ' + {{ $scope.person[i] }} );
}
}
you can hide the inputs by using ng-if or ng-hide and show them on showAll by setting that variable used by those to true or false:
<div class="col-xs-2" ng-if="person.show">
<input class="form-control" type="text" ng-model="person.age[$index]" required>
</div>
And in showAll:
$scope.showAll = function() {
for (var i=0; i < $scope.people.length; i++) {
$scope.people[i].show = true;
}
};

Bootstrap modal dialog text fields don't update after I programmatically change the values until the user types in the field

I'm having the following problem: I have a list of items, any of which a user can click on to edit. At this time, a bootstrap modal dialog shows with fields for each of the editable values. I'm updating the values for the fields from an underlying Knockout viewmodel, so when the user edits an item on the modal dialog, they can see the field being modified in the background. So, the modifying currently works fine. However, when the dialog first opens, it doesn't have the values from the item the user selected; instead, it has the values that were loaded previously. But, when the user starts to edit a field, it instantly updates to what the value is supposed to be, and allows the user to continue editing the correct field. Not sure what's going on here.
Here is my modal:
<script id="myModal" type="text/html">
<div class="modal-header">
<button type="button" class="close" data-bind="click: close" aria-hidden="true">×</button>
<h3 data-bind="html: header"></h3>
</div>
<div class="modal-body">
<div class="form-group">
<label>First Name</label>
<input type="text" data-bind="value: modal.firstName, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Last Name</label>
<input type="text" data-bind="value: modal.lastName, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Phone</label>
<input type="text" data-bind="value: modal.phone, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Email</label>
<input type="text" data-bind="value: modal.email, valueUpdate: 'afterkeydown'" class="form-control" />
</div>
</div>
<div class="modal-footer">
</div>
</script>
<!-- Create a modal via custom binding -->
<div data-bind="bootstrapModal: modal" data-keyboard="false" data-backdrop="static"></div>
Here is the part where the list is populated via knockout:
<ul data-bind="foreach: tasks">
<li>
<div style="border:double">
<div>
<label data-bind="text: firstName"></label>
</div>
<div>
<label data-bind="text: lastName"></label>
</div>
<div>
<label data-bind="text: phone"></label>
</div>
<div>
<label data-bind="text: email"></label>
</div>
<div>
<button data-bind="click: editI.bind(this)">Edit</button>
#*<button data-bind="click: $parent.removeUser">Delete</button>*#
</div>
</div>
</li>
</ul>
<form #*data-bind="submit: addUser"*#>
<button type="submit">Add User</button>
</form>
<button #*data-bind="click: save"*#>Save</button>
Here is where I set the modal values, which works, when the knockout viewmodel is loaded:
viewModel.modal = {
header: ko.observable("This is a modal"),
firstName: ko.observable("a"),
lastName: ko.observable("a"),
phone: ko.observable("a"),
email: ko.observable("a"),
body: ko.observable("test body"),
closeLabel: "Close",
primaryLabel: "Do Something",
show: ko.observable(false), /* Set to true to show initially */
onClose: function () {
viewModel.onModalClose();
},
onAction: function () {
viewModel.onModalAction();
}
Finally, here is the edit function that gets called when the modal is opened. This is where things go awry. Item elements are set to the modal viewmodel, but aren't shown in the modal until a user starts editing that item...then, bam, the item shows in the field.
self.editI = function (item) {
viewModel.modal.body = item.email;
viewModel.modal.firstName = item.firstName;
viewModel.modal.lastName = item.lastName;
viewModel.modal.phone = item.phone;
viewModel.modal.email = item.email;
prevState = item;
viewModel.modal.show(true);
Note: I've found a couple of SO posts similar to this one:
how to destroy bootstrap modal window completely?
They haven't really helped though.
After a quick glance, what jumps out is that you don't use the correct syntax to assign the values to your observables. Try this:
self.editI = function (item) {
viewModel.modal.email(item.email()); // item.email() OR ko.unwrap(item.email) if unsure whether it is an observable you're receiving
viewModel.modal.firstName(item.firstName()); // item.firstName() OR ko.unwrap(item.firstName) if unsure whether it is an observable you're receiving
viewModel.modal.lastName(item.lastName()); // item.lastName() OR ko.unwrap(item.lastName) if unsure whether it is an observable you're receiving
viewModel.modal.phone(item.phone()); // item.phone() OR ko.unwrap(item.phone) if unsure whether it is an observable you're receiving
prevState = item;
viewModel.modal.show(true);
Edit: observables are basically function wrappers around your variable. By assigning their value with =, you remove the observable wrapper, because you re-assign the entire variable. By using the ()-syntax, you actually CALL the wrapper function and it will handle the UI update and assigning the new value to its inner variable.
Edit 2: you can make your code a little cleaner (at least in my opinion) by using chaining:
self.editI = function (item) {
prevState = item;
viewModel.modal
.email(item.email())
.firstName(item.firstName())
.lastName(item.lastName())
.phone(item.phone())
.show(true);
To wrap up:
In order to give an observable a new value and see the change in the ui, use ():
var x = ko.observable(3);
x(4); // UI will now reflect 4
To get the underlying value out of an observable, use () or ko.unwrap (works if the variable is observable and also if it isn't, which is often useful)
var x = ko.observable(3);
console.log(x()); // 3
console.log(ko.unwrap(x)); // 3
console.log(ko.unwrap(3)); // 3, not giving any errors
You needed to assign the value of one observable to another, so you combine both:
var x = ko.observable(3);
var y = ko.observable(4);
x(y()); // UI will reflect x = 4
x(ko.unwrap(y)); // UI will reflect x = 4
var z = 4;
x(ko.unwrap(z)); // 4
x(z()); // Error
Edit 3: live edit in a simple way (added because of comments below this answer). First, some updates to your HTML template (notice the with-binding).
<script id="myModal" type="text/html">
<div class="modal-header">
<button type="button" class="close" data-bind="click: close" aria-hidden="true">×</button>
<h3 data-bind="html: header"></h3>
</div>
<div class="modal-body">
<div class="form-group" data-bind="with: modal.item">
<label>First Name</label>
<input type="text" data-bind="value: firstName, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Last Name</label>
<input type="text" data-bind="value: lastName, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Phone</label>
<input type="text" data-bind="value: phone, valueUpdate: 'afterkeydown'" class="form-control" />
<label>Email</label>
<input type="text" data-bind="value: email, valueUpdate: 'afterkeydown'" class="form-control" />
</div>
</div>
<div class="modal-footer">
</div>
</script>
Now what is this modal.item? It is basically the item you're editing. Not a copy, but the item itself! This will give you live-edit. However, we put the item inside the observable item that was created for this case and used in the with-binding. See what we do here? We created bindings in the modal template, in order not to break them we need to bind against an observable that will be filled with our item. This way we avoid your initial problem.
self.editI = function (item) {
viewModel.modal.item(item); // viewModel.modal.item = ko.observable(null);
prevState = ko.toJS(item); // Because we are now editing 'item' directly, prevState will change along if we simply set it to item. So instead, I used ko.toJS to make a 'flat' copy.
viewModel.modal.show(true);
A 'restore' to the previous state can be done by copying back the properties of prevState into item:
self.restoreChanges = function () {
var editingItem = viewModel.modal.item(); // Get the item we're editing out of the observable
if (editingItem && prevState) {
editingItem.email(prevState.email);
editingItem.firstName(prevState.firstName);
// Rest of properties
}
}

How do I trigger custom binding on click event on knockoutjs?

I'm trying to use customBindings but I have no idea how to achieve this. I heard people saying that DOM manipulation shouldn't be mixed in ViewModel so that's why I'm trying to create CustomBindings.
Here's Jsfiddle http://jsfiddle.net/Y3M6n/2/
Here's my HTML
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<div id="div1" class="row">
Name <input type="text" data-bind="value: name" />
Surname <input type="text" data-bind="value: surname" />
</div>
<br/>
<div id="div2" class="row">
Name: <span data-bind="text:name">
Surname: <span data-bind="text:surname">
</div>
<button data-bind="click: submit" >Click</button>
And here's my js code.
function Ctrl() {
var self = this;
self.name = ko.observable();
self.surname = ko.observable();
self.submit = function() {
alert('How do I swap the two divs here');
}
}
ko.applyBindings(new Ctrl());
ko.bindingHandlers.swapDiv = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var div1 = $('#div1').html();
var div2 = $('#div2').html();
$('#div1').replaceWith(div2);
$('#div2').replaceWith(div1);
}
};
My intention is that the first div shows inputs and after a user clicks on the button it should show the confirmation div (second div, which will be hided and shown). If it passes the validation then just confirmation div (div2) on top of the inputs (div1) so the user can enter new information right away. It's the business requirement to keep the flow smooth.
Not sure about your intention, but what if you focus not on the markup but on the view models.
For example, define the fields in view model and swap the values, not actual markup.
like following:
http://jsfiddle.net/tabalinas/Y3M6n/1/
<div id="div1" class="row" data-bind="text: text1">
</div>
<br/>
<div id="div2" class="row" data-bind="text: text2">
</div>
var vm = {
text1: ko.observable("Div 1"),
text2: ko.observable("Div 2"),
submit: function() {
var temp = vm.text1();
vm.text1(vm.text2());
vm.text2(temp);
}
};
I doubt that custom bindings should be used for this purpose. It's usually used to create some reusable component or specific event.
If I'm wrong, clarify your intentions, and I'll try to help.

Categories