How to access the data after post in Angularjs - angularjs

My Viewer
<button type="submit" ng:click="sendviewer(vl.viewerid)" ></button>
My Controller
$scope.viewerDetails = [];
var viewerid = [];
$scope.sendviewer = function(viewer){
var success = function(data)
{
for( var i = 0; i < 1; i++ )
{
var obj = data[i];
if(obj.viewerid != undefined) {
viewerid.push(obj.viewerid);
}
}
$scope.viewerDetails = viewerid;
}
viewerService.postViewer(viewer).success(success);
}
My Service
return {
postViewer: function(viewer){
$location.path('/viewerdetails');
return $http({
method: 'POST',
url: 'api.php',
data: {
'view': viewer
},
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
});
}
}
My returned data is available in function memory but how will i be able to access that data outside the function to be accessible inside my view? if i try to do {{ viewerDetails.viewerid }} will display blank.

Well, it looks like the for-loop is just unnecessary, kind of weird actually.
$scope.viewerIds = [];
$scope.sendviewer = function(viewer){
var success = function(data)
{
var obj = data[0];
if(obj.viewerid != undefined) {
$scope.viewerIds.push(obj.viewerid);
}
}
viewerService.postViewer(viewer).then(success);
}
also you should not use success since it is deprecated. Use promise syntax with then instead.
This would add the id of the result of the call to the $scope.viewerIds array.
It is not entierly clear if this is what you want. but to me it looks like it.
Are you sure the data is like you expect it? an array where the first element is your object. This is also kind of weird api to be honest..
and then in your html you could perhaps loop the array to show the ids:
<div ng-repeat="id in viewerIds"> ID: {{id}}</div>

Since viewerid is a local array variable, the data stored inside it is not available outside the scope. You should store viewerid inside $scope. Like
$scope.viewerid = [];
Now it can be accessed on the DOM.

Related

angular grep or filter to get an object given an ID

I have a json array of Category objects from my database. (I am storing it in vm.data in my controller, which is bound to an angular repeater.)
Now I need to go back and get the SubCats in each category using the 'Category' field in each SubCat.
Here is my generic data getter:
function getData(params, callback) {
$http({
method: 'GET',
url: 'database.php',
params: params
}).then(function successCallback(response) {
callback(response.data);
});
}
So, to get my Categories, I do:
function getCats() {
var params = {};
params.table = "Categories";
getData(params,buildCats);
};
which calls back to
function buildCats(data) {
vm.data = data;
$.each(vm.data,function(index,value){
getSubCat(value.uID);
});
};
then, one by one it gets the SubCats:
function getSubCat(categoryId) {
var params = {};
params.table = "SubCats";
params.where_field = "Category";
params.where_value = categoryId;
getData(params, buildSubCat);
};
which calls back to this: (I no longer have the Category Id, so I have to go looking for it in my SubCat data)
function buildSubCat(data) {
var categoryId = data[0].Category;
$filter('filter')(vm.data, function(el){
el.uID==categoryId;
}, true).SubCats = data;
}
and it is here I am running into trouble.
I need to get hold of the unique Category object in vm.data - the actual object, not a copy of it - whose uID equals the ID of the SubCats I am currently processing, then ADD the current SubCat object to it. The above does not work. At least, I never see the SubCats within the Categories in vm.data.
I tried to pull it apart, like this:
var foo = $filter('filter')(vm.data, function(el){
el.uID==categoryId;
}, true);
foo.SubCats = data;
but foo is new instance of the Category object, not a reference to the one in vm.data, so my adding is not affecting vm.data.
I suppose I could try to replace vm.data[n] with foo, but this seems a poor practice.
How can I asynchronously retrieve the Categories and SubCats and combine them?
Something tickling the back of my brain tells me I should be passing the actual Category object into the getData, so it is directly available in the callback, but I'm not sure if that makes sense.
OK, I figured out a solution, just not sure if it's the best.
I accept a third param to pass the target object into the getData function:
function getData(params, target, callback) {
$http({
method: 'GET',
url: 'index.php',
params: params
}).then(function successCallback(response) {
callback(target, response.data);
});
}
In my getCats, I pass vm.data as the target param (though there is no need to, as will be seen in a moment).
function getCats() {
var params = {};
params.table = "Categories";
params.sort_field = "Order";
params.sort_dir = "ASC";
getData(params, vm.data, buildCats);
};
Now, I should be able to do this:
function buildCats(target, data) {
target = data;
$.each(target,function(index, catObj){
getSubCat(catObj);
});
};
But for some reason it does not work. target seems to have lost its reference to vm.data. Bonus points if you can figure out what went wrong.
But that's OK - I don't need the target - I can assign the results directly to vm.data:
function buildCats(target, data) {
vm.data = data;
$.each(vm.data,function(index, catObj){
getSubCat(catObj);
});
};
I can now pass the actual Category object itself to the getSubCat call:
function getSubCat(categoryObject) {
var params = {};
params.table = "SubCats";
params.where_field = "Category";
params.where_value = categoryObject.uID;
params.sort_field = "Order";
params.sort_dir = "ASC";
getData(params, categoryObject, buildSubCat);
};
which means I now have the actual object in the callback:
function buildSubCat(categoryObject, data) {
categoryObject.SubCats = data;
};
And my HTML looks like this:
<div class="category" ng-repeat="cat in vm.data">
<h2>{{cat.Name}}</h2>
<div class="subcategory" ng-repeat="SubCat in cat.SubCats">
<h3>{{SubCat.Name}}</h3>
</div>
</div>

Is there a way to refresh virtual repeat container?

I've got a md-virtual-repeat-container that handles the data from an API call triggered by a search box. I'd like to be able to refresh this container when a user enters a second search query.
I'm using a setup similar to this plnkr which is from this question:. The only difference is its getting data from a server when the user enters a search term.
My Question
Is there a way to trigger a refresh an md-virtual-repeat-container?
I don't think the code is relevant but here's my (simplified) production code:
var self = this;
self.infiniteItems = {
numLoaded_: 0,
toLoad_: 0,
items: [],
getItemAtIndex: function (index) {
if (index > this.numLoaded_) {
this.fetchMoreItems_(index);
return null;
}
return this.items[index];
},
getLength: function() {
return this.numLoaded_ + 25;
},
fetchMoreItems_: function (index) {
if (this.toLoad_ < index) {
this.toLoad_ += 5;
var offset = 0;
$http({
method: 'GET',
datatype: 'json',
url: '{my-api-call}',
contentType: "application/json; charset=utf-8",
cache: false,
params: {
param: query,
page: offset
}
}).then(angular.bind(this, function (obj) {
this.items = this.items.concat(obj.data.SearchResults);
this.numLoaded_ = this.toLoad_;
this.offset++;
$scope.searchResults = obj.data.SearchResults;
}));
}
}
};
You can force a redraw of your md-virtual-repeat container by triggering a fake window resize event (this causes the directive to call its private handleScroll_() method which then calls containerUpdated()).
This will trigger a fake window resize event:
angular.element(window).triggerHandler('resize');
You can also use this to cause refresh of items in specific scope and not in the entire document:
scope.$broadcast('$md-resize')
Resetting the model should also work.
In your case you could a have a new function on infiniteItems:
refresh : function() {
this.numLoaded_= 0;
this.toLoad_ = 0;
this.items = [];
}
I'm going to post my solution since there isn't an answer yet. If an better answer gets posted I'll gladly accept it.
First, I migrated all of my search result code into it's own template, directive and controller.
Next, I added an empty container in place of my logic. I'll use this as a place to dump my directive dynamically every time I search.
`<div id="search-result-container"></div>`
Finally, I dynamically appended my new directive like this
$scope.handleHotlineSearchClick = function () {
var query = $('#legal-search-input').val();
if (query != "") {
$scope.searchLoaded = false;
$('#search-result-container').empty().append($compile("<search-result></search-result>")($scope));
}
};
This way every time the user enters a new search term the directive gets reloaded.

How do I handle promises inside a filter?

I want to create a filter that converts money to another currency, for example Japanese Yen to USD and I want it to be as simple as:
{{ myMoneyInYen | usd }}
My problem is, I am fetching the values used for conversion from an API, but I can't seem to make it work. It's just blank. It would have been really nice if I could that. You guys think I should turn it into a directive instead?
module.filter('svcData', ['promisedSvc', function(promisedSvc) {
return function(input) {
promisedSvc.then(function(svc) {
var valuesUsedForConversion = svc.getData(input);
//DO actual conversion here.
return convertedValue;
});
};
}]);
You should probably set the resulting data into a scope accessible variable when resolving your promise, and filter this variable instead.
You could use a stateful filter:
angular.module("app").filter('svcData', function($http) {
var cached = {};
var apiUrl = 'http://my.service.com';
function svcDataFilter(data_id, data_prop) {
if (data_id) {
if (data_id in cached) {
// avoid returning a promise!
return typeof cached[data_id].then !== 'function' ?
cached[data_id][data_prop] : undefined;
} else {
cached[data_id] = $http({
method: 'GET',
url: apiUrl + data_id
}).success(function (data) {
cached[data_id] = data;
});
}
}
}
svcDataFilterFilter.$stateful = true;
return svcDataFilterFilter;
})
and use it like so: {{data_id |svcData:'property'}}
Beware: The function svcDataFilter will be called on each digest cycle.
Also, you would need to figure out a way to reset the cache if it grows too big.
See: https://glebbahmutov.com/blog/async-angular-filter/
And the plunker (the link above won't display it, so here's a direct link): http://plnkr.co/edit/EK2TYI1NZevojOFDpaOG?p=preview

Create an angular filter that returns remote data?

I have a snippet of code that I use very often, that calls an API in order to return a user name based on an ID sent over to it.
GetUserNames.get({users: user}, function (data) {
$scope.userName = data[0];
})
What I would like to do is turn this into a filter where I can do something like
{{userID | returnUserName}}
so that I can reuse and keep my controllers and my views clean. I wrote something like this:
angular.module('app.filters').filter('returnUserName', function(GetUserNames) {
return function(input) {
GetUserNames.get({ users: input }, function (data) {
console.log(data[0])
return data[0]
});
}
});
and while console.log is returning the correct value, I dont see it on the page. I'm guessing this is a timing issue. Is there anything I can do to make this work, so that the value is then populated on the page?
I have a snippet of code that I use very often
Whenever you hear yourself saying that think services not filters. While you could in theory have your filter make remote calls, it is a gross misuse of filters and will probably be very inefficient (likely causing your page to be slow and possibly even DDoSing your server).
Your patterns should look more like
{{ returnUserName(userId) }}
Or perhaps even pre-computer the username somewhere and then just do
{{ userName }}
Just to answer your question , here's a snippet of code that should answer your question.
If you use Angular 1.3.X, you can use $stateful like :
angular.module('app.filters').filter('returnUserName', function($http) {
var data = null, serviceInvoked = false;
function realFilter(value) {
return data;
}
filterStub.$stateful = true;
function filterStub(value) {
if( data === null ) {
if( !serviceInvoked ) {
serviceInvoked = true;
$http.get('/url/test/').then(function (res) {
data = res;
});
}
return "";
}
else return realFilter(value);
}
return filterStub;
});
For more information : here
I hope it'll help you.
As #Florian Orpeliere mentioned, you could use a stateful filter - and additionally use some cache variable:
angular.module("app").filter('svcData', function($http) {
var cached = {};
var apiUrl = 'http://my.service.com';
function svcDataFilter(data_id, data_prop) {
if (data_id) {
if (data_id in cached) {
// avoid returning a promise!
return typeof cached[data_id].then !== 'function' ?
cached[data_id][data_prop] : undefined;
} else {
cached[data_id] = $http({
method: 'GET',
url: apiUrl + data_id
}).success(function (data) {
cached[data_id] = data;
});
}
}
}
svcDataFilterFilter.$stateful = true;
return svcDataFilterFilter;
})
and use it like so: {{data_id |svcData:'property'}}
Beware: The function svcDataFilter will be called on each digest cycle.
Also, you would need to figure out a way to reset the cache if it grows too big.
See: https://glebbahmutov.com/blog/async-angular-filter/
And the plunker (the link above won't display it, so here's a direct link): http://plnkr.co/edit/EK2TYI1NZevojOFDpaOG?p=preview

Angular JS - Manipulating Data in a Service / Factory and Making Sanitized Object Available to Controller

I currently have a simple factory like the following:
myFactory.factory('myFactory', function($resource){
return $resource('http://to/my/api', {}, {
query: {
method: 'GET'
}
});
});
I would like to move some of the data manipulation / sanitation that I am currently doing in my controller to my factory. I changed my factory to the following:
myFactory.factory('myFactory', function($resource){
var connection = $resource('http://to/my/api', {}, {
query: {
method: 'GET'
}
});
var filters = connection.query();
for(var i = 0; i < filters.data.length; i++){
// Get the name value in the data array
var name = filters.data[i].Name;
// Create new properties in $scope.filters and assign arrays to them. Each array is a separate filter
filters[name] = filters.data[i].Values;
}
return filters;
});
Essentially what I am doing in the above is reordering the structure of the object array returned from the AJAX call.
When I try to console log my 'filters' variable from the factory in my controller, I get an 'undefined' message. What is wrong with my above factory syntax?
Because you're not publicly exposing anything to return filters, try:
myFactory.factory('myFactory', function($resource){
return {
getFilters:function(){
var connection = $resource('http://to/my/api', {}, {
query: {
method: 'GET'
}
});
var filters = connection.query();
for(var i = 0; i < filters.data.length; i++){
// Get the name value in the data array
var name = filters.data[i].Name;
// Create new properties in $scope.filters and assign arrays to them. Each array is a separate filter
filters[name] = filters.data[i].Values;
}
return filters;
}
}
});
and call it like this in your controller:
$scope.filters = myFactory.getFilters();
Excuse the horrible formatting, here's a plunk.

Resources