Angular JS 和 Symfony2
Posted
技术标签:
【中文标题】Angular JS 和 Symfony2【英文标题】:Angular JS and Symfony2 【发布时间】:2015-12-31 05:35:03 【问题描述】:我目前正在使用 Symfony2 进行一个项目,并寻求一些建议。
我正在考虑以两种 (2) 不同的方式混合应用程序a) 登录页面应使用带有 CRF 令牌的传统形式并让 symfonty2 处理它。 b) 所有内部页面(可能是模块)我希望它们不是 AJAX,但内部的其他活动应该像单个页面一样。
例如我有一个员工模块。当用户单击它完全从服务器加载时(所有模板和表单等),现在员工模块下的每个活动(如 add/update delete/view 等)都应通过 AJAX 加载并返回响应在 JSON 中,即 AngularJS。
我目前正在考虑使用 FOSUserBundle 在初始请求时返回 html,然后根据请求类型 Accept: application/json 它将返回 JSON(还记得 add/updat delete/view 部分吗? )。
我的问题是使用 Angular Partials (html) 文件还是 Symfony2 Twig 更好?还是使用 Angular JS 会更好,但让这些部分由 Symfony2 树枝呈现? (我在这里考虑表单,想从客户端和服务器端验证)
有没有人遇到过类似的问题,如果有,那么使用 AngularJS 和 Symfony2 或任何其他框架开发 HYBRID 应用程序的方法是什么?任何相关的想法表示赞赏。
【问题讨论】:
这是不错的幻灯片分享 - slideshare.net/aperic/symfony2-and-angularjs。实际上在谷歌你可以找到很多关于如何让 Symfony2 和 Angular JS 成为朋友的文章。如果我理解正确,您需要在 Symfony2 上构建 REST api 并使 Angular App 发送请求以执行正确操作。希望这对您有所帮助:) 感谢您的评论,我在视频中有这些幻灯片,但主要侧重于 SPA,而我正在寻找混合应用程序。 我遇到了类似的问题,我发现的一个解决方案是如果路由是通过 Angular 调用的,则传递一个变量。例如,动作defaultAction
可以接受一个变量isAngular
(或者你喜欢的调用):控制器检查这个变量是否存在,如果存在返回一个你可以在Angular中使用的JSON响应,而不是完整的呈现的页面。无论如何,很大程度上取决于您要实现的目标。
如果应用程序更复杂,拥有单独的路由来管理获取完整页面的调用和获取 JSON 数据的调用可能更有用:这取决于控制器的复杂性。
自一年半以来,我一直在使用这样的 web 应用程序:后端 Symfony2 和前端 AngularJS,以及 FOSUserBundle。您可以将 Angular 模板放在 Twig 模板上,但我建议您将它们分开。您可以在脚本或布局(使用 ng-app 指令)上打开 Angular 应用程序,然后只使用自定义指令来调用您的 Angular 模块。你可以问我一些具体的例子,但我建议你仔细阅读 Angular 文档。
【参考方案1】:
我的情况和你一样。 AngularJS+Symfony2项目,REST API,使用FOSUserBundle登录等
...而且每一种方式都有利有弊,所以没有正确的方式,我只想说我做了什么。
我选择 AngularJS 原生模板,没有 CSRF 验证,使用 Twig 构建的基本模板,服务器端验证,使用 FOSJSRoutingBundle,以及一些帮助程序(BuiltResponse 和 BaseController)。
为什么选择原生模板?
通过逐字逐句的使用,我们解决了变量问题,但我们的模板中的逻辑会更复杂。
我们还将有一个可扩展性较低的应用程序。我们所有的表单模板都在 Symfony 应用程序中执行请求,AngularJS 的最佳优点之一是从存储服务(如 S3)或 CDN(如 Cloudfront)加载我们的控制器、模板等。由于没有服务器端处理,我们的模板会加载得更快。即使有缓存,Twig 显然也更慢。
根据我自己的经验,Twig 和 AngularJS 模板都很难管理。我开始将它们一起制作,但管理起来很痛苦。
我做了什么?
我在前端创建了静态模板,具有相同的字段名称,这不是很好。每次更新表单时,我们都需要手动更新模板。但这是我找到的最好方法。由于字段名称相同,我们在 Angular 控制器中调整模型名称不会有问题。
如果您将软件创建为服务,那么无论如何您都需要这样做。您不会在移动应用程序中从应用程序加载表单模板,对吗?
为什么没有 CSRF 验证?
显然,我们不在 REST API 中使用 CSRF 验证。但是,如果你想这样做,你需要在每次加载表单时发出请求,以获取 CSRF 令牌。真的,真的很糟糕。因此,我们创建了一个 CRUD,还需要创建一个“csrf-CRUD”,还有 4 条路线。这没有任何意义。
我做了什么?
我在表单中禁用了 CSRF。
基础模板?!
是的。基本模板只是在我们的应用程序中加载任何路由。这是我正在做的事情:
如果您使用的是 html5 angularjs url,这将帮助我们避免当用户直接访问某些应用程序 URL 时出现错误。就这么简单。
服务器端验证,为什么?
如果我们在 Angular 中进行验证,我们需要在服务器端进行相同的操作,因此我们需要维护 2 个验证代码。那是痛苦的。我们在表单中所做的每一次更改,我们都需要更改前面的验证,后面的验证以及 Angular 静态表单。真的,真的很痛苦。
我做了什么?
我基本上使用 Symfony 约束进行了服务器端验证。对于每个请求,应用程序都会验证表单并检查是否发现任何错误,如果是,它会获取第一个错误并将其作为响应发送。
在 AngularJS 中,应用程序检查 errors 键内是否有任何错误。因此,我们在所有应用程序中都使用了一个进程来执行任何表单请求。是这样的:
路线呢?
还有另一个问题:路线。直接放url不是可靠的方法。如果我们更改 url 中的任何内容,则该路由将消失,用户不会喜欢。
要解决这个问题,我们可以使用FOSJsRoutingBundle。使用该库,我们可以将路由名称直接放在 Angular 控制器中,它将填充路由的确切 url。它与 Symfony 完全集成,因此参数可以很好地工作。
我们可以直接使用url来代替:
Routing.generate('panel_products_show', id: $routeParams.product_id);
瞧!我们得到路由 url。
这将解决您遇到的大部分问题。但还有更多。
问题 1 - 表单输入
Symfony 的表单通常有一个前缀,比如“publish_product”,所以每个字段都有一个类似[publish_product]name
的名称。啊,这对我来说是个问题。
在 Angular 中,publish_product 不被视为数组。您需要使用单引号来执行此操作,例如 ['publish_product']name
。这真的很糟糕,我们需要更改每个键才能使用这种格式。在 AngularJS 中,我是这样做的:
formData('[publish_product]name')
绝对愚蠢。
最好的解决方案是简单地删除 Symfony 中的表单前缀,使用 createNamedBuilder
方法而不是 createBuilder
。我让第一个参数为空,是的,我们不再需要使用前缀。现在,我们使用:
formData.name
好多了。
问题 2 - 难以维护的路线
每个请求都可以返回任何东西,我需要重复很多代码。这真的很难维护,所以我只是创建一些应用程序规则、构建响应、BaseController 等。
createNamedBuilder
createNamedBuilder
是个大方法。对于我们拥有的每一种形式,我们都需要这样做:
解决起来很简单。我刚刚创建了一个 BaseController 并从中扩展了每个控制器。我创建了一个简单的方法来做到这一点。
对于每条路线,我们不需要重复 3 行,好多了。
回复
当我的应用程序开始增长时,我遇到了一个严重的问题:我的所有反应都不同。这真的很难维持。对于我所做的每个请求,有时我使用“响应”,有时使用“数据”,错误消息在响应中丢失,等等。
所以,我决定创建一个 buildResponse,我只需要设置一些参数,就可以为每条路由获得相同的结果,甚至是 GET 路由。
response
键显示状态和消息。它可以是错误或成功,消息是一个可选字段,可以为空。 例如,带有消息“您创建了产品”的成功状态。
data
键显示我需要的任何信息。例如,用户添加了产品,现在他需要链接才能看到它。在数据中,我输入了帖子的 url,我可以很容易地从 AngularJS 控制器中获取它。
notifications
是我的业务逻辑的特定键。每个操作都可以向用户返回一个通知。
你有什么钥匙并不重要。最重要的是有一个标准化的响应,因为当您的应用程序增长时,它会非常有帮助。
这是来自我的控制器的路由:
完全标准化。 Scrutinizer 代码质量工具说我所有的路线都是重复的。 :D
拥有一个 BaseController 和一个 builtResponse 会对你有很大帮助。当我开始重构我的代码时,每条路线丢失了大约 4-10 行。
详情: getFormError
返回表单的第一个错误。这是我的方法:
public function getFormError(FormInterface $form)
if ($form->getErrors()->current())
return $form->getErrors()->current()->getMessage();
return 'errors.unknown';
... buildResponse 中的参数是: 1.状态。我从 BaseController 中的一个常量中得到它。它可以更改,所以我认为不要在每条路线中使用字符串值很重要。 2.翻译信息。 (我使用 preg_match 来检查它是否有翻译格式,因为 getFormError 已经翻译了错误)。 3.数据(数组)参数。 4.通知(数组)参数。
我还有其他问题
到目前为止,该项目只有一种受支持的语言。当我开始使用多语言版本时,我将遇到另一个大问题:维护两个版本的翻译:后端消息和验证以及来自前端的文本。这恐怕会是个大问题。当我得到最好的方法时,我会更新这个答案。
我花了几个月的时间才得到这种方法。如此多的代码重构,未来可能还会更多。所以我希望它可以帮助不需要这样做的人。
1.如果我有更好的方法来做到这一点,我会更新这个答案。
2.我不擅长写英文,所以这个答案可能会有很多语法错误。抱歉,我正在修复我看到的内容。
【讨论】:
一年多后:你还在用那个方法还是有什么大问题发生?以上是关于Angular JS 和 Symfony2的主要内容,如果未能解决你的问题,请参考以下文章
ngRoute (angular-route.js) 和 ui-router (angular-ui-router.js) 模块有什么不同呢?
Angular 8 和 jest - 找不到文件:jest-preset-angular/InlineHtmlStripStylesTransformer.js