AngularJS: how to undo any last change in the document - angularjs

Is it possible to use $watch in order to create an undo functionality which I could use to undo any last change in the view?
For example, I have multiple input fields, ng-includes on demand etc - what would be the easiest and most efficient way of implementing Undo functionality?
I was thinking about pushing any change on the view to an array, and in case of undoing() simply deleting the last item from that array and serving the array again, though not sure if there is another, better way of doing that in angular. Any tips?

You could use the LazyJsonUndoRedo:
https://github.com/azazdeaz/LazyJsonUndoRedo
http://dailyjs.com/2014/07/18/lazy-json-undo/
From the readme:
A 'drop in' history handler with automatic undo/redo functionality for nested javascript objects, using ES6 Object.observe() or Polymer shim.

Related

Should I use ids to locate elements?

Started with Angular and Protractor.
It just feels wrong to write some heavy css selectors which will break instant when you change something.
Using ID's would make testing way easier.
I'm not using any id attribute for styling yet. Are there any drawbacks using ids for testing I haven't considered?
The general rule is to use IDs whenever possible assuming they are unique across the DOM and not dynamically generated. Quoting Jim Holmes:
Whenever possible, use ID attributes. If the page is valid HTML, then
IDs are unique on the page. They're extraordinarily fast for
resolution in every browser, and the UI can change dramatically but
your script will still locate the element.
Sometimes IDs aren't the right choice. Dynamically generated IDs are
almost always the wrong choice when you're working with something like
a grid control. You rely on an id that is likely tied to the specific
row position and then you're screwed if your row changes.
Also, in general try to use the "data-oriented" approach: by.model, by.binding, by.repeater locators, or if you rely on class names, choose them wisely: do not use layout-oriented classes like .col-xs-4 or .container-fluid.
See also these related topics:
Best Practices for Watir and Selenium Locators
best way to detect an element on a web page for seleniumRC in java

is angulars reloadOnSearch evil?

I've been programming with angularjs for some time now, however, i started using reloadOnSearch for my application and at first i thought this was going to save me for uneccesary reloads of controllers and ajax calls against my API.
For example say i have a list of things, and then present them in a view. When you first access this view it fetches the list of items from the API and presents them, and when you click on an item it adds the ?id=xxx&view=show query parameter to the url without reloading the controller.
When we access a single item from the already loaded list, it just fetches the list item from that we already have and presents it. However, if we forcibly reload the page, the controller now realizes that it has to fetch it from the API instead as it doesn't exist within the list collection.
At first this seemed like a great thing. However as i think about it, i now have to manage the state of the entire controller, whereas before i could have state enclosed within single functions in the controller.
I seem to be having trouble deciding if reloadOnSearch is evil and should be avoided, or if it is worth keeping around. What is your opinion?
Also, would it be better to use something like ui-router instead? I just saw a introduction video which implies that one can have better control of state.
I've converted my app to use ui-router instead. which basically nests application logic in "sub scopes" making it possible to share data between states/pages. I realize now that this is possible without ui-router aswell, however one would have to create pages that has a hierarchy of <div ng-controller>
So in conclusion, the way i was using reloadOnSearch was indeed evil. However, there were better ways of using it aswell.

Putting presentation data in angular controller?

Got a webapp I'm building in Angular.
This app walks a user to authorizing accounts, presenting specific instructions based on the users choices.
I've implemented this as HTML that is shown or hidden based on values in the model, so for 3 different choices, I have 3 different sets of HTML sections that are similar but with different texts.
In the spirit of DRY, I should instead have one set of HTML sections, and instead switch the text based on the values of the model. This means putting text data inside the model, including small snippets of markup, like anchor and strong tags.
Does putting presentation data into the controller violate the principals of Angular?
There are quite a number of options to avoid repeating code depending on what you are looking to do. The following ideas are things I would consider and use when they make sense (I placed these from simple to complex, so you probably can skip the first few):
ng-bind -- Put it on a span/div. Simple & works to bind the model to the display
ng-switch, ng-if, ng-hide, ng-show -- Work to conditionally show an element
custom directive -- use this when you want to alter the behavior of an element or if you want to alter the dom based on a template. If you use "ng-transclude" the contents of the element you template will be included in the result. This can be very elegant but it works best when you have a single format. I can provide examples but angular's documentation also has excellent examples.
service -- I generally use this just to provide data only. This could be via a restful api and $resource or via $http calls. Either way, I wouldn't recommend doing much more than load/save data here.
$scope method -- In other words:
$scope.myMethod = function(x,y,z) { /* code making decisions based on the model */ }
Then you can call this method from one of the previous either via a prebuilt directive (ng-show, etc) or via a custom directive that manipulates the dom for how you expect it to be.
ng-bind-html -- Last option I know to suggest is to use this directive combined with the $sce service to bind whatever you want to the DOM. If you are binding something with angular code in it - make sure to use the $compile service as well. I generally don't favor this approach except as a last resort because it makes it harder to find where elements in the DOM are coming from and it can make debugging + testing a real pain. That said, these tools wouldn't exist if people didn't need them.
I'm sure that this isn't complete and maybe others have suggestions but that is where I would start. Best of luck!
I would put the text data in a separate angular service. This article gives an example: http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/
Then if you decided at some point to move it to some other storage, your service would still be the single access point for the rest of the app.

Backbone: Many small requests for model changes vs one collection sync?

What is a general good practice for when some action - changes multiple models in Backbone.js:
Trigger multiple PUT requests for each mode.save()
Single request to sync the entire collection
In case if the quantity of the changed models greater than 1 - definitely it should be the second item.
Usually, good REST api practice seems to suggest that you should update, save, create, delete single instances of persistent elements. In fact, you will find that a Backbone.Collection object does not implement these methods.
Also, if you use a standard URI scheme for your data access point, you will notice that a collection does not have a unique id.
GET /models //to get all items,
GET /models/:id //to read an element,
PUT /models/:id //to update an element,
POST /models/:id //to create an element,
DELETE /models/:id //to delete an element.
If you need to update every model of a collection on the server at once, maybe you need to ask why and there might be some re-thinking of the model structure. Maybe there should be a separate model holding that common information.
As suggested by Bart, you could implement a PATCH method to update only changed attributes of a particular element, thus saving bandwidth.
I like the first option, but I'd recommend you implement a PATCH behavior (only send updated attributes) to keep the requests as small as possible. This method gives you a more native "auto-save" feel like Google Docs. Of course, this all depends on your app and what you are doing.

Approach to hide/show Backbone model views

Help me to find best approach for managing Backbone views.
For example I have a collection of views MyCollectionView which consists of MyModelView - views of each model in the collection.
And what if I want to hide/show some models on the page?
Now I am following this way:
Use collection.each for every model
Inside of loop I call model function filter with some params
In this function I check model properties and call model.trigger 'hide' or model.trigger 'show'
And finally in the model view I use this.model.bind 'hide', this.hide, this where actually I use .hide() or .show()
This way seems me awful... Why do I need to do this long chain of functions and events. Does any simplest approach exist?
Thanks!
Your models shouldn't be telling views what they should do - they are supposed to represent data and shouldn't take part in controlling the application - so no wonders it feels wrong to you :)
The more elegant way would be to add a filter method to the MyCollectionView which would use underscore methods to filter the views you want to show/hide and do its job of well... doing that - picking which models should be shown. Then having hte array of matches just call a method for rendering the list and pass your array of models to it so it can render the views for matching models.
From my experience of creating such filters I can tell you that it might be much more efficient for longer lists to remove whole list do filtering on collection level and render again only the views which are matching the filter query. jQuery hide/show can be a bit taxing - though it's a concern you will have only with large amount of data/views.
Also! Take advantage of the underscore methods bound to the collection - you don't need to do collection.each(... you can just do
var matches = collection.filter(function(model) {
return /*matching condition*/;
});
(Also remember to use documentFragment when rendering lists and appending to the DOM pre-generated list of views rather then appending them one by one)

Resources