One of the columns in my ng-repeat directive outputs the values of $$hashkey.
I have no idea how this started happening. I get data from a simple GET and inspecting that data as it gets in from the success callback shows the $$hashkey being inserted to each object. I understand the $$hashkey is used by angular but this never happened before as far as HTML view output goes.
This is on 1.2.16
HTTP GET:
$http.get('index.php/getWorkbook/'+$routeParams.workbook).success(function(data) {
console.log(data); // Has $$hashkey inserted
$scope.workbook = data;
});
HTML:
<tr ng-repeat='row in workbook'>
<td ng-repeat="key in notSorted(row)" ng-init="value = row[key]">
<input type="text" ng-model="value" ng-blur="edit(value, key, row)" />
</td>
</tr>
Here is the controller function.
$scope.notSorted = function(obj){
if (!obj) {
return [];
}
return Object.keys(obj);
}
Seems the rows don't like being ran through notSorted(). Adding angular.copy() ended up working for me.
$scope.notSorted = function(obj){
obj = angular.copy(obj);
if (!obj) {
return [];
}
return Object.keys(obj);
}
Try this change in your controller
$scope.workbook = data;
$scope.workbook = angular.fromJson(angular.toJson($scope.workbook));
Related
I have two select drop down list and one text box.
There is option to clone each row using ng-repeat.
When selecting dropdown value i will get value from database. So i need to bind that value in textbox.
Controller:
$scope.major_change = function(value, index){
$scope.major_id = value;
};
$scope.minor_change = function(value, index){
$scope.minor_id = value;
$scope.active = 'active';
var temp = surveyService.getDistList($scope.major_id, $scope.minor_id);
temp.then(function (msg) {
alert(msg.data[0].gi_code);
$scope.gi_code[index] = msg.data[0].gi_code;
}, function () {
$scope.Error = 'Error in adding record';
});
};
Html Screen
You are binding the wrong value to ng-model. This was the reason two way binding was not working.
$scope.minor_change = function(value, index){
$scope.minor_id = value;
/*$scope.dist.gi_code[index] = value;*/
$scope.dist_list[index].gi_code=value;
}
Working Plunker: https://plnkr.co/edit/dRr90YtjtSRhHUZKisMQ?p=preview
I don't know what actually your html code look like but may be this help you as desired.You can use two-way data binding as :
HTML Code :
<tr>
<td><select class="select1"></option></option></td>
<td><select class="select1"></option></option></td>
<td><input type="text" ng-model="value" ></td>
Javascript Code :
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.value= $('.select1').val();
});
</script>
I have data attached to $scope object in controller, just like this
$scope.result = [];
$scope.jsonData = function () {
var result = [];
var keys = $scope.data[0];
for (var i = 1; i < $scope.data.length; i++) {
var item = {};
item[keys[0]] = $scope.data[i][0];
item[keys[1]] = $scope.data[i][1];
item[keys[2]] = $scope.data[i][2];
$scope.result.push(item);
}
console.log($scope.result);
};
I am able to access this data only while clicking button in HTML using ng-click directive
<button ng-click="jsonData()">
<table border="1">
<tr ng-repeat="x in result">
<td>{{x.Name}}</td>
<td>{{x.Age}}</td>
<td>{{x.Address}}</td>
</tr>
</table>
</button>
However, I am unable to access json data using ng-init directive. Am I doing anything wrong?
<div ng-init="jsonData()">
<table border="1">
<tr ng-repeat="x in result">
<td>{{x.Name}}</td>
<td>{{x.Age}}</td>
<td>{{x.Address}}</td>
</tr>
</table>
</div>
Why are you using ng-init this way? Take a look at the documentation:
ng-init documentation
This doesn't look like a valid use case for ng-init unless there is more code to your controller I cannot see.
I would get rid of the ng-init on your div and just run the function directly in your controller by calling:
$scope.jsonData();
right after you define the function. Like this:
$scope.result = [];
$scope.jsonData = function () {
var result = [];
var keys = $scope.data[0];
for (var i = 1; i < $scope.data.length; i++) {
var item = {};
item[keys[0]] = $scope.data[i][0];
item[keys[1]] = $scope.data[i][1];
item[keys[2]] = $scope.data[i][2];
$scope.result.push(item);
}
console.log($scope.result);
};
$scope.jsonData();
The likely cause of the problem is that the ng-init directive is executing before the data arrives from the server.
In the controller, chain from the promise that puts the data on scope:
$http.get(url).then(function(response) {
$scope.data = response.data;
}).then(function() {
$scope.jsonData();
});
In an MV* framework, the controller creates the model and then the framework maps the model to the DOM. The framework then collects events from the user and informs the controller which then updates the model.
Having the DOM initiate, by ng-init, a change to the model is a violation of the ZEN of Angular.
It is a very good idea to decouple DOM manipulation from app logic. This dramatically improves the testability of the code. By coupling the app sequencing and logic to the ng-init directive, the code becomes fragile, difficult to test, hard to maintain, and error prone.
I have a chunk of html that is updated periodically from the controller. Here is the html:
<tr ng-repeat="product in products">
<td>{{product.name}}</td>
<td>{{product.price}}</td>
<td>{{product.description}}</td>
<td>{{product.quantity}}</td>
<td><input value="{{product.price}}"/></td>
</tr>
If a user changes the value though, I don't want it to be updated by the interval with the old data. Is there a way to only have it update product.price the first time?
Here is the relevant controller code:
$interval(function(){
DappStore.getProducts()
.then(function(data){
$scope.$apply(function(){
$scope.products = data;
});
},1000);
},1000);
One-Time-Binding is the Keyword.
One-time expressions will stop recalculating once they are stable,
which happens after the first digest if the expression result is a
non-undefined value.
All you have to do is to change your code like this:
<tr ng-repeat="product in ::products"> <!--HERE ARE THE CHANGE "::"-->
<td>{{product.name}}</td>
<td>{{product.price}}</td>
<td>{{product.description}}</td>
<td>{{product.quantity}}</td>
<td><input value="{{product.price}}"/></td>
</tr>
If I understand you correctly, you want to use one-way data binding instead of two-way data binding.
Simply use {{::product.name}} instead of {{name}}
For more info:
http://blog.thoughtram.io/angularjs/2014/10/14/exploring-angular-1.3-one-time-bindings.html
With this in the controller polling interval, only the newer values are created but anything in the page won't be touched.
$interval(function(){
DappStore.getProducts()
.then(function(data){
$scope.$apply(function(){
var products_length;
if(typeof $scope.products == 'undefined'){
products_length = 0;
$scope.products = [];
} else {
products_length = $scope.products.length;
}
for(var i = products_length; i < data.length; i++){
$scope.products[i] = data[i];
}
});
});
},1000);
I recently wrote a simple custom filter which only displays items in my model given a specific model property and it works great. It is below..
Filter
app.filter('status', function() {
return function(input, theStatus) {
var out = [];
for (var i = 0; i < input.length; i++) {
var widget = input[i];
if (widget.status === theStatus)
out.push(widget);
}
return out;
};
});
The filter is applied as such on an ng-repeat.
<tr ng-repeat="widget in pendingWidgets = (widgetList | status: 0)">
<td><span class="glyphicon glyphicon-usd" /></td>
<td><span class="glyphicon glyphicon-usd" /></td>
<td><span class="glyphicon glyphicon-usd" /></td>
<td><span class="glyphicon glyphicon-usd" /></td>
</tr>
And on a panel heading as so
<div class="panel-heading"><span class="badge">{{pendingWidgets.length}}</span></div>
When the glyph is clicked ng-click runs updateStatus() as below...
$scope.updateStatus = function(theId, newStatus) {
widgets.setStatus(tagNumber, newStatus);
$scope.displayAlert = true;
};
And the widget.setStatus() is as such..
app.factory('widgets', ['$http', function($http) {
var o = {
widgets:[]
};
o.setStatus = function(aWidget, theStatus) {
return $http.put('/widgets/' + aWidget, { 'status': theStatus }).success(function (data) {
// do I need to put something here?
});
};
return o;
}]);
My question lies in
How can I get my page to refresh on the ng-click action when the updateStatus() call is made on my model? When the glyph is clicked the model is updated but the page is not. Only on a page refresh or when I visit a different page and then come back does the page display the updated model accurately with respect to the custom filter.
It doesn't look like you're updating the status for a particular widget (on the client side). You're telling your server about the update, but on the client side, no update happens.
That's why when you refresh (i imagine you're loading the widgets from the db / backend) you see the update.
Where you have: // do I need to put something here? you need to do something like:
aWidget.status = data.status; // where data is the updated widget object
(this assumes that your backend is returning the updated widget - which if you're following the same conventions that I'm used to - it should be).
I am trying to load json data on the form which has two text fields. TextFields display correct price and qty from json. But, the problem is when user updates the price and qty, updated price and qty are not reflecting in controller method - $scope.total(). Here is my code
HTML file
<div ng-repeat="row in myData">
<input type="text" ng-model="row.price">
<input type="text" ng-model="row.qty">
</div>
JS file
$http.get('http call’).success(function(data){
$scope. myData = data
})
$scope.row = {
price : 10.0,
qty : 2;
};
$scope.total = function() {
console.log($scope.row.price);
console.log($scope.row.qty);
}
JSON - [{"price":10.50,"qty":3}]
Not sure what I am missing here? why updated values are not reflecting in controller?
Your $scope.total function needs to be called somewhere, and as mentioned by #pankajparkar, $scope.row is different than the row in your directive (which belongs to the ng-repeat's scope only).
Do this:
<div ng-repeat="row in myData">
<input type="text" ng-model="row.price" ng-change="total()">
<input type="text" ng-model="row.qty" ng-change="total()">
</div>
JS:
$http.get('http call').success(function(data){
$scope.myData = data;
});
$scope.total = function() {
console.log($scope.myData[0].price * $scope.myData[0].qty);
};
As your data is an array you loop througt is to get the total of all your objects:
$scope.total = function() {
var sum = 0;
myData.forEach(function(obj){
sum += obj.qty * obj.price
});
return sum;
}
and you need to call this function when values change.
Fiddle
With only one element in your array, your function total should be like #Joao answer:
$scope.total = function() {
return $scope.myData[0].price * $scope.myData[0].qty;
};
Fiddle
First thing if response is only one item then it should not be an array
It should be Single JSON such as {"price":10.50,"qty":3}
HTML Code
<input type="text" ng-model="myData.price">
<input type="text" ng-model="myData.qty">
<button type="button" ng-click="updateTotal()">
JS CODE
$http.get('http call').success(function(data){
$scope.myData = data;
});
$scope.total = function(){
//now here you will get updated scope values
console.log($scope.myData.price * $scope.myData.qty);
}
Hope this will be help full to you.
$scope.total is a function, but it is not returning anything.
instead of:
var total = $scope.row.price * $scope.row.qty
try:
return $scope.row.price * $scope.row.qty