Saving a lot of elements RESTfully on a page in AngularJS - javascript

Let's say I'm on a screen editing a post (and its associated meta-data). Now, the post has many categories, tags, and possibly other related items (hasMany).
When saving the entire post, I have to save the post and every single entity associated with it. This is how I do it in AngularJS:
All the data is stored in a separate model so that it can be universally accessed. Each related entity has multiple meta-data indicating whether they're new, dirty, or whether they have server-side validation errors.
A different controller called "SaveCtrl" manages saving. It goes through all the entities of a post, figures out the ones that have changed, and only sends them along with the post to a single save endpoint.
I have a factory that parses the response from the server. An element might not get saved due to a server-side validation error. Regardless, once we get the response, each of those entities have to be updated with the new state and validation errors. This allows the server-side to update entities without any errors, while returning errors for the few that did have validation errors.
Now, here are some problems with this approach:
The saving is not entirely RESTful. While retrieving data, I believe I should probably make multiple GET calls for each resource (URL design for an API). However, I don't know of a similar approach for saving. Should I save a collection, or a single entity at a time?
The meta-data for each entity (specifying whether it's new, dirty, saved, or unsaved) has to be put in the model data itself. Since ng-repeat creates a separate scope, I'd ideally like to save such state information in these separate scopes, with the ng-repeat elements wrapped in a controller that'd handle this.
My question
Which strategy should I use to save an entire post with all its related entities? Or is my present strategy good enough? Let me know if there are some additional details required.

Related

How to integrate Redux with very large data-sets and IndexedDB

I have an app that uses a sync API to get its data, and requires to store all the data locally.
The data set itself is very large, and I am reluctant to store it in memory, since it can contains thousands of records. Since I don't think the actual data structure is relevant, let's assume I am building an email client that needs to be accessible offline, and that I want my storage mechanism to be IndexedDB (which is async).
I know that a simple solution would be to not have the data structure as part of my state object and only populate the state with the required data (eg - store email content on state when EMAIL_OPEN action is triggered). This is quite simple, especially with redux-thunk.
However, this would mean I need to compromise 2 things:
The user data is no longer part of the "application state", although in truth it is. Since the sync behavior is complex, and removing it from the app state machine will hurt the elegance of the redux concepts (the way I understand them)
I really like the redux architecture and would like all of my logic to go through it, not just the view state.
Are there any best-practices on how to use redux with a not-in-memory state properties? The thing I find hardest to wrap my head around is that redux relies on synchronous APIs, and so I cannot replace my state object with an async state object (unless I remove redux completely and replace it with my own, async implementation and connector).
I couldn't find an answer using Google, but if there are already good resources on the subject I would love to be pointed out as well.
UPDATE:
Question was answered but wanted to give a better explantation into how I implemented it, in case someone runs into it:
The main idea is to maintain change lists of both client and server using simply redux reducers, and use a connector to listen to these change lists to update IDB, and also to update the server with client changes:
When client makes changes, use reducers to update client change list.
When server sends updates, use reducers to update server change list.
A connector listens to store, and on state change updates IDB. Also maintain internal list of items that were modified.
When updating the server, use list of modified items to pull delta from IDB and send to server.
When accessing the data, use normal actions to pull from IDB (eg using redux-thunk)
The only caveat with this approach is that since the real state is stored in IDB, so we do lose some of the value of having one state object (and also harder to rewind/fast-forward state)
I think your first hunch is correct. If(!) you can't store everything in the store, you have to store less in the store. But I believe I can make that solution sound much better:
IndexedDB just becomes another endpoint, much like any server API you consume. When you fetch data from the server, you forward it to IndexedDB, from where your store is then populated. The store gets just what it needs and caches it as long as it doesn't get too big or stale.
It's really not different than, say, Facebook consuming their API. There's never all the data for a user in the store. References are implemented with IDs and these are loaded when required.
You can keep all your logic in redux. Just create actions as usual for user actions and data changes, get the data you need and process it. The interface is still completely defined by the user data because you always have the information in the store that is needed to GET TO the rest of it when needed. It's just somewhat condensed, i. e. you only save the total number of messages or the IDs of a mailbox until the user navigates to it.

Angular JS and Symfony2

I am currently working on a project using Symfony2 and seeking some advice on that.
I am thinking of a hybrid application in two(2) different ways a) Login Page shall use traditional form with CRF Token and let symfonty2 handle it. b) All Inner pages ( which potentially are modules ) I want them to be non AJAX, but the other activities inside that shall behave like a Single Page.
For example I have an employee module. When user clicks on that it is entirely loaded from Server ( all the templates and forms etc ) now each activity under employee module like add/update delete/view etc shall be loaded through AJAX and response to be returned in JSON i.e AngularJS.
I am currently thinking of using FOSUserBundle to return html on initial request and then based on request type Accept: application/json it will return the JSON ( remember the add/updat delete/view part? ).
My question is it a better idea to use Angular Partials (html) files or Symfony2 Twig? or would it be better to use Angular JS, but let those partials be rendered by Symfony2 twig? ( I am thinking of Forms here, would want to validate that both from client and server side )
Has any one been through similar problem, if yes then what approach was used to develop HYBRID application using AngularJS and Symfony2 or any other framework? any relevant ideas are appreciated.
I was in the same situation you are. AngularJS+Symfony2 project, REST API, login using FOSUserBundle, etc.
... And every way has pros and cons, so there is no right way, i'm just gonna say exactly what i did.
I choose AngularJS native templates, no CSRF validation, a base template built using Twig, server-side validation, use of the FOSJSRoutingBundle, and some helpers (BuiltResponse and BaseController).
Why native templates?
With the use of verbatim, we solve the variable problems, but we gonna have a more complex logic in our templates.
We also will have a less scalable application. All our forms templates are doing a request in the Symfony application, and one of the best pros of the AngularJS is load our controllers, templates, etc from a storage service, like S3, or CDN, like Cloudfront. As there is no server-side processing, our templates would load so much faster. Even with caching, Twig is slower, obviously.
And both, Twig and AngularJS templates, are really complex to manage, in my own experience. I started making them together, but was painful to manage.
What i did?
I created static templates in front-end, with the same field names, it's not really good. We need to update the templates every time we update the forms, manually. But was the best way i found. As the field names are equal, we won't have problems to ajust the model names in the Angular controllers.
And if you are creating the software as a service, you will need to do it anyway. Will you not load the form templates from the application in a mobile app, right?
Why no CSRF validation?
We don't use CSRF validation in a REST API, obviously. But, if you wanna do it, you need to make a request every time you load a form, to get the CSRF token. It's really, really bad. So, we create a CRUD, and also we need to create a "csrf-CRUD", 4 routes more. That doesn't make any sense.
What i did?
I disabled the CSRF in the forms.
Base template?!
Yep. A base template is just to load any route in our application. Here is what i'm doing:
This will help us to avoid errors when users are going directly to some Application URL if you are using html5 angularjs urls. Simple like that.
Server-side validation, why?
If we do a validation in the Angular, we need to do the same in the server-side, so we have 2 validation codes to maintain. That is painful. Every change we do in the form, we need to change the validation in the front, validation in the back and also the Angular static form. Really, really painful.
What i did?
I basically did a server-side validation using the Symfony constraints. For every request, the application validates the form and check if any error was found, if yes, it gets the first one and send it as a response.
In the AngularJS, the application checks if there is any error inside of the errors key. So, we have a proccess used in all application to do any form request. It's like that:
And the routes?
There is another problem: the routes. Put the url directly is not a reliable way. If we change anything in the url, that route is gone and the users won't like that.
To fix that, we can use the FOSJsRoutingBundle. With that library, we can put the route name directly in the Angular controller, and it will fill with the exact url of the route. It's completely integrated with the Symfony, so parameters will work very well.
Instead using the url directly, we can do it:
Routing.generate('panel_products_show', {id: $routeParams.product_id});
And voilá! We get the route url.
That will solve the biggest part of the problems you have. But there are more.
Problem 1 - Form inputs
The forms from Symfony generally have a prefix, like "publish_product", so every field has a name like [publish_product]name. Ah, how that was a problem for me.
In the Angular, publish_product is not considered a array. You need to put the single quote to do this, like ['publish_product']name. And it's really bad, we need to change every key to use this format. In AngularJS, i was doing like that:
{{ formData('[publish_product]name') }}
Absolutely stupid.
The best solution was simply remove the form prefix in the Symfony, using the createNamedBuilder method instead just createBuilder. I let the first parameter null, and yeah, we don't need to use the prefix anymore. Now, we use:
{{ formData.name }}
So much better.
Problem 2 - Routes hard do maintain
Every request can return anything, i need to repeat much code. That is really hard to maintain, so i just create some application rules, built responses, a BaseController, etc.
createNamedBuilder
createNamedBuilder is a big method. We need to do this, for every form we have:
It's simple to solve. I just created a BaseController and i'm extending every controller from it. I created a simple method that does it.
For every route, we do not need to repeat 3 lines, much better.
Responses
When my application started growing, i had a serious problem: all my responses are different. That was really hard to maintain. For every request i was doing, sometimes i was using "response", sometimes "data", the error messages were lost in the response, etc.
So, i decided to create a buildResponse, that i just need to set some parameters and i get the same result for every route, even GET routes.
response key shows me the status and the message. It can be error or success, and the message os a optional field, that can come blank. For example, a success status with the message "You created the product".
data key shows me any information i need. For example, the user added the product, and now he needs the link to see it. In the data, i put the url of the post, and i easily can get it from the AngularJS controller.
notifications is a specific key for my business logic. Every action can return a notification to the user.
It doesn't matter what keys you have. The most important thing is have a standardized response, because when your application grows, it will be really helpful.
That is a route from my controller:
Completely standardized. The Scrutinizer code quality tool says all my routes are duplicated. :D
Have a BaseController and a builtResponse will help you so much. When i started refactoring my code, each route lost about 4-10 lines.
Details: getFormError return the first error of the form. Here is my method:
public function getFormError(FormInterface $form)
{
if ($form->getErrors()->current()) {
return $form->getErrors()->current()->getMessage();
}
return 'errors.unknown';
}
... And the parameters from the buildResponse are:
1. Status. I get it from a constant in the BaseController. It can be changed, so i believe is important do not use a string value in each route.
2. The translation message. (I use a preg_match to check if it has a translation format, because getFormError already translates the error).
3. The data (array) parameter.
4. The notifications (array) parameter.
Other problem i'm gonna have
The project just have one supported language until now. When i start to work in a multilingual version, i'm gonna have another big problem: maintain 2 versions of the translations: the back-end messages and validations and the text from the front-end. That probably will be a big problem. When i get the best approach, i'll update this answer.
I took some months to get the this approach. So many code refactorings and probaly much more in the future. So i hope it help someone to do not need to do the same.
1. If i get a better way to do this, i'll update this answer.
2. I'm not good at writing english, so this answer probably will have many grammatical errors. Sorry, i'm fixing what i'm seeing.

AngularJS - accessing elements inside a controller from a service

I have an app with two controllers where I want a change in one controller to affect (different) data stored in the other. tl;dr when I remove a category from the first controller, I want to edit any items in the second controller with that category, so that they will now be category-less.
As far as I can tell what I want is to use a service, but I feel it would be simpler if there were a way for me to simply edit the data inside the controller scope. So my questions are:
Is there a way to simply edit controller data or call controller methods from a service?
Otherwise, is it reasonable to store the latter controller's data in the service, even though the former controller only needs access to change it? How do I reference this data for the purpose of doing ng-repeats?
Edit: to clarify the data is a set of json objects which contain data for each category and each item, and the web page contains ng-repeats to go through and list each of them. I have a number of functions which edit both lists of data, and I want changes to one list to make changes in the other.
Your idea was correct, you should put all your business logic, including data that needs to be consistent between different parts of your application, into services. controllers should only manage the view and connect the data to it.
Keep in mind that services are Singletons - there is always only one instance of each service, holding your data.
To answer your question: I would argue that storing data in a service instead of a controller is always reasonable when it works (aka when the data is not specific to one of multiple views, but consistent throughout the current application state), and giving access to that data to manipulate it is perfectly fine - even better would be to put the manipulation logic into the service itself (or another service only for that) and to just let the controller connect to a call invoking that.
There is an article by Todd Motto on that topic (thin controllers)
I think it will be better use events for this purpose. In your first controller you can published the event on category deletion like below.
$scope.deleteCategory = function (category) {
$rootScope.$broadcast("categoryDeleted", category);
}
Then you can observe this event in any controller like below in second controller you can listen categoryDeleted event.
$scope.$on("categoryDeleted", function (event, category) {
// do whatever you want
});
Do not call controller directly from the service, this is a bad practice, not only in AngularJS, but in most languages frameworks.
The problem you have described ("a change in one controller to affect (different) data stored in the other") is a problem of communication between components. You can solve this issue with events, thus there is no need to move data from the second controller to the service.
Let's consider some example:
http://jsfiddle.net/simpulton/XqDxG/
When you click on the LOG button the this.broadcastItem() is invoked, and the 'handleBroadcast' event is broadcasted.
Other constrollers, controllerOne and controllerTwo, handle this event:
$scope.$on('handleBroadcast'
and do the things they want to do.
So, in yor case, you can introduce the 'categoryRemoved' event, and broadcast this event in the first controller. Once the event is broadcasted, your second controller handle it and edit its items.
Also you should pass the removed category in the event (instead of 'msg' in the example), so that second controller has to aware which exactly category has been removed.
In way you want to do that, $rootScope can be used (shared across controllers - modyfing in one, accessing in another), but its generally bad idea. Im not sure if I get it right but its typical situation when you actually need service with controlling some external data.. Its some static json you want to modify? Can you specify it more clearly ? :)

How do you pass a lot of data into a view that doesn't need to persist in Angular?

I know that you could use a service or an HTTP call to a database to get the data you want in a view.
And I know that you could access data in the URL as parameters or via the query string.
But what if you want to pass - 1) a lot of data, 2) that doesn't need to persist - into a view?
For example, I have a form for creating a Thing, and I want the user to be able to preview the Thing. I already have a show view for Thing, and there's too much to display for a modal or to display it on the same page.
It seems unnecessary to use a service, and definitely doesn't seem necessary to use a database.
It seems excessive to pass it in via the URL.
Maybe you could use $sessionStorage?
Thoughts? Is there a best practice here?
Using a service is in fact a good idea. The first "view" set the service data, the other get it.
Or, if that answers what you mean by "without persisting the data", you could simply emit the data from one view (controller/directive/service) to another.
View1:
$scope.$root.$broadcast('bigdata', data);
View2:
$scope.$on('bigdata', function(e, data) {
// Deal with the data which is garbage collected at the end of this callback
});

dojo.store.Observable, JSON REST and queryEngine

Does anybody know how to use the JsonRest store in dojo witn an Observable weapper, like the one in dojo.store.Observable?
What do I need, server side, to implement the store and make it work as an Observable one? What about the client side?
The documentation says http://dojotoolkit.org/reference-guide/1.7/dojo/store/Observable.html
If you are using a server side store like the JsonRest store, you will need to provide a queryEngine in order for the update objects to be properly included or excluded from queries. If a queryEngine is not available, observe listener will be called with an undefined index.
But, I have no idea what they mean. I have never created a store myself, and am not 100% familiar with queryEngine (to be honest, I find it a little confusing). Why is queryEngine needed? What does the doc mean by "undefined index"? And how do you write a queryEngine for a JsonRest store? Shouldn't I use some kind of web socket for an observable REST store, since other users might change the data as well?
Confused!
I realize this quesiton is a bit old, but here's some info for future reference. Since this is a multi-part question, I'll break it down into separate pieces:
1) Server-side Implementation of JsonRest
There's a pretty decent write up on implementing the server side of JsonRest Store. It shows exactly what headers JsonRest will generate and what content will be included in the rest. It helps form a mental model of how the JsonRest api is converted into HTTP.
2) Query Engine
Earlier in the same page, how query() works client side is explained. Basically, the query() function needs to be able to receive an object literal (ex: {title:'Learning Dojo',categoryid:5}) and return the objects in the store that match those conditions. "In the store" meaning already loaded into memory on the client, not on the server.
Depending on what you're trying to do, there's probably no need to write your own queryEngine anyway -- just use the built-in SimpleQueryEngine if you're building your own custom store. The engine just needs to be handed an object literal and it adds the whole dojo query() api for you.
3) Observables
My understanding is that the Observables monitor client side changes in the collection of objects (ex: adding or removing a result) or even within a specific object (ex: post 5 has changed title). It does NOT monitor changes that happen server-side. It simply provides a mechanism to notify other aspects of the client-side app that data changed so that all aspects of the page stay synchronized.
There's a whole write up on using Observables under the headings 'Collection Data Binding' and 'Object Data Binding: dojo/Stateful'.
4) Concurrency
There's two things you'd want to do in order to keep your client side data synchronized with the server side data: a) polling for changes from other users on the server, b) using transactions to send data to the server.
a) To poll for changes to the data, you'd want to have your object store track the active query in a variable. Then, use setTimeout() or setInterval() to run the query in the background again every so often. Make sure that widgets or other aspects of your application use Observables to monitor changes in the query result set(s) they depend on. That way, changes on the server by other users would automatically be reflected throughout your application.
b) Use transactions to combine actions that must be combined. Then, make sure the server sends back HTTP 200 Status codes (meaning 'It Worked!'). If the transactions returns a HTTP status in the 400s, then it didn't work for some reason, and you need to requery the data because something changed on the backend. For example, the record you want to update was deleted, so you can't update it. There's a write up on transactions as well under the heading 'Transactional'

Categories