I have a select list that needs to default to a certain value based on an object from an http response:
<select ng-model="isRentVals.cur" ng-options="v for v in isRentVals.values"></select>
The object from the response is called my_property:
function editPropController($scope, $http) {
$scope.my_property;
$http.get('/api/propertyById/' + $scope.prop_id)
.success(function(properties) {
$scope.my_property = properties[0];
$scope.isRentVals = { "cur" : $scope.my_property.is_rented, "values" : ["true", "false"]};
})
.error(function(err) {
alert('We got an error: ' + err);
});
The response from the $http call will come after the select element is bound to isRentVals.cur. isRentVals is not defined yet (will be defined in in the success callback in some milliseconds later) angular inserts an empty val then binds. How can I work around this?
The value is defaulted to blank and I have to reset my_property.is_rented in an ng_click method to save the new values.
The response for your $http call will come after your select element is bound to isRentVals.cur. Because isRentVals is not defined yet (will be defined in in your success call back in some milliseconds later) then angular insert an empty item to your select element to be able to bind to current value of isRentVals.cur. Also for better answers you should create a plunker.
Initialize isRentVals like this:
$scope.isRentVals = {}; //initialize as object
and overwrite the model when the data loaded:
function editPropController($scope, $http) {
$scope.my_property;
$scope.isRentVals = {};
$http.get('/api/propertyById/' + $scope.prop_id)
.success(function(properties) {
$scope.my_property = properties[0];
$scope.isRentVals = { "cur" : $scope.my_property.is_rented, "values" : ["true", "false"]};
})
.error(function(err) {
alert('We got an error: ' + err);
});
Related
I need to get an attribute value ("val_index") of an entity which is selected in lookup.
function onLookupChange(){
var entityName, entityId, entityLabel, lookupFieldObject;
lookupFieldObject = Xrm.Page.data.entity.attributes.get('my_attribute');
if (lookupFieldObject.getValue() != null) {
entityId = lookupFieldObject.getValue()[0].id;
entityName = lookupFieldObject.getValue()[0].entityType;
entityLabel = lookupFieldObject.getValue()[0].name;
}
// here I need to get an attribute value of a selected entity. Attribute's name is "val_index"
}
How can I do that?
Use the SDK.REST.js library which ships with the CRM SDK to do this. Include this as a script in your form entity and you can reference the functions to make REST calls.
An example call might look like this:
// Assume we are working with the account entity.
// This call is asynchronous.
SDK.REST.retrieveRecord(entityId, "Account", "val_index", null,
// Success.
function (result) {
var value = result.val_index;
// Do something.
},
// Error retrieving the value.
function (error) {
// Notify the user...
});
How to I get angular to retain data updates returned in the response to a PUT?
I have a basic angular (vsn 1.2.26) RESTful app that successfully retrieves, updates and writes data back to the server. The server alters the "updateTime" field of the updated record and returns it in the (200) response to the browser. I can see the updated value in the $resource.save callback function, but I can't figure how to persist it to the $scope beyond the duration of the callback to make it visible in the UI.
angular.module('myResources',['ngResource'])
.factory('Fund',['$resource',function($resource)
{
return $resource('http://myhost/xyz/fund/:id'
,{ id: '#guid' }
,{ save: { method: 'PUT' }
,query: {method: 'GET', isArray: false }
}
);
}])
...
$scope.selectRecord = function(R)
{
...
$scope.record = R;
}
$scope.saveRecordChanges = function()
{
...
myFundResource.save($scope.record,function(response){
$scope.record = angular.fromJson(response); // gets refreshed data but doesn't update UI
console.log("new updateTime=" + $scope.record.updateTime); // correctly displays new value in the log
});
}
You can add this to your save callback function:
$scope.$apply();
Or you can change you function into:
$scope.saveRecordChanges= function(){
myFundResponse.save($scope.record).$promise.then(function(response){
$scope.record = response;
});
};
Cuz in angular $promise, it will invoke $asyncEval to call your success or error callback, which means with angular $promise will update the view automatically.
My error was that I was expecting the ng-repeat record list to update - but I wasn't updating the list, only redirecting the $scope.record (current record) pointer to a new object. I added code to update the record list:
myFundResource.save($scope.record,function(response)
{
var vIdx = $scope.allRecords.indexOf($scope.record); // allRecords is bound to the UI table via ng-repeat
$scope.record = response; // ** $scope.record no longer points to the same object as $scope.allRecords[vIdx] - array is unaware of this change
$scope.allRecords[vIdx] = $scope.record; // replace array element with new object
});
Thank you to #Tyler.z.yang for your insights and questions that got me thinking in the right direction.
I am writing an application where I need to display car inventory. I ping an API to get all cars matching search criteria such as Car Make, Model and Year. I need to display an image of each car along with the other information. Once the JSON data is available, it also has an ID (StyleID) for each car in my results that I need to use to make another API call to request images for that car.
After reading a few articles (such as this one) I figured I need to use a custom directive in order to query and insert each car's image in a specific spot when looping over the results.
I read this custom directive tutorial by Jim Lavin to create my sample. I was hoping that this approach will work however I must be missing something as it simply doesn't execute my custom directive and display the car image as I want it to.
Can someone please help?
Here's the plunker that shows my code:
http://plnkr.co/edit/5DqAspT92RUPd1UmCIpn?p=preview
Here's the information about the specific media call to Edmunds API that I am trying to use.
And here's the URL to the media endpoint
Repeating my code :
My HTML code :
<div firstImageOfMyCar data-styleid="style.id"></div>
or
<firstImageOfMyCar data-styleid="style.id"></firstImageOfMyCar>
And here's my custom directive:
// Custom Directive to get first image of each car.
app.directive('firstImageOfMyCar', function() {
return {
restrict: "E",
link: function(scope, elm, attrs) {
// by default the values will come in as undefined so we need to setup a
// watch to notify us when the value changes
scope.$watch(attrs.styleid, function(value) {
//elm.text(value);
// let's do nothing if the value comes in empty, null or undefined
if ((value !== null) && (value !== undefined) && (value !== '')) {
// get the photos for the specified car using the styleID.
// This returns a collection of photos in photoSrcs.
$http.get('https://api.edmunds.com/v1/api/vehiclephoto/service/findphotosbystyleid?styleId=' + value + '&fmt=json&api_key=mexvxqeke9qmhhawsfy8j9qd')
.then(function(response) {
$scope.photoSrcs = response.photoSrcs;
// construct the tag to insert into the element.
var tag = '<img alt="" src="http://media.ed.edmunds-media.com' + response.photoSrcs[0] + '" />" />'
// insert the tag into the element
elm.append(tag);
}, function(error) {
$scope.error3 = JSON.stringify(error);
});
}
});
}
};
});
Angular normalizes an element's tag and attribute name to determine which elements match which directives. We typically refer to directives by their case-sensitive camelCase normalized name (e.g. ngModel). However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case forms, typically using dash-delimited attributes on DOM elements (e.g. ng-model).
Try
<div first-image-of-my-car data-styleid="style.id"></div>
or
<first-image-of-my-car data-styleid="style.id"></first-image-of-my-car>
Note: if you use the first, with the attribute, you will need to change the restrict in the directive to restrict: "A", (or "AE" to cover both cases)
Also, $http, and$scope are not defined in your directive. You can simply add $http to the directive function and DI will inject it. You probably want to use scope instead of $scope.
There were also some other things wrong with the example provided. Here is a working version: http://plnkr.co/edit/re30Xu0bA1XrsM0VZKbX?p=preview
Note that $http's .then() will call the provided function with data, status, headers, config, data will have the response you are looking for. (response.data[0].photoSrcs[0])
Please look at the answer by #TheScharpieOne. But i also played around with your code and api. And I would like to add, that your code might benefit from using angular services to wrap the api calls.
Here is an Example for a service:
app.service('VehicleService', function ($q, $http) {
this.getAllMakes = function () {
var deferred = $q.defer();
var url = 'https://api.edmunds.com/api/vehicle/v2/makes?state=new&view=basic&fmt=json&api_key=mexvxqeke9qmhhawsfy8j9qd'
$http.get(url).then(function (response) {
deferred.resolve(response.data.makes);
}, function (error) {
deferred.reject(new Error(JSON.stringify(error)));
});
return deferred.promise;
}
this.getCar = function (makeName, modelName, year) {
var deferred = $q.defer();
var url = 'https://api.edmunds.com/api/vehicle/v2/' + makeName + '/' + modelName + '/' + year + '?category=Sedan&view=full&fmt=json&api_key=mexvxqeke9qmhhawsfy8j9qd'
$http.get(url).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.reject(new Error(JSON.stringify(error)));
});
return deferred.promise;
};
});
You could use it like this:
function CarCtrl($scope, VehicleService, VehiclePhotoService) {
// init make select
VehicleService.getAllMakes().then(function (value) {
$scope.makes = value;
});
$scope.getCars = function () {
VehicleService.getCar($scope.make.niceName, $scope.model.niceName, $scope.year.year)
.then(function (value) {
console.log(value);
$scope.myCars = value;
})
}
}
Here is a complete working jsfiddle: http://jsfiddle.net/gkLbh8og/
Thanks to #asgoth, I am able to use AngularJS $http service to retrieve stock prices from Yahoo as described here: Cannot read response from AngularJS $resource JSONP get from Yahoo Finance
In the "getHistoricalPrice" function, it puts the price inside an array, which is inside an object. From inside that function, I am able to access the price and write it to console.
The function returns the object to where it is called from. From there, I can successfully write the entire object out to console. However, I cannot access the elements of this object. I tried many different ways, but still cannot access the data in the object. You can see the code at http://jsfiddle.net/curt00/LTazR/2/ or below:
angular.module('app', ['ngResource']);
function AppCtrl($scope, $http, $resource) {
var historical_price = getHistoricalPrice("AAPL", 'start date is hard coded', 'end date is hard coded');
console.log("after calling historical price: ", historical_price); // historical_price is an object and all of the correct data is outputted to console here, but I cannot access its elements directly from Javascript.
for(var key in historical_price) {
console.log("key =",key); // this outputs "key = list"
}
console.log("after calling getHistoricalPrice: ", historical_price.list[0][1]); // Cannot access this as browser console gives error: TypeError: Cannot read property '1' of undefined
console.log("after calling getHistoricalPrice: ", historical_price['list'][0][1]); // Cannot access this as browser console gives error: TypeError: Cannot read property '1' of undefined
console.log("after calling getHistoricalPrice: ", historical_price[0][1]); // Cannot access this as browser console gives error: TypeError: Cannot read property '1' of undefined
function getHistoricalPrice(symbol, start, end) {
var query = 'select * from csv where url=\'http://ichart.yahoo.com/table.csv?s=' + symbol + '&a=' + '11' + '&b=' + '19' + '&c=' + '2012' + '&d=' + '11' + '&e=' + '19' + '&f=' + '2012' + '&g=d&ignore=.csv\'';
var url = 'http://query.yahooapis.com/v1/public/yql?q=' + fixedEncodeURIComponent(query) + '&format=json&callback=JSON_CALLBACK';
var histData = {};
$http.jsonp(url, {timeout: 30000}).success(function(json) {
var list = [];
var result = json.query.results.row;
result.shift(); // remove the header (columns) row
angular.forEach(result, function(row) {
list.push([(new Date(row.col0)).getTime()/1000, parseFloat(row.col4)]);
});
list.sort(function(val1, val2) {
return val1[0] - val2[0];
});
histData.list = list;
console.log('Loaded historical data',histData.list[0][1],', for ' + symbol); // This works and gives the price
});
return histData;
}
var fixedEncodeURIComponent = function(str) {
return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\*/g, "%2A");
};
}
Any help or suggestions to solve this problem is greatly appreciate!
It's a matter of timing.
In lines 12-14 you are trying to access histData.list before it has been populated. This is because this code is run before the success callback to the $http.jsonp function is executed.
Any code that depends on that callback being completed must be in the callback or in a function called in the callback.
See my answer on https://stackoverflow.com/a/13967709/1916258
A great way to debug the Yahoo api is using the YQL Console: http://developer.yahoo.com/yql/console/
Info about the different posibilities (which stock info) can be found on http://www.gummy-stuff.org/Yahoo-data.htm
Edit: there was still a problem with function fixedEncodeURIComponent. It should encode quotes (") too:
var fixedEncodeURIComponent = function(str) {
return encodeURIComponent(str).replace(/[!'()]/g, escape).replace(/\*/g, "%2A").replace(/\"/g, "%22");
};
BobS is right, you aren't timing things correctly. Also you declared fixedEncodeURIComponent after you had called it. This was resulting in an immediate error when I loaded up the jsfiddle.
While you were passing the callback through to your function correctly, you weren't actually calling it. I stripped out all the post processing of the json as you have some other errors involving the query and just implemented the callback so you can see it working.
After the request is finished and you're still in the success function you need to add
if(typeof(callback) === "function"){
callback();
}
This calls that function you passed in and runs it. Here is a working jsFiddle of it:
http://jsfiddle.net/LTazR/22/
I also updated a new variable i created call output so you can see it changing.
Thanks to everybody for providing suggestions.
I solved the problem by using AngularJS' $scope variable, such as $scope.symbol[user].price. I created this variable before calling the getHistoricalPrice function and then in that function, after the result is returned from $http.jsonp, I put the value into the $scope variable, as such:
$scope.symbol[user].price = row.col4;
In my app, I have to fetch some JSON data and assign it to an array before the page is loaded. This is my code for fetching the JSON using the CardService service:
cards = [];
var cs = {
...
fetchCards: function() {
var d = $q.defer();
$http.get("data/cards.php").success(function(data) {
cards = data;
d.resolve();
}).error(function(data, status) {
d.reject(status);
});
return d.promise;
},
getCards: function() { return cards; };
...
}
In the controller's resolve block, I have the following:
WalletController.resolve = {
getCards: function(CardService) {
CardService.fetchCards().then(loadView, showError);
}
}
And in the actual controller, I have the following:
function WalletController($scope, CardService) {
$scope.cards = CardService.getCards();
}
The problem is, the fetchCards function in the service seems to resolve the promise before the JSON data is assigned to the cards variable. This leads to my view loading with blank data until I refresh a couple times and get lucky.
I can confirm the late loading as when I log the cards variable in the console, I get an empty array at line 122 (when my view is loaded) and a full array at line 57 (when the JSON call is successful). Line 57's code somehow executes after the view is loaded.
How do I fix this?
I haven't used resolve but I'm throwing this out there just in case the issue you are having is related to binding to an array returned from a service.
If you are returning your cards array from a service and binding to it in the UI you may want to try to populate that same array instead of setting cards = data; (which will overwrite the local cards with a new array which is not bound to the UI).
Something like:
fetchCards: function() {
var d = $q.defer();
$http.get("data/cards.php").success(function(data) {
cards.length = 0;
for(var i = 0; i < data.length; i++){
cards.push(data[i]);
}
d.resolve();
}).error(function(data, status) {
d.reject(status);
});
return d.promise;
},
See this fiddle for a working example of what I'm trying to describe. Clicking the first button multiple times will update the view but once you click on the second button the binding will be broken.
The main difference between the two is:
First button uses data.length = 0 and data.push() to retain the original array's reference
Second button overwrites the original data array reference with a new one using data = newArray
Update: Also, as Mark Rajcok, mentioned below you can use angular.copy to retain the original array's reference by emptying it out and adding new ones from the source like this:
fetchCards: function() {
var d = $q.defer();
$http.get("data/cards.php").success(function(data) {
angular.copy(data, cards);
d.resolve();
}).error(function(data, status) {
d.reject(status);
});
return d.promise;
},