How to bind current object to text input? - javascript

MessageModel
function MessageModel(content) {
var self = this;
self.content = content;
}
RoomViewModel
self.currentMessage = ko.observable(new MessageModel(""));
self.addMessage = function () {
self.messages.push(self.currentMessage());
self.currentMessage(new MessageModel(""));
};
View
<form data-bind="submit: addMessage">
<input data-bind='value: currentMessage.content, valueUpdate: "afterkeydown"' />
<button id="ButtonSendMessage" type="submit">Send</button>
</form>
When user types in input box want the current message content property to update and when I click add I want the currentMessage to be added with content. But the content is always blank.

Maybe because the content isn't an observable and the value bind is wrong beacuse currentMessage is an observable so to bind any property of you must to do like currentMessage().prop or use the with: currentMessage binding in an element context, try something like this:
function MessageModel(content) {
var self = this;
self.content = ko.observable(content);
}
And also i suggest you to use the textInput bind:
<form data-bind="submit: addMessage">
<input type="text" data-bind='textInput: currentMessage().content' />
<button id="ButtonSendMessage" type="submit">Send</button>
</form>
textInput Binding:
The textInput binding links a text box () or text area () with a viewmodel property, providing two-way updates between the viewmodel property and the element’s value. Unlike the value binding, textInput provides instant updates from the DOM for all types of user input, including autocomplete, drag-and-drop, and clipboard events.
Ref:TextInput Bind

Related

two way binding [(ngmodel)] unaffected by jquery change event

I need to update the angular model on a js change event,
this is a simplified, isolated demo:
hero-form.component.html:
<button type="button" id='btn1'>change</button>
<input type="text" id="txt1" name="txt1" [(ngModel)]="str1" />{{str1}}
hero-form.component.ts:
...
import * as $ from "jquery";
...
export class HeroFormComponent implements OnInit {
str1 = "initval";
ngAfterViewInit(){
var txt1 = $('#txt1');
$('#btn1').on('click', function(){
txt1.val('new val').change();
// when js/jquery triggers a change on the input, I need the str1
// which was bound using [(ngModel)] to be updated
});
}
when clicking the button the value of the textbox changes to new val but the interpolation {{str1}} is not affected, however if I change the value of the textbox manually it works.
is it possible to update the model bound with ngmodel on a js change event ?
In angular project we should not implement your requirement like your way.
You can use (click) event and #txt1 to get value
In ts component implement change str1 value.
export class AppComponent {
name = 'Binding data in Angular';
str1 = "initval";
ngAfterViewInit() {
}
change(val) {
console.log(val)
this.str1 = val;
}
}
Updated HTML
<hello name="{{ name }}"></hello>
<button type="button" id='btn1' (click)="change(txt1.value)">change</button>
<input type="text" #txt1 id="txt1" name="txt1" />{{str1}}
Demo: https://stackblitz.com/edit/angular-click-change-value

How to ignore changes not done by user?

I've got a ko.observable in my viewmodel, which is attached to an input. When user changes value of that input (per-character) I run AJAX call, which downloads suggestions from backend.
When user chooses one of suggestions, I'd like to fill the input with chosen value, but without sending the AJAX call. But setting observable's value will still trigger event and call function attached to textInput binding.
How can I set observable's value without triggering textInput?
Sample code:
var name = ko.observable();
name.subscribe(nameChanged);
var nameChanged = function() {
someService.post(name(), success, failure);
}
var someAction = function() {
name("Test"); // I don't want to trigger AJAX call here
}
<input type="text" data-bind="textInput: name" />
Another option is to use a computed observable as an intermediary. The ajax function can trigger in the write event of the computed so that direct writes to the observable bypass it.
function viewModel(){
var self = this;
self.realName = ko.observable('test');
self.Name = ko.computed({
read: function(){
return self.realName();
},
write: function(value){
self.realName(value);
nameChanged(value);
}
});
function nameChanged(newName) {
console.log("name changed:", newName);
}
self.modifyName = function(){
self.realName(self.realName() + 'z');
}
}
ko.applyBindings(new viewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" data-bind="textInput: Name" />
<br />
<button data-bind="click: modifyName">Modify</button>
Maybe set a particular CSS class on the field after the selection is made and put some logic around the ajax call to check if the field has the CSS class.
Instead of subscribing to the change event of the observable you could tie your ajax call to the change event of the ui element instead. You would get rid of name.subscribe(nameChanged); and add an event binding:
data-bind="textInput: name, event: { changed: nameChanged }"
An "old fashioned" way that works, but is not really elegant, is to temporarily dispose the subscription and then reattach it:
var myName = ko.observable();
var nameSub = myName.subscribe(nameChanged);
function nameChanged(newName) {
console.log("name changed:", newName);
}
function changeWithoutLog() {
nameSub.dispose();
myName("Test");
nameSub = myName.subscribe(nameChanged);
}
ko.applyBindings({ name: myName });
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<input type="text" data-bind="textInput: name" />
<button data-bind="click: changeWithoutLog">change without post</button>

How can I create a web component that acts like a form element?

I am trying to create a web component that is usable in a form element specifically, that has a name, and a value. I recognize that I can create a web component that extendsan HTMLInputElement:
<input is="very-extended">
but I am trying to create an entirely new element.
When creating a regular web component, you can create it from the prototype of a regular HTMLElement (HTMLElement.prototype). Which leads me to assume that I can create a different element with the prototype of an HTMLInputElement (HTMLInputElement.prototype). You actually use that prototype when extending the API of input elements, so why can't I use that prototype to create an entirely new element that works in a form?
If you look at the shadow dom of a regular input field:
you can see that there is a div inside of there. I understand that this HTMLInputElement has methods and attributes, getters/setters, etc. So why is it that when I attempt to create my component, it fails to be part of the name, value pairs found in the form?
Here is an example of how I am trying to create this web component:
Please note that his should be tested in a browser that supports web components.
(function() {
var iconDoc = (document._currentScript || document.currentScript).ownerDocument;
var objectPrototype = Object.create(HTMLInputElement.prototype);
Object.defineProperty(objectPrototype, 'name', {
writable: true
});
Object.defineProperty(objectPrototype, 'value', {
writable: true
});
objectPrototype.createdCallback = function() {
var shadow = this.createShadowRoot();
var template = iconDoc.querySelector('#test');
shadow.appendChild(template.content.cloneNode(true));
};
document.registerElement('custom-input', {
prototype: objectPrototype
});
})();
console.log(
$('form').serialize()
)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<template id="test">
<div>This is a special input</div>
</template>
<form>
<input name="regular" value="input">
<custom-input name="foo" value="bar"></custom-input>
</form>
Why is the name, value pair not found among the form, and how can I create a custom form element?
You can create your <custom-input> custom element that will be interpreted by a form, just by adding inside your template a hidden input element with the
name and value pair your want.
<template>
<input type="hidden" name="foo" value="defaultVal">
</template>
The default value (and name) can by updated by your custom element internal logic.
This hidden input must not be inserted inside a Shadow DOM to be detected by the container form.
(function() {
var iconDoc = (document._currentScript || document.currentScript).ownerDocument;
var objectPrototype = Object.create(HTMLInputElement.prototype);
objectPrototype.createdCallback = function() {
//var shadow = this.createShadowRoot();
var template = iconDoc.querySelector('#test');
this.appendChild(template.content.cloneNode(true));
};
document.registerElement('custom-input', {
prototype: objectPrototype
});
})();
console.log(
$('form').serialize()
)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<template id="test">
<input type="hidden" name="foo" value="bar">
</template>
<form>
<input name="regular" value="input">
<custom-input name="foo" value="bar"></custom-input>
</form>
KevBot,
You seem to think that the element includes itself in the form. That is not the case. It is the form that searches its children elements by tag name, to decide which elements it should include. It will simply ignore those with unknown tag names.
Your custom-input name is not among the elements the form searches. Therefore, it is not included on the form. It doesn't matter the custom element's prototype. That's why it works if you use is, since then the tag name is maintained.
Of course, you may implement your own custom-form that behaves differently, if you want.
You can do this:
(function() {
var newInputExtended = Object.create(HTMLInputElement.prototype);
newInputExtended.createdCallback = function() {
this.value = 'baz';
};
document.registerElement('foo-input', {
extends: 'input',
prototype: newInputExtended
});
window.something = function(form, event) {
$('<p>')
.text($(form).serialize())
.appendTo('body')
event.preventDefault();
}
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form onsubmit="something(this, event)">
<input is="foo-input" name="foo" value="bar">
<button>Hi</button>
</form>
But you get an error if you try to create a new shadow root. It looks like you are limited to only extending the data/logic around the element's user-agent shadow root.

Pass Model to Controller to Change in Controller

Here is my situation, I have this HTML:
<input type="text" ng-model="inputModel" />
<div class="button-container">
<button type="button" ng-click="setValue(inputModel)"></button>
</div>
<input type="text" ng-model="inputModelTwo" />
<div class="button-container">
<button type="button" ng-click="setValue(inputModelTwo)"></button>
</div>
And my view controller:
$scope.setValue = function (valueToSet) {
valueToSet = "Some value.";
};
I need to be able to "connect" the input fields and their buttons and I'm doing this by having the respective button pass the respective input field's model to the controller to be modified. The problem is that when I click the button the function fires off and valueToSet is changed but the change isn't reflected back in the view! What am I missing?
If you are trying to dynamically pass in your models as a function parameter, you'll need to access a PROPERTY on the models by using dot notation.
Try defining the models in the controller like so:
$scope.inputModel = {};
$scope.inputModelTwo = {};
$scope.inputModel.text = 'hey';
$scope.inputModelTwo.text = 'ho';
Then pass in the entire model to the function as you were already doing.
Inside the function, alter the property that you desire (in this case 'text'), like so:
$scope.setValue = function (valueToSet) {
console.log(valueToSet);
valueToSet.text = "Some value.";
};
JSFiddle

DataPicker not getting binded to textbox ? fiddle provided

Well in other cases i will get datepicker binded to my textbox which will be straight forward but not in this case .
Fiddle link : http://jsfiddle.net/JL26Z/1/ .. while to setup perfect seanrio i tried but unable to bind datepicker to textboxes . except that everything is in place
My code :
**<script id="Customisation" type="text/html">** // here i need to have text/html
<table style="width:1100px;height:40px;" align="center" >
<tr>
<input style="width:125px;height:auto;" class="txtBoxEffectiveDate" type="text" id="txtEffective" data-bind="" />
</tr>
</script>
The above code is used for my dynamic generation of same thing n no of time when i click each time on a button . So above thing is a TEMPLATE sort of thing .
My knockout code :
<div data-bind="template:{name:'Customisation', foreach:CustomisationList},visible:isVisible"></div>
<button data-bind="click:$root.CustomisatioAdd" >add </button>
I tried same old way to bind it with datepicker
$('#txtEffective').datepicker(); // in document.ready i placed
Actually to test this i created a textbox with some id outside script with text/html and binded datepicker to it and It is working fine sadly its not working for the textbox inside text/html and i want to work at any cost.
PS: well i haven't posted my view model as it is not required in this issue based senario
View model added with Js
var paymentsModel = function ()
{
function Customisation()
{
var self = this;
}
var self = this;
self.isVisible = ko.observable(false);
self.CustomisationList = ko.observableArray([new Customisation()]);
self.CustomisationRemove = function () {
self.CustomisationList.remove(this);
};
self.CustomisatioAdd = function () {
if (self.isVisible() === false)
{
self.isVisible(true);
}
else
{
self.CustomisationList.push(new Customisation());
}
};
}
$(document).ready(function()
{
$('#txtEffective').datepicker();
ko.applyBindings(new paymentsModel());
});
Any possible work around is appreciated
Regards
The best way I've found to do this is create a simple bindingHandler.
This is adapted from code I have locally, you may need to tweak it...
** code removed, see below **
Then update your template:
** code removed, see below **
By using a bindingHandler you don't need to try to hook this up later, it's done by knockout when it databinds.
Hope this is helpful.
EDIT
I created a fiddle, because I did indeed need to tweak the date picker binding quite a lot. Here's a link to the Fiddle, and here's the code with some notes. First up, the HTML:
<form id="employeeForm" name="employeeForm" method="POST">
<script id="PhoneTemplate" type="text/html">
<div>
<span>
<label>Country Code:</label>
<input type="text" data-bind="value: countryCode" />
</span>
<span><br/>
<label>Date:</label>
<input type="text" data-bind="datepicker: date" />
</span>
<span>
<label>Phone Number:</label>
<input type="text" data-bind="value: phoneNumber" />
</span>
<input type="button" value="Remove" data-bind="click: $parent.remove" />
</div>
</script>
<div>
<h2>Employee Phone Number</h2>
<div data-bind="template:{name:'PhoneTemplate', foreach:PhoneList}">
</div>
<div>
<input type="button" value="Add Another" data-bind="click: add" />
</div>
</div>
</form>
Note I removed the id=... from in your template; because your template repeats per phone number, and ids must be unique to be meaningful. Also, I removed the datepicker: binding from the country code and phone number elements, and added it only to the date field. Also - the syntax changed to "datepicker: ". If you need to specify date picker options, you would do it like this:
<input type="text" data-bind="datepicker: myObservable, datepickerOptions: { optionName: optionValue }" />
Where optionName and optionValue would come from the jQueryUI documentation for datepicker.
Now for the code and some notes:
// Adapted from this answer:
// https://stackoverflow.com/a/6613255/1634810
ko.bindingHandlers.datepicker = {
init: function(element, valueAccessor, allBindingsAccessor) {
//initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {},
observable = valueAccessor(),
$el = $(element);
// Adapted from this answer:
// https://stackoverflow.com/a/8147201/1634810
options.onSelect = function () {
if (ko.isObservable(observable)) {
observable($el.datepicker('getDate'));
}
};
$el.datepicker(options);
// set the initial value
var value = ko.unwrap(valueAccessor());
if (value) {
$el.datepicker("setDate", value);
}
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.datepicker("destroy");
});
},
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor()),
$el = $(element);
//handle date data coming via json from Microsoft
if (String(value).indexOf('/Date(') === 0) {
value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
}
var current = $el.datepicker("getDate");
if (value - current !== 0) {
$el.datepicker("setDate", value);
}
}
};
function Phone() {
var self = this;
self.countryCode = ko.observable('');
self.date = ko.observable('');
self.phoneNumber = ko.observable('');
}
function PhoneViewModel() {
var self = this;
self.PhoneList = ko.observableArray([new Phone()]);
self.remove = function () {
self.PhoneList.remove(this);
};
self.add = function () {
self.PhoneList.push(new Phone());
};
}
var phoneModel = new PhoneViewModel();
ko.applyBindings(phoneModel);
Note the very updated binding handler which was adapted from this answer for the binding, and this answer for handling onSelect.
I also included countryCode, date, and phoneNumber observables inside your Phone() object, and turned your model into a global variable phoneModel. From a debugger window (F12 in Chrome) you can type something like:
phoneModel.PhoneList()[0].date()
This will show you the current value of the date.
I notice that your form is set up to post somewhere. I would recommend instead that you add a click handler to a "Submit" button and post the values from your phoneModel using ajax.
Hope this edit helps.
Dynamic entities need to have datepicker applied after they are created. To do this I'd use an on-click function somewhere along the lines of
HTML
<!-- Note the id added here -->
<button data-bind="click:$root.CustomisatioAdd" id="addForm" >add </button>
<script>
$(document).on('click', '#addForm', function(){
$('[id$="txtEffective"]').datepicker();
});
</script>

Categories