Updating properties of an item within an observable array in Knockout - javascript

I'm trying to implement an updatable "selected item" of an observable array in Knockout, such that changes on the selected item's properties get propagated back to the list.
In the following example, my model contains a list of users in an observable array, with each user having an observable 'name' property. The model also has a selectedUser property, which references a user in the list.
I have an input control bound to the name() value of the selected user. I want to be able to change the name within the input, and see that change propagate to the list below.
JS:
var UserModel = function() {
this.users = ko.observableArray([
{id:1, name:ko.observable('Bob')},
{id:2, name:ko.observable('Jane')}
]);
this.selectedUser = ko.observable(this.users()[0]);
console.log(this.users()[0].name());
}
ko.applyBindings(UserModel);
HTML:
<input type="text" data-bind="value: selectedUser().name()" value="">
<table data-bind="foreach: users()">
<tr >
<td data-bind="text: $data.name()"> </td>
</tr>
</table>
FIDDLE:
http://jsfiddle.net/5t5k2/5/

You're almost there.
The only problem is that you're binding the input field to an invocation of the observable and not the function itself.
When you do .name() you're invoking the observable so you get a string back. Instead, bind to the observable itself.
Change:
<input type="text" data-bind="value: selectedUser().name()" value="">
to
<input type="text" data-bind="value: selectedUser().name" value="">
( fiddle )
If you want that immediate feel, you can tell it to update on key down too and not just on the change event (that fires on tab or when you lose focus)
<input type="text" data-bind="value: selectedUser().name, valueUpdate: 'keyup'" value="">
( fiddle )

Related

add dirty attribute to text and dropdownlist with [ngModelOptions]="{standalone: true}" in angular 8 (kendo)

I have a few dropdownlist tag that is binding dynamic data, so I need to use [ngModelOptions]="{standalone: true}". I know that using ngModelOption means ignoring the form attribute, is there any more that can add the dirty attribute to text or dropdownlist.
HTML file
<form #form="ngForm">
<div #ngFor="let item of [].constructor(details.length); let i = index>
<input [(ngModel)]="person[i].age" id="age" name="age" [ngModelOptions]="{standalone: true}"/>
<kendo-dropdownlist [data]="personName" [(ngModel)]="person[i].name" id="name" name="name" [ngModelOptions]="{standalone: true}"></kendo-dropdownlist>
</div>
<input [(ngModel)]="city" id="city" name="city" />
<button (click)="checkDirty()">CHECK DIRTY</button>
</form>
typescript file
#ViewChildren('form', {static: true})
public form: NgForm;
details = ["one","two"];
personName=["A","B","C"];
city="";
person = [{"age":"", "name":""},{"age":"", "name":""}];
checkDirty(){
console.log(this.form.dirty);
// when i type something in the city input, and click check dirty button, it return true
// however, if i type anything in age input or select any value in name and click check dirty button, it always return false.
}
I know that adding [ngModelOptions]="{standalone: true} basically means ignore the attribute in form. Is there any way that can add the dirty attribute manually for the dropdownlist name and input age?

Why is my AngularJs 'add' button only working once?

I'm just starting out with Angular.
I've written some code that downloads a JSON array configuredAPIs and displays each object within it, <div ng-repeat="capi in configuredAPIs">. For each of these, there's another directive to list the items from an array of strings, <tr ng-repeat="eurl in capi.externalURLs">
Underneath there's a text box to add a new string to this array, which I've bound to a $scope variable called url.
When I click the 'add' button, everything works - the new string is added to the array, a new row appears in the table.. ..but it only works once. Subsequent clicks on the 'add' button add empty strings to the array (and thus empty text boxes).
What have I done wrong?
index.html
<div ng-app="testApp" ng-controller="testCtrl">
<div ng-repeat="capi in configuredAPIs">
<h1>Configured API</h1>
<p>
Name:
{{ capi.name }}
</p>
<h2>External URLs</h2>
<form ng-submit="addExternalURL(capi)">
<table>
<!-- A row in the table for each string in the array -->
<tr ng-repeat="eurl in capi.externalURLs">
<td>
<input type="text" ng-model="eurl" />
</td>
</tr>
<!-- Final table row to add a new string to the array -->
<tr>
<td>
<input type="text" ng-model="url" placeholder="Enter a new external URL">
<input class="btn-primary" type="submit" value="add">
</td>
</tr>
</table>
</form>
</div>
</div>
controller.js
var app = angular.module('testApp', []);
app.controller('testCtrl', function ($scope, $http) {
$scope.url = 'new url';
$http.get("/api/configuredapis?orgid=2")
.success(function (response) { $scope.configuredAPIs = response; });
$scope.addExternalURL = function ($capi) {
$capi.externalURLs.push($scope.url);
$scope.url = '';
};
});
It is because AngularJS does not watch and update primitives (e.g. strings, numbers, booleans) the way one obviously thinks it does.
So instead you bind objects with values to the scope or use a function which returns the primitive value.
See:
https://github.com/angular/angular.js/wiki/Understanding-Scopes
http://www.codelord.net/2014/05/10/understanding-angulars-magic-dont-bind-to-primitives/
Example for using an object (at controller):
$scope.primitives = {
url : 'foo://'
}
And within the template:
<input type="text" ng-model="primitives.url">
So what happens in your example is that once you set it to '' the changes to the model within the template are not recognized anymore.

how to get ng-repeat checkbox values on submit function in angularjs

I have a form which has 10 checkboxes. By default angular js triggers on individual checkbox. I want to grab all selected check box values on submit action only. Here is my code...
<form name="thisform" novalidate data-ng-submit="booking()">
<div ng-repeat="item in items" class="standard" flex="50">
<label>
<input type="checkbox" ng-model="typeValues[item._id]" value="{{item._id}}"/>
{{ item.Service_Categories}}
</label>
</div>
<input type="submit" name="submit" value="submit"/>
</form>
$scope.check= function() {
//console.log("a");
$http.get('XYZ.com').success(function(data, status,response) {
$scope.items=data;
});
$scope.booking=function(){
$scope.typeValues = [];
console.log($scope.typeValues);
}
I am getting empty array.
Can somebody tell how to grab all selected checkbox values only on submit event.
<div ng-repeat="item in items">
<input type="checkbox" ng-model="item.SELECTED" ng-true-value="Y" ng-false-value="N"/>
</div>
<input type="submit" name="submit" value="submit" ng-click="check(items)"/>
$scope.check= function(data) {
var arr = [];
for(var i in data){
if(data[i].SELECTED=='Y'){
arr.push(data[i].id);
}
}
console.log(arr);
// Do more stuffs here
}
Can I suggest reading the answer I posted yesterday to a similar StackOverflow question..
AngularJS with checkboxes
This displayed a few checkboxes, then bound them to an array, so we would always know which of the boxes were currently checked.
And yes, you could ignore the contents of this bound variable until the submit button was pressed, if you wanted to.
As per your code all the checkboxes values will be available in the typeValues array. You can use something like this in your submit function:
$scope.typeValues
If you want to access the value of 3rd checkbox then you need to do this:
var third = $scope.typeValues[2];
Declare your ng-model as array in controller, like
$scope.typeValues = [];
And in your template, please use
ng-model="typeValues[item._id]"
And in your controller, you will get this model array values in terms of 0 and 1. You can iterate over there.

Is it possible to pass HTML input field variables to a custom Angularjs filter?

I'm new to Angular so this might be real easy.
I have a JSON array of some offers. Each offer has a price among other things. I want to be able to add a filter so that the user can specify their min/max spent and only sees offers that fall into these brackets.
In the list controller I have
// MinMax filter:
$scope.minMax = function(prop, mn, mx){
return function(item){
if ( (Number(item[prop]) >= mn) && (Number(item[prop])<= mx) ) return true;
}
}
In the View HTML I have two simple text input fields:
<input id='cheapest' name="cheapest" type='text' value='10' />
<input id='dearest' name="dearest" type='text' value='5000' />
and the repeater
<div ng-repeat="offer in offers | filter: minMax('Price' , [?cheapest?], [?dearest?]) ">
The filter works when I hardcode values in, but how do I bind them to the input field?
Thanks
This is a calling to Captain ngModel ;)
Just bind your input values:
<input id='cheapest' name="cheapest" type='text' value='10' ng-model="cheapest" />
<input id='dearest' name="dearest" type='text' value='5000' ng-model="dearest" />
and pass the value to your filter:
<div ng-repeat="offer in offers | filter: minMax('Price', cheapest, dearest) ">
or access them via $scope.cheapest in your controller.
I am not sure with the syntax though but it will work somehow :)

Update ViewModel using Custom Bindings KnockoutJS

So I have this SPA developed using this sample.
The sample shows the list of Todo in a table something like this
<section id="lists" data-bind="foreach: todoLists, visible: todoLists().length > 0">
<table width="100%" style="margin-top: 20px;" class="table-main">
<thead>
<tr class="b-table-line">
<th>Select</th>
<th>Title</th>
<th>Artist</th>
</tr>
</thead>
<tbody data-bind="foreach: todos">
<tr>
<td>
<input type="checkbox" data-bind="checked: isDone" /></td>
<td>
<input class="todoItemInput" type="text"
data-bind="value: title,
disable: isDone,
blurOnEnter: true,
updateOnTitle:true,
click: $root.clearErrorMessage" />
</td>
<td>
<input class="todoItemInput" type="text"
data-bind="value: artist,
click: $root.clearErrorMessage" />
</td>
</tr>
</tbody>
</table>
Now what I am trying to do here is as soon as I change the Title text, I try to change Artist text as well, for that I have created a custom binding updateOnTitle and associated it with the textbox as shown in the table. Its definition looks something like this:
ko.bindingHandlers.updateOnTitle = {
init:function(element,valueAccessor,allBindings,viewModel,bindingContext)
{
$(element).blur(function (evt) {
//Here I am trying to update the artist property based on title
bindingContext.$data.title("Title goes here");
bindingContext.$data.artist("New Artist Name Here");
}
}
The changes are not reflected in the table above. Both these properties are observable.
I would like to know what exactly am I missing here?
I may as well turn my comment into an answer. I think binding handlers are better suited as a general approach to solve problems, and it pays off to avoid having a binding rely on a specific ViewModel (your binding refers to "artist" and "title"). A writeable computed observable may be more suited for the task.
Suppose the following view:
<h3>Inputs</h3>
Title: <input type="text" data-bind="value: title, valueUpdate: 'afterkeydown'" /><br />
Artist: <input type="text" data-bind="value: artist, valueUpdate: 'afterkeydown'" />
<hr />
<h3>Read only version</h3>
Title: <span data-bind="text: title"></span><br />
Artist: <span data-bind="text: artist"></span>
Notice the first input is bound to titleEditing, the computed observable. The ViewModel could be defined with these properties:
function ViewModel() {
var self = this;
var _title = ko.observable('my title');
self.title = ko.computed({
read: _title,
write: function(newval) {
_title(newval);
self.artist('New Artist Name Here');
}
});
self.artist = ko.observable('john doe');
};
Now, if you update the first input, title will change and artist will reset.
See this fiddle for a demo.

Categories

Resources