Using Angular-Bacons $scope.$watchAsProperty(property) in Angulars ng-repeat - angularjs

I´m trying to find a good way to use Baconjs together with Angularjs in conjuctions with Angular-Bacon.
Now digesting from Bacon to the Angular scope works perfectly fine but I´m stumbling with Angular-Bacons $scope.$watchAsProperty(property) within Angulars ng-repeat:
Let´s say I have the Angular scope $scope.databaserecords and render it out with:
<div ng-repeat="record in databaserecords">
Each of the records has a record.checked property and I want to handle all checked records together in one Bacon stream, for example to add a certain tag to all records at once.
At this point using $scope.$watchAsProperty(databaserecords) I get no Bacon events when checking or unchecking certain records, so how could I accomplish to receive these changes back in Bacon?
I might also mention, that using $scope.$watchAsProperty(property) out of ng-repeat, for example for input fields, works well without any problem.
Thanks for your help! :)

If I've understood correctly, your actual databaserecords remains the same throughout the scope, so you'll need to invoke $watchAsProperty with the objectEquality argument set to true:
$scope.$watchAsProperty("databaserecords", true)
By default angular opts to compare objects with a simple object equality check. In your case the list object remains the same, so a deeper check is necessary. This is already implemented in angular-bacon but it seems I've omitted it from the docs.

Related

How to clear the md-autocomplete cache?

I'm using md-autocomplete to show results of an api query. Attribute md-items is iterating over a promise: item in getItems(searchText).
This is working well, and using the cache subsequent uses of the same search text return immediately with the same results.
But I need to be able to clear the cache at some points, when other search parameters change. How can I do this? By accessing the md-autocomplete controller perhaps? Although that seems non-standard and I'm not sure how.
As of version 1.0.5 of angular-material this isn't possible. I didn't find any acceptable workarounds, so I'm just disabling the cache with md-no-cache="true".
I've logged an issue for this on the angular-material project including a suggestion on how it could work.
It is definitely possible to reset the md-no-cache attribute programmatically anytime on your md-autocomplete directive.
If you have a boolean variable on your controller, let's say:
$scope.noCacheResults = false;
Then on your directive you can bind this variable to the md-no-cache attribute:
<md-autocomplete ...
md-no-cache="noCacheResults">
</md-autocomplete>
Like this, whenever your search parameters change you can set the $scope.noCacheResults to true or false depending whether you want to keep caching the query results or not.
Something that worked for me. Put an ng-if on your autocomplete. Then, in the code that changes the value of the other fields affecting this field, set that value to false, and then within a timeout, set it to true again. This will effectively remove the item from the DOM and put it back all nice and new with no cache.

AngularJS: Force ng-repeat update when property of an element changes

I'm wrestling with the way angular watches arrays. I have the following markup:
<map name="theMap" center="{{mapInfo.center.toUrlValue()}}" zoom="{{mapInfo.zoom}}" on-dragend="dragEnd()" on-zoom_changed="zoomChanged()">
<marker ng-repeat="pin in pins() track by pin.iconKey()" position="{{pin.latitude}}, {{pin.longitude}}" title="{{pin.streetAddress}}" pinindex="{{$index}}" on-click="click()"
icon="{{pin.icon()}}"></marker>
</map>
Each individual pin returned by pins() has a number of properties, sub-properties, etc. One of those sub-properties controls the marker color. When that subproperty changes I want the UI to update.
Because ng-repeat appears to $watch based on simply changes to the collection it's not obvious how to achieve that. I thought that my tracking function, iconKey(), would do it because it returns a different value depending upon the subproperty's value. But that didn't work.
One other thing: the subproperty gets changed in the callback from an $interval that runs under a directive. I mention this because, in an earlier post, someone thought that there might be a context/scope problem.
However, even when I make the change in an event listener within the UI's controller (where the event is raised within the "success" clause of the $interval callback) it still doesn't work.
That's why I think the problem is just angular not noticing the change in iconKey(); it's acting like all it $watches for ng-repeat is the array's length. Which doesn't change when the subproperty changes.
Update
I've created a plunker to demonstrate the issue I'm facing. You can find it at http://plnkr.co/edit/50idft4qaxqw1CduYkOd
It's a cut down version of the app I'm building, but it contains the essential elements (e.g., a data context service to hold information about the map pins and an $interval service to toggle the subproperty of one of the pin array elements).
You start the update cycle by clicking Start in the menu bar (you may want to drag the map down slightly to put both pins into full view). It should toggle the color of each pin, alternatively, 5 times each, once every 2 seconds. It does this by toggling the value of the isDirty property of the pin object in a listener defined in the controller. The event is raised by the $interval service.
If you break on line 22 during the test cycle you'll see the pin's icon being returned. So something within angular is calling for the information...but the pin color doesn't change.
I look forward to someone quickly pointing out the bone-headed mistake that has nothing to do with any of my theories :). Apologies in advance for whatever blinders I'm wearing.
Update 2
After checking out the code snippet in the response I simplified my plnkr and demonstrated that angular is, in fact, updating the UI when a subproperty changes. So this appears to be a limitation or bug in ng-map.
What you are missing here is the concept of array and function your function pins() passes an array and that array is been bound with ng-repeat. But the brutal fact here is that no matter what that array is never changed, because you do not have ANY reference to that array hence the rg-repeat will always remain as is...
I'll suggest to try get the array be referenced two ways to do that
ng-init="pinsArray = pins()"
and second inside controller
$scope.pinsArray = $scope.pins()
then make changes to $scope.pinsArray inside controller
ng-repeat will be changed to
ng-repeat="pin in pinsArray"
also read about filters I am guessing that's what you where trying to do with "track by"
hope this helps..
Edit: different story with ngMap markers
seems like it doesn't watch sub-property.
so here's a work around
add following statement to you update the pinsArray after making changes to its properties.
pinsArray = angular.copy(pinsArray);
the solved plnkr example:
http://plnkr.co/edit/EnW1RjE9v47nDpynAZLK?p=preview

Having a set of checkboxes map to a nested array

I am working on a SPA that pulls in customer data from one $resource call, and gets some generic preference data from another $resource call.
The preference data is sent as an array, which I want to use to populate a series of checkboxes, like so:
<div ng-repeat="pref in fieldMappings.mealPrefs">
<input type="checkbox"
id="pref_{{$index}}"
ng-model="customer.mealPrefs"
ng-true-value="{{pref.name}}" />
<label class="checkbox-label">{{pref.name}}</label>
</div>
When a user clicks one or more checkboxes, I want the values represented in that array of checkboxes to be mapped to an array nested inside a customer object, like so:
.controller( 'AppCtrl', function ( $scope, titleService, AccountDataService ) {
// this is actually loaded via $resource call in real app
$scope.customer = {
"name": "Bob",
"mealPrefs":["1", "3"]
};
// this is actually loaded via $resource call in real app
$scope.fieldMappings.mealPrefs = [
{'id':"1", 'name':"Meat"},
{'id':"2", 'name':"Veggies"},
{'id':"3", 'name':"Fruit"},
{'id':"4", 'name':"None"}
];
});
I have tried setting up ng-click events to kick off functions in the controller to manually handle the logic of filling the correct part of the customer object model, and $watches to do the same. While I have had some success there, I have around 2 dozen different checkbox groups that need to be handled somehow (the actual SPA is huge), and I would love to implement this functionality in a way that is very clean and repeatable, without duplicating lots of click handlers and setting up lots of $watches on temporary arrays of values. Anyone in the community already solved this in a way that they feel is pretty 'best practice'?
I apologize if this is a repeat - I've looked at about a dozen or more SO answers around angular checkboxes, and have not found one that is pulling values from one object model, and stuffing them in another. Any help would be appreciated.
On a side-note, I'm very new to plunkr (http://plnkr.co/edit/xDjkY3i0pI010Em0Fi1L?p=preview) - I tried setting up an example to make it easier for folks answer my question, but can't get that working. If anyone wants to weigh in on that, I'll set up a second question and I'll accept that answer as well! :)
Here is a JSFiddle I put together that shows what you want to do. http://jsfiddle.net/zargyle/t7kr8/
It uses a directive, and a copy of the object to display if changes were made.
I would use a directive for the checkbox. You can set the customer.mealPrefs from the directive. In the checkbox directive's link function, bind to the "change" event and call a function that iterates over the customer's mealPrefs array and either adds or removes the id of the checkbox that is being changed.
I took your code and wrote this example: http://plnkr.co/edit/nV4fQq?p=preview

Customisation of the typeahead feature in angular-ui-bootstrap

I would like to use the angular-bootstrap-ui typeahead feature with the $http object.
A simple use case is found at the following plunk:
http://plnkr.co/edit/eGG9Kj?p=preview
How do I manipulate the returned json BEFORE it is being rendered?
I will be working with a more comlex json structure than a simple array.
How to overwrite the html-template being used so I can apply my own styles?
In case no results are found an empty typeahead-dropdown is shown. How do i avoid this?
Thanks for any hints.
For questions (1) and (2) I believe that this is duplicate of Bootstrap-UI Typeahead display more than one property in results list?. Have a look at the answers in there that provide solutions to customizing various part of results displaying, including template override.
If the above doesn't solve your issue please update your question.
For (3) - it this example it is a back-end issue since it returns [""] where there are no matches while it should return []. So it has nothing to do with the typeahead directive - just make sure that your backend returns empty array when there are no matches.

AngularJS pattern for persisting record selection through multiple asynchronous calls?

We're working on CRUD patterns for a fairly large application. One common UI pattern we're using to define a one-to-many relationship where a record is associated by checkbox. The challenge is to persist selections (checked/unchecked) through asynchronous calls (search/sort) that refresh the record list (and the associated ng-model). I'd like to hear from more advanced AngularJS users (I'm a noob) what's considered a best practice for this? Any feedback is appreciated!
EDIT
Here's a working plunk showing how I'd most likely tackle this with my current understanding of Angular. Please let me know if you have a better approach!
I think u could maintain a separate collection of selected names. So the next time you filter the list, you just need to lookup in the collection in order to keep the item selected. That you can do by binding some variable (arrSelected) in the controller or you can create a separate service also.
Have you considered using a filter instead of refetching. Of course this really depends on how many items you plan on having, but I've had success filtering a nested JSON object with around 5000 items.
Here is the full plunker : http://plnkr.co/edit/l4jYgt0LjRoP2H0YuTIT?p=preview
Highlights:
In index.html for your repeat you add | filter
<tr ng-repeat="user in users | filter:userFilter">
In script.js we add the filter function and a $scope variable to hold the filtered letter:
$scope.filteredLetter
$scope.userFilter = function (elem) {
if (elem.name.lastIndexOf($scope.filteredLetter, 0) === 0 || $scope.filteredLetter == '') {
return true;
} else {
return false
}
}
As a bonus I added in ng-class to show which letter was highlighted.
Pretty simple code but this gives you full persistence now even as people change things. You can even experiment now with adding a <input> tag with an ng-model binding to say $scope.filteredName. Then all you need to do is add the JS to the filter to do a deeper filter for part of the name.

Resources