Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angularjs timing with a filter and an $http service

Tags:

angularjs

When my app loads i register a filter and a service with angular. But the filter is trying to execute before the service comes back with the data so my items collection is barking in the filter. I had a fiddle for it but i don't know if you can use $http in a fiddle because it is an external resource. Here is what i have Fiddle Sorry that it doesn't actually work. its been through alot. I think my issue is timing. I need the filter to wait for the http response or just not 'enforce' itself.

The error i get now is 'items is undefined' cause that is where the filter is being applied. I did have this working before i tried to mash the http call into a service. But i feel like that is the angular way and i "just want to be in compliance".

When my controller fires it makes the call to fetch data:

eventApi.async().then(function () {
    $scope.eventCollection = eventApi.data();
});

but before it gets back the filter is applied in the html:

 <tr ng:repeat="event in events  | myEventFilter:ddlFilter |
       orderBy:sort.column:sort.descending">

Fiddle

like image 540
VBAHole Avatar asked Feb 01 '13 21:02

VBAHole


People also ask

What is $HTTP service in AngularJS?

The $http service is a core AngularJS service that facilitates communication with the remote HTTP servers via the browser's XMLHttpRequest object or via JSONP. For unit testing applications that use $http service, see $httpBackend mock. For a higher level of abstraction, please check out the $resource service.

What is the difference between a service () and a factory ()?

factory() is a method that takes a name and function that are injected in the same way as in service. The major difference between an AngularJS service and an AngularJS factory is that a service is a constructor function and a factory is not.

What are the differences between AngularJS module's service provider and factory?

provider. Providers have the advantage that they can be configured during the module configuration phase. See here for the provided code. So factory is a function which is responsible for creating the value.

Is HTTP request is synchronous or asynchronous in AngularJS?

The problem is as follows, $http. get is asynchronous, before the response is fetched, the function returns. Therefore the calling function gets the data as empty string.


1 Answers

Here's a fork of your updated fiddle that fixes a couple missed dependencies (eventApi not injected into the controller, $timeout not injected into the eventApi service). My comments will be based on this code: http://jsfiddle.net/BinaryMuse/zww7e/1/

This fiddle successfully gets us to the issue you posted about: "Cannot read property 'length' of undefined" in your filter. In general, filters should be able to handle null/undefined values by default. I would simply modify the filter to read something like this:

return function (items, eventFilterType) {
  var arrayToReturn = [];
  if (items === undefined) return arrayToReturn;
  // ...
}

That should take care of your root problem; the fiddle now runs without errors, although it doesn't appear to work properly; as far as I can tell, it's because you're setting $scope.eventCollection instead of $scope.events in the if (live) section of your controller. Making this change causes data to actually show up in the view.

However, you may be interested in another property of Angular: when you use a promise built with $q, you can bind the view directly to the promise, and the view will treat it as though you bound it to the resolved value of the promise. So, for example, you can do the following:

// in the service
$timeout(function () {
  deffered.resolve(fakeEvents); // <-- resolve with the data
}, 2000);
return deffered.promise;

// in the controller
if (live) {
  $scope.events = eventApi.async();
}

Here's an updated version of the fiddle that demonstrates this technique: http://jsfiddle.net/BinaryMuse/zww7e/2/

[Update]

As pointed out by Jeff in the comments, Angular is deprecating automatic promise unwrapping; you can see the commit here, but here is the message:

fix($parse): deprecate promise unwrapping and make it an opt-in

This commit disables promise unwrapping and adds $parseProvider.unwrapPromises() getter/setter api that allows developers to turn the feature back on if needed. Promise unwrapping support will be removed from Angular in the future and this setting only allows for enabling it during transitional period.

If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a promise (to reduce the noise, each expression is logged only onces). To disable this logging use $parseProvider.logPromiseWarnings(false).

Previously promises found anywhere in the expression during expression evaluation would evaluate to undefined while unresolved and to the fulfillment value if fulfilled.

This is a feature that didn't prove to be wildly useful or popular, primarily because of the dichotomy between data access in templates (accessed as raw values) and controller code (accessed as promises).

In most code we ended up resolving promises manually in controllers or automatically via routing and unifying the model access in this way.

Other downsides of automatic promise unwrapping:

  • when building components it's often desirable to receive the raw promises
  • adds complexity and slows down expression evaluation
  • makes expression code pre-generation unattractive due to the amount of code that needs to be generated
  • makes IDE auto-completion and tool support hard
  • adds too much magic

BREAKING CHANGE: $parse and templates in general will no longer automatically unwrap promises. This feature has been deprecated and if absolutely needed, it can be reenabled during transitional period via $parseProvider.unwrapPromises(true) api.

like image 196
Michelle Tilley Avatar answered Sep 19 '22 16:09

Michelle Tilley