knockout issue with deleting from observable array - function

I'm having data binding a knockout function to my html.
Following is the view model:
var DonutViewModel = function () {
this.donuts = ko.observableArray();
//donutData = JSON.parse(donutData);
var items = $.map(donutData, function (data) { return new Donut(data) });
this.donuts(items);
this.deletedonut = function (item) {
this.donuts.remove(item);
}
}
var viewModel;
$(document).ready(function () {
viewModel = new DonutViewModel();
ko.applyBindings(viewModel);
});
following is the html:
<tr>
<td><input id="txtdonutid" type="text" data-bind="value:id"/></td>
<td><input id="txtdonuttype" type="text" data-bind="value:type"/></td>
<td><input id="txtdonutname" type="text" data-bind="value:dname"/></td>
<td><input id="txtppu" type="text" data-bind="value:ppu"/></td>
<td><input type="button" value="Delete Donut" data-bind="click: function() {$parent.deletedonut($data)}"/></td>
</tr>
Notice how I have data-bound the delete function and THIS WORKS!. but if I do the following:
<td><input type="button" value="Delete Donut" data-bind="click: {$parent.deletedonut($data)}"/></td>
well, this doesn't work. The delete function doesn't even get hit.
Can someone tell me what I'm doing wrong?

You only need to the function() ... syntax in your click binding if you want to pass additional parameter to your handler beside the $data. (see the documentation: Accessing the event object, or passing more parameters section)
But if your only parameter is $data then KO will automatically pass that in so you can just write:
<input type="button" value="Delete Donut"
data-bind="click: $parent.deletedonut"/>
Note: there is no need for the {} and you also don't need arguments as well ($data) because you are passing the $parent.deletedonut as the reference to that handler function.
But in itself this won't work in your case because you are using this in your handler to access your view model.
You have two options:
You can use bind: data-bind="click: $parent.deletedonut.bind($parent)" in this case you don't need to change your handler.
Demo JSFiddle
Or you can store a reference of the this in a variable like self and use that in your handler instead of the this.
So change your handler to:
var self = this;
this.deletedonut = function (item) {
self.donuts.remove(item);
}
Demo JSFiddle.

Related

Refresh SlickGrid via call to JsonResult after initial load

I have setup a MVC webpage with a SlickGrid and can populate it upon initial page load. My javascript for loading:
$.getJSON("../Home/GetSlickGridData",
function(data) {
dataView.beginUpdate();
//dataView.setItems(data);
dataView.setItems(data, "OrderID");
dataView.setFilterArgs({
searchString: searchString,
searchShipmentID: searchShipmentID,
searchDestination: searchDestination,
searchCarrier: searchCarrier
});
dataView.setFilter(myFilter);
dataView.endUpdate();
});
This calls a controller action:
public JsonResult GetSlickGridData()
{
The issue now is that I'm unsure of how to refresh the grid when a search criteria is added to the cshtml page's search form:
#using (Html.BeginForm("GetSlickGridData", "Home"))
{
<table>
<tr>
<td>Shipper</td>
<td><input type="text" name="myText" id="txtShipper" /></td>
<td><input type="submit" value="Filter" onclick="GetData"></td>
</tr>
</table>
}
Can I use the loading for initial page load and for a search?
UPDATE:
Based on #Steve T's answer:
The search:
> <input type="text" name="myText" id="txtShipper" />
> <input type="button" value="Filter" onclick="GetData()">
The jquery:
function GetData() {
Slick.GlobalEditorLock.cancelCurrentEdit();
grid.invalidateAllRows();
var searchText = $("#txtShipper").val();
$.getJSON("../Home/GetSlickGridData?search=" + searchText,
function (data) {
The controller:
public JsonResult GetSlickGridData(string search)
And the map route (to ensure the controller works):
routes.MapRoute("search", "Home/GetSlickGridData/{search}",
new {Controller = "Home", Action = "GetSlickGridData"});
Instead of submitting the form and reloading the whole page you can change the filter button to type="button" and then use the GetData() function (not sure if you have this already implemented) to repeat the initial $.getJSON call with the value of text field txtShipper as a parameter

accessing angularjs textbox value in a controller

I am trying to learn AngularJS and require help in passing user entered text box text value after button click to append to a string url value while calling the http service.
I'm trying to add in the following way but it is showing me a value of undefined while appending the URl with the user entered text from the text box.
Here is my HtmlPage1.html
<form ng-submit="abc(inputValue)">
<input type="text" name="name" ng-model="inputValue" />
<button type="submit">Test</button>
</form>
and my script file Script.js
var app = angular.module("repos", [])
.controller("reposController", function ($scope, $http, $log) {
$scope.inputValue = null;
$scope.abc = function (value) {
$scope.inputValue = value;
};
$http({
method:'GET',
url: 'https://api.github.com/users/'+$scope.inputValue+'/repos'
})
.then(function (response) {
$scope.repos = response.data;
$log.info(response);
});
});
Can anyone help me in this regard on how to get the right value that the user has entered to appended to the URL?
Thanks in advance.
Your get call is placed before you enter any value. In order to call the API with inputValue, place the get call inside the button click.
Also, you do not have to pass the inputValue into the function from HTML, Angular's 2 way binding will do the job for you.
Ex:
HTML
<form ng-submit="abc()">
<input type="text" name="name" ng-model="inputValue" />
<button type="submit">Test</button>
</form>
JS:
var app = angular.module("repos", [])
.controller("reposController", function ($scope, $http, $log) {
$scope.inputValue = null;
$scope.abc = function () {
$log.info($scope.inputValue) // you will have your updated value here
$http({
method:'GET',
url: 'https://api.github.com/users/'+$scope.inputValue+'/repos'
})
.then(function (response) {
$scope.repos = response.data;
$log.info(response);
});
});
};
I hope this helps.
Just remember that you have the code on your controller thanks to 2 way binding.
There you will set up an object for models. Ad later you can use them to submit data.
In order for you to understand what I am trying to explain I made an example, I hope it Helps
In your code:
Set the ng-model on the input tag
<input type="text" name="name" ng-model="vm.data.inputValue" />
On your controller make it available as in my example
vm.data ={};
Then use a function to send it using ng-click.
<button type="submit" ng-click="vm.submit()">Test</button>
I am sure there are more ways to do this.
I am not that good, explaining so I made an example, that I hope helps:
https://jsfiddle.net/moplin/r0vda86d/
my example is basically the same but I prefer not to use $scope.

Function is not defined ReferecenError onchange in Angularjs

I'm having issues with onchange event for file input.
Here is my code:
Html:
<input type="file" kendo-upload k-options="uploadOptions" id="fileInput" onchange="fileUploaded(event)"/>
Angularjs code - using Typescript.
$scope.fileUploaded = (event) => {
console.log("Testing file upload");
var reader = new FileReader();
var target = event.target || event.srcElement;
reader.onload = function(e) {
$scope.$apply(function() {
$scope.readFile = reader.result;
});
};
var file = target.files[0];
reader.readAsText(file);
};
I tried to follow this and change accordingly but am still having issues.
Pass angularJS $index into onchange
I changed like this.
<input type="file" kendo-upload k-options="uploadOptions" id="fileInput" onchange="angular.element(this).scope().fileUploaded(this)">
I got this error.
Uncaught TypeError: undefined is not a function.
onchange
When I am trying to use ng-change, I have this error.
<div kendo-window="importFile" k-visible="false" k-modal="true" k-title="'Import File'"
k-min-width="450" k-max-width="450"
k-min-height="418" k-max-height="418">
<form kendo-validator="validator" ng-submit="validate($event)">
<li style="padding-bottom: 5px;">
<input type="file" kendo-upload k-options="uploadOptions" id="fileInput" ng-change="fileUploaded($event)"/>
</li>
</form>
</div>
This gives error when accessing this
$scope.importFile.center();
"TypeError: Cannot read property 'center' of undefined"
Again, works fine if I use ng-click.
Any help on this is higly appreciated.
Thanks!
You cannot use ngChange in this case since ngChange always requires ngModel and ngModel doesn't work with input type file. Also, you cannot access a function binded to $scope directly using onchange. Instead use the following:
onchange="angular.element(this).scope().fileUploaded($event)"

HTML "this" versus AngularJS ng-model

We are in the process of converting a very old ColdFusion application that made convenient use of "this" in conjunction with a function that did formatting:
<td>$<input type="text" name="txtTaxProration" id="txtTaxProration" value="0.00" alt="Tax Proration" onblur="dollarBlur(this);"></td>
The dollarBlur function would convert the numeric input to currency, i.e if the user entered 123, it was converted to 123.00; 23.45 was left as 23.45. This made the reference on the HTML side easy, but even easier in the actual function as the name of the element did not have to be specified. Is there some analogous way to do this in Angular?
<td>$<input type="text" ng-model="NetSheetDetail.TxtHomeWarranty" name="txtHomeWarrantyPolicy" id="txtHomeWarrantyPolicy" value="0.00" ng-change="angularDollarBlur(this)" ng-model-options="{ updateOn: 'blur' }"></td>
The following works fine, almost, HTML
<input type="text" ng-model="NetSheetDetail.TxtHomeWarranty" ng-change="reCalcX('NetSheetDetail.TxtHomeWarranty')" ng-model-options="{ updateOn: 'blur' }" ></td>
Controller
$scope.reCalcX = function (propName) {
alert($scope.$eval(propName));
$scope['propName'] = 666;
};
$scope.$eval(propName) does correctly reflect what was entered on the webpage ($scope['propName'] is undefined). However, $scope['propName'] doesn't appear to work - the change is not reflected back in the webpage.
Yes, simply pass the model.
ng-change="angularDollarBlur(NetSheetDetail.TxtHomeWarranty)"
$scope.angularDollarBlur = function (model) {
console.log(model);
}
Here's another example:
angular.module('app',[]).controller('myController', function ($scope) {
$scope.NetSheetDetail = {
TxtHomeWarranty: 'Hello World!'
};
$scope.angularDollarBlur = function (text, obj, prop) {
alert(text);
obj[prop] = 'Nope.';
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="myController">
<input type="text" ng-model="NetSheetDetail.TxtHomeWarranty" ng-change="angularDollarBlur(NetSheetDetail.TxtHomeWarranty,NetSheetDetail,'TxtHomeWarranty')" />
</div>

textbox disable in knockout

I have a table like below:
<tbody data-bind="foreach: tasks">
<tr>
<td>
<span data-bind="text: goal" />
</td>
<td>
<input type="text" data-bind="value: note ,
disable: !($data.isAllowedForMember)" />
</td>
</tr>
</tbody>
I want to make note textbox disable when isAllowedForMember = false. But everytime its making note disable(wheather isAllowedForMember = true or false).
Here is my viewmodel
//viewmodel
function GoalSheetViewModel() {
self.tasks = ko.observableArray([]); //tasklist
self.note = ko.observable();
self.isAllowedForMember = ko.observable();
self.IsAllowedToChange = function () {
$.ajax({
success: function (results) {
self.isAllowedForMember(results.d);
},
})
};
};
You should unwrap observable if you use it in condition:
<input type="text" data-bind="value: note , disable: !$parent.isAllowedForMember()" />
The following article can help you to learn some useful things about knockout: http://www.knockmeout.net/2011/06/10-things-to-know-about-knockoutjs-on.html
EDIT:
isAllowedForMember is member of parent context so you should use $parent object to access it:
<input type="text" data-bind="value: note , disable: !$parent.isAllowedForMember()" />
As Artem said you need to unwrap the observable, but even better to use a computed with a name saying what the business rule means
like
this.readonlyMember = ko.computed(function() {
return this.isAllowedForMember();
}, this);
But you also have a releation problem with your model since you get
ReferenceError: isAllowedForMember is not defined