Angular JS and Symfony2 - javascript
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.
Related
Recovering identity in Blazor application
I have a Blazor server side application with a custom identityprovider. Whenever I need to recover the identity I do so from the session storage using javascript. This all works well. But now I have added an upload control and I have embedded it with <CascadingAuthenticationState> to see if the identity was still known until that point, which it is. Once I try to upload a file it makes a call to a controller using routing and that's where it goes haywire. Once I enter the constructor of the controller the identity is null. All attempts to recover it using javascript result in JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendered While I am familiar with this error message, I am puzzled why this happens because the render mode of the application is Server and not ServerPrerendered. Is it because I am in a post event? I've also played with several ways passing the httpcontext but that's also null. Basically what I'm trying to accomplish is that I need my token there to call an API, but I've lost all track of who I am at that point. I can think of ugly solutions like passing it in the URL, but that's not safe at all. Or store it in a singleton with a lock or something like that, but those are all fugly solutions. Can anyone kick me into right direction? I feel like I'm running into a very large, very dark forest now.
The vendor of the component added a new event to add custom headers, so now my problem is solved.
Best practice for node - mongo - angular
I have an app I am designing using node/mongo/angular, what I am not getting is how is the best way to get my data from mongo into my pages? I can use node, and thru my routes send back data from mongo with my template(hogan in this case), and bind using mustachejs. That works fine for most things. I have one screen that has a decent amount of drop down lists, to bind them for an edit scenario now seems a challenge. I would like to get them bound to an angular model and go about it that way. Is it better to get the data thru the route in node, then use something like ng-init and get it into angular? Or would I be better off not getting the data thru the route in node, and then using angular to perform a "get" request and bind that way?
From the documentation of ng-init, more precisely from the red warning alert at the top of the page...: The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope. So no, do not use ng-init. While that can be a good strategy for lazy migrations from regular applications to single page applications, it's a bad idea from an architectural point of view. Most importantly, you lose two things: An API. The benefit of SPAs is that you have an API and that you're constantly developing and maintaining it, even before it has external users A clean separation of concerns. Views are strictly limited to presentation, can be cached by the client and all data is transferred through JSON API endpoints.
I would say that the best way to get data from Mongo into your page, is as mnemosyn said, using an API. Basicly, you can have your API route, f.ex '/api/data' configured and then it can be used by a angular service, (which can use ngResource to make things easier). Any controller that wishes to access this data can use the angular service to get it, do some stuff with it, and then update it using the same angular service.
Passing JavaScript variables to a ColdFusion query
I have an update query to which I am passing a JavaScript array called "newdata", obviously, that didn't work so I don't know how to pass my JavaScript variables to ColdFusion in order to run an update query. How I can see surfing some webs? ColdFusion doesn't have access to Javascript variables. Here's my code, regards! <cfquery name="updatereserva" datasource="Prueba"> UPDATE reserva_habitac SET FechaENTRADA = newdata["#firstdate#"] WHERE idRESERVA = newdata["idreserva"] </cfquery> Is there another way to do this?
There are several ways. You could put the JavaScript variables into a form, and submit that to a specific page in your app. You could execute an AJAX request that posts your JavaScript array as JSON to the desired page in your application Etc. ColdFusion has some built in support for AJAX, but there are also a ton of JS libraries and frameworks that could do just as good (if not way better) of a job. There is, after all, nothing ColdFusion-specific about an AJAX request; CF just ships with some functionality to help, if you want to use that. The important thing is that in designing your app, you need to remember that JavaScript and ColdFusion operate in different contexts--the former on the client (browser), the latter on the server. While this is certainly not an impediment to making a very robust JavaScript-fronted app (or even an app that just used JavaScript here and there to talk to ColdFusion), you do have to design a bit differently than you would with a non-JS app, so it's important to get the difference clear and go from there.
How to maintain serverside validation and clientside validation
When we have some controls in ASP.NET, it's of course preferable to use both clientside and serverside validation. Clientside for usability purposes, and serverside for security purposes. However, this easily gets quite ugly. Of course, if your validation is a length check or a regular expression, it is easy to maintain. However, let's say that your validation is more advanced - what to do? One way is to implement the exact same functionality in both server code and Javascript. Another way is that both calls a webservice with the same logic, but then your AJAX call has to be synchronous which in general is bad practice. So, my question is, what's the best way to perform both serverside and clientside validation?
Your ajax call does not have to be synchronous for validation; it just needs to be quick: When the user tabs off a field, say, an ajax call is made to the service and the field displays a spinner next to it, indicating the server is validating the data. If the service returns success, the spinner is replaced by a green check mark. If the service returns failure, a red X, perhaps with explanatory text. When the user finally clicks submit, the same validation takes place on the server (synchronously or not). Another option (besides #user133811's of using JS is both places) is to find a cross-compiler, which will compile your language of choice into JavaScript. These may or may not be available, however. I've generally found that doing complex validation on the client is unnecessary and undesirable. Yes, let me know a field is required, or if a username exists (via ajax), or simple things, but if this is a complex mortgage application or something, I expect I'll have to submit before finding out I need to include my W2 if I declare my pet goldfish as co-signatory (or whatever, you get the point).
the same code for server and client would mean that your server needs to use JS. Node could be something you could potentially use.
How to update variable dynamically within Rails app
I am writing my first Rails app using the twitter gem. I'm simply retrieving search results and trying to cycle through them individually every 5 seconds or so. My thought was to create a variable and have this variable represent the array index and simply update this variable dynamically with Javascript (every 5 seconds or so). What's the best way to achieve this on the client-side? AJAX? Javascript? Does this make sense? I will be glad to provide more context if helpful. Thanks.
Sounds you're trying to build a "recent tweets" marquee of some sort. Without knowing your requirements, you could try simply loading the ten most recent tweets in Rails, putting them in ten hidden divs, and then using jQuery just to cycle through the different tweets on the page. If it is a requirement to "update" the most recent tweets without the user refreshing the page, then yes, you'd probably need an AJAX call.
It's hard to tell what you think you're asking: by the time your JavaScript is executing the server is no longer involved. If you want to update some sort of count on the server side and persist it in a meaningful way, you can do so via Ajax. What are you actually trying to do, though?
Ruby runs on the server while JavaScript (usually) runs on the client. The Ruby generates an HTML document (perhaps with embedded JS) and the server delivers it to the client. At that stage the Ruby has finished executing. The only way to do anything further with Ruby would be to make a new HTTP request to the server. This could be done by following a link, submitting a form, setting location.href, using XMLHttpRequest or numerous other techniques. This would cause the Ruby program to be executed again (or a different one to be executed) which would do whatever it did with the input data. You cannot simply "set a variable" on the server from the client.
In my particular case, I used ruby's .to_json method to convert the data and then manipulated it with javascript. This gave me the flexibility to loop through the data pretty seamlessly. Atleast it seemed to work for my particular situation. Thanks for the help guys!