Model not updating after REST API call - angularjs

I'm new to AngularJS and have been playing around with it to get to grips with it. I've been getting to grips with $scope within a controller and how its used as the context between the view and the model. I'm also getting to grips with Promises and async web calls.
However, I've reworked an incredibly simple project to use this instead of $scope within the controller and not pass in the $scope object.
However, where I am making an async call to fetch a JSON file to load the variable usdToForeignRates, the view never seems to update. All the other properties work. Any ideas what I'm doing wrong?
NOTE: I can get the view to update, if I change this.usdToForeignRates to be $scope.usdToForeignRates, pass in the scope to the controller and then just change index.html to reference usdToForeignRates rather than invoice.usdToForeignRates.
controllers.js:-
var financeApp = angular.module('financeApp', []);
financeApp.controller('invoiceController', ['$http', function($http) {
this.qty = 1;
this.cost = 2;
this.inCurr = 'EUR';
this.currencies = ['USD', 'EUR', 'CNY'];
this.usdToForeignRates = [];
this.counter = 1;
loadRates();
function loadRates(){
$http.get('rates/rates.json').then(function(rates)
{
this.usdToForeignRates = rates.data;
});};
this.add = function () {
this.counter = this.counter + 1;
};
this.getRate = function getRate(curr){
return this.usdToForeignRates[curr];
};
this.total = function total(outCurr) {
var result = this.qty * this.cost * this.usdToForeignRates[outCurr] / this.usdToForeignRates[$scope.inCurr];
return result;
};
this.pay = function pay() {
window.alert("Paid!");
};
}]);
index.html:-
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<script src="controllers.js"></script>
<meta charset="UTF-8">
<title>Currency Service</title>
</head>
<body>
<div ng-app="financeApp" ng-controller="invoiceController as invoice">
<div>
<select ng-model="invoice.inCurr">
<option ng-repeat="c in invoice.currencies">{{c}}</option>
</select>
</div>
<ul>
<li ng-repeat="c in invoice.currencies">{{c + ' ' + getRate(c) + 'rate'}}</li>
</ul>
<ul>
<li ng-repeat="r in invoice.usdToForeignRates">{{r}}</li>
</ul>
<button ng-click="invoice.add()">Add</button>
<div ng-model="invoice.counter">{{invoice.counter}}</div>
</div>
</body>
</html>

This in the then function is the http promise context. You need a reference to the controller 'this'. Try
var self = this;
function loadRates(){
$http.get('rates/rates.json').then(function(rates)
{
self.usdToForeignRates = rates.data;
});};

Related

Angular variable are not refreshing on page when fetching data from google script

Doing one Google Script APP. With server side method that returns array of string.
getClassRoomList().
Can you please suggest what is wrong with my current HTML? As the success handler is running all well on response. But the ng variable message is not reflecting on page; while the jQuery does populate the table.
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.js"></script>
<script>
var app = angular.module('myApp',[]);
app.controller('mainCtrl',function($scope){
$scope.message = [];
$scope.populateTable = function(array){
//Setting ng variable; but the page doesn't show anything
$scope.message = array;
//Setting the Table by JQuery; it does work.
var table = $('#myTable');
table.empty();
for (var i = 0; i < array.length; i++) {
var item = '<tr><td><div class="classname">' + array[i] +'</div></td></tr>';
table.append(item);
}
};
$scope.mainClick = function(){
$scope.message = $scope.message + 'chirag';
google.script.run.withSuccessHandler($scope.populateTable).getClassRoomList();
};
});
</script>
</head>
<body ng-controller="mainCtrl">
<button ng-click="mainClick()">Proceed</button>
<table id="myTable"></table>
<div ng-bind="message"></div>
</body>
</html>
This works. Thanks. google.script.run.withSuccessHandler((e) => {$scope.populateTable(e); $scope.$apply();}).getClassRoomList();

Adding object to model, doesn't update dom

I have a method that creates an object and pushes it to an array
$scope.contracts = [];
$scope.addContract = function () {
var contract = {
...
}
$scope.contracts.push(contract);
console.log($scope.contracts);
}
now in my DOM, i have the following (merely for debugging)
{{contracts}}
But this doesn't update. I validate in the console, that the object is in the array.
Why doesn't the model update?
I've already tried various applications of $scope.$apply, but they all result in an
$apply already in progress
Something must be wrong with your code, check your DOM. Does anything happen when you call your function? Heres is a working example:
var app = angular.module("myApp",[]);
app.controller("test", function($scope){
$scope.contracts = [];
$scope.addContract = function () {
var contract = {
"con":"tract"
}
$scope.contracts.push(contract);
console.log($scope.contracts);
}
});
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="test">
{{contracts}}
<button ng-click="addContract()">Add contract</button>
</body>
</html>

(newbie) Get data from array of objects using ng-repeat and for loop

I have to access some objects inside an array:
.success(function(data) {
$scope.activities = data.result[0].attributes
});
And I can access this particular index, as expected, inside my view with
<div ng-repeat="x in activities">
<p>{{x.name}}: {{x.value}}</p>
</div>
Now obviously, this only returns the first object in the array, at index 0. (Right?). So I figure I have to somehow loop the function..
.success(function(data) {
for(var i = 0; i < data.result.length; i++) {
$scope.activities = data.result[i].attributes;
}
});
As far as I can tell, the actual for loop is working.. but I need help in the next step exposing this to my view.
Here is my plunker: https://plnkr.co/edit/2ZukY3Oq8vYvghfCruHx?p=preview
(although the data is not available, I have added what the response looks like in the comments)
Well, since the JSON data you pasted in plnkr is invalid, I don't know what attributes means, if it's just an object or it's an array of objects, anyway I made it as a single object.
EDIT
Since now I know attributes is an Array of objects, you can achieve what you want using special repeats.
Here's a snippet working:
(function() {
"use strict";
angular.module('app', [])
.controller('mainCtrl', function($scope) {
$scope.getLeadActivities = function() {
var url = 'xxxxxxxx.mktorest.com';
var endpoint = '/rest/v1/activities.json';
var activityTypeIds = '46';
var access_token = $scope.access_token;
var leadIds = $scope.leadId;
var nextPageToken = $scope.pagingToken;
// This is your data from API
var data = {
"requestId":"14213#155c8de2578",
"success":true,
"nextPageToken":"EOXAPD2V5UOJ5B3S5GGP7NUCX6UI6BFXCWHPJXF245PN2QTGMF3Q====",
"moreResult":false,
"result":[
{
"id":75843491,
"leadId":5334578,
"activityDate":"2016-07-06T06:45:11Z",
"activityTypeId":46,
"primaryAttributeValue":"Web",
"attributes":[
{
"name":"myDataName",
"value":"myDataValue"
}
]
},
{
"id":75843491,
"leadId":5334578,
"activityDate":"2016-07-06T06:45:11Z",
"activityTypeId":46,
"primaryAttributeValue":"Web",
"attributes":[
{
"name":"myDataName2",
"value":"myDataValue2"
}
]
},
{
"id":75843491,
"leadId":5334578,
"activityDate":"2016-07-06T06:45:11Z",
"activityTypeId":46,
"primaryAttributeValue":"Web",
"attributes":[
{
"name":"myDataName3",
"value":"myDataValue3"
}
]
}
]
};
// You'll put it inside the THEN method of your $http.get
$scope.activities = data.result;
}
});
})();
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
</head>
<body ng-controller="mainCtrl">
<button ng-click="getLeadActivities()">Get Lead Activities</button>
<div ng-repeat-start="activity in activities"></div>
<ul ng-repeat-end ng-repeat="attr in activity.attributes">
<li ng-bind="attr.name + ': ' + attr.value"></li>
</ul>
</body>
</html>
As per my understanding of your issue, you can solve it by using two ng-repeat clauses.
Your controller code,
.success(function(data) {
$scope.ativitiesArray = data.result;
});
Your html code,
<div ng-repeat="activities in activitiesArray track by $index">
<div ng-repeat="x in activities.attributes track by $index">
<p>{{x.name}}: {{x.value}}</p>
</div>
</div>
I've added track by $index phrase to ng-repeat as it will increase performance. Also, as per suggestions in the comments, please avoid using .success(). It is recommended to use .then().
Hope this solves the issue.

How to set first item as default select

I am new to angular.js. I am using list of users with sorter list, when I click the user name the selected user phone number should display in the selected area. It is working fine.
My question is how to I set the first user as default select. here is my sample code. Please help me on this.
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Example</title>
<script src="js/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<h1>Selected View</h1>
<ul>
<li ng-repeat="userNames in users | orderBy:orderProp:direction" ng:click="select(userNames)">{{userNames.name}}</li>
</ul>
<p>selected: {{selectedUser.phone}}</p>
<script>
var myApp = angular.module('myApp', []);
//myApp.by.id('setbtn')element('h1').addClass('active');
myApp.controller('MainCtrl', ['$scope', function ($scope) {
$scope.users = [{name:'John', phone:'555-1276'},
{name:'John', phone:'555-1278'},
{name:'Mary', phone:'800-BIG-MARY'},
{name:'Mike', phone:'555-4321'},
{name:'Adam', phone:'555-5678'},
{name:'Julie', phone:'555-8765'},
{name:'Juliette', phone:'555-5678'}];
//sorting
$scope.direction = false;
$scope.orderProp = "name";
$scope.sort = function(column) {
if ($scope.orderProp === column) {
$scope.direction = !$scope.direction;
} else {
$scope.orderProp = column;
$scope.direction = false;
}
};
//selected list
$scope.select = function(phone) {
$scope.selectedUser = phone;
};
}]);
</script>
</body>
</html>
Just use this in the controller to sets the default user's phone number
$scope.selectedUser = $scope.users[0];
Since the users in a JS Object, instead of a native datatype, the initial selection often causes a problem.
You'll be better off to refactor the select options to use an array of strings rather than an array of Objects.
It is covered in detail in this post.
Hope this helps!
You can sort users from controller instead of html
HTML
<li ng-repeat="userNames in getSortedUsers()"
ng:click="select(userNames)">{{userNames.name}}</li>
Controller
$scope.getSortedUsers = function () {
// Use $filter service. You have to add $filter as dependencies in controller
var users = $filter('orderBy')($scope.users, $scope.orderProp +
':' + $scope.direction);
// Set first user as selected user
$scope.selectedUser = users[0];
return users;
}

ng-options displays blank selected option, even though ng-model is defined

I have created a plunkr to emphasize the problem, perhaps it's because the source of the ng-repeat is a function, I am not sure, but so far I've tried everything in order to solve this, and couldn't mange.
plunkr:
http://plnkr.co/edit/qQFsRM?p=preview
HTML
<html>
<head>
<script data-require="angular.js#1.2.0-rc1" data-semver="1.2.0-rc1" src="http://code.angularjs.org/1.2.0rc1/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app='myApp' ng-controller='mainCtrl'>
<ng-include src="'menu.html'">
</ng-include>
</html>
Script
var app = angular.module('myApp', []);
app.controller('mainCtrl', function($scope, $httpBackend){
$scope.model = {};
$scope.model.myJobs = {};
$scope.refreshJobs = function(){
}
});
app.controller('menuCtrl', function($scope){
$scope.model.locations = function(){
var loc = [];
loc[1] = 'Dublin';
loc[2] = 'Stockholm';
loc[3] = 'New Jersy';
$scope.model.selectedLocationDef = loc.indexOf('Dublin');
return loc;
}
$scope.model.selectedLocation = $scope.model.selectedLocationDef;
$scope.$watch('model.selectedLocation', function(location){
$scope.refreshJobs(location);
});
});
If you use an array as model, then the model is a string not a number. So you need to convert the number to string. Just try
$scope.model.selectedLocation = '1';
Last I checked, Angular does not support the ability to bind array keys to ng-model via ng-options. You can, however, mimic this behavior using an object hash:
menu.html:
<div ng-controller="menuCtrl">
<select ng-model="model.selectedLocation" ng-options="x.value as x.label for x in model.locations()">
</select>
</div>
script.js:
$scope.model.locations = function(){
var loc = [{
value: 0,
label: 'Dublin'
}, {
value: 1,
label: 'Stockholm'
}, {
value: 2,
label: 'New Jersey'
}];
$scope.model.selectedLocation = 1; // Set default value
return loc;
}
Bear in mind that this will bind the integers to your model, and not the cities themselves. If you want your model value to be Dublin, Stockholm, or New Jersey, simply do:
menu.html:
<div ng-controller="menuCtrl">
<select ng-model="model.selectedLocation" ng-options="name for name in model.locations()">
</select>
</div>
script.js:
$scope.model.locations = function(){
var loc = ['Dublin', 'Stockholm', 'New Jersey'];
$scope.model.selectedLocation = 'Dublin'; // Set default value
return loc;
}

Resources