I'm thinking of separating our MVC-based website's front-end into few ponents and I was thinking about using Angular for that purpose (e.g. creating cart application that I can include to my view afterwards).
Problem is that I need to pass few variables into that application and I'm wondering how to do that safely and professionally. I was thinking about something like:
- I'm going to
ng build --prod --aot
- I inject all my scripts to my view
- I inject variables passed to view to my app
... and "code" representation for my thoughts:
Controller:
public function viewAction()
{
$this->view->addCss('angular/app/styles.css'); // adds styles for cart app
$this->view->addJS('angular/app/scripts.js'); // adds scripts for cart app
$this->view->setJSVariable('idCustomer', 555); // sets global var idCustomer
}
View:
<!-- bunch of HTML for view ... -->
<script>
// CartApp would be an angular app.
CartApp.use([
idCustomer
]);
</script>
So my question is... would it be possible (and would it be a good solution) to get the CartApp
as an object and then make some function (like use
in above example) that would set/pass the data? (let's say to some globally provided service/ponent or anything else). Or is there any other way to do this? Like taking the variables from inside the application from the window object? (as they're going to be bound to the window anyways). Thank you.
I'm thinking of separating our MVC-based website's front-end into few ponents and I was thinking about using Angular for that purpose (e.g. creating cart application that I can include to my view afterwards).
Problem is that I need to pass few variables into that application and I'm wondering how to do that safely and professionally. I was thinking about something like:
- I'm going to
ng build --prod --aot
- I inject all my scripts to my view
- I inject variables passed to view to my app
... and "code" representation for my thoughts:
Controller:
public function viewAction()
{
$this->view->addCss('angular/app/styles.css'); // adds styles for cart app
$this->view->addJS('angular/app/scripts.js'); // adds scripts for cart app
$this->view->setJSVariable('idCustomer', 555); // sets global var idCustomer
}
View:
<!-- bunch of HTML for view ... -->
<script>
// CartApp would be an angular app.
CartApp.use([
idCustomer
]);
</script>
So my question is... would it be possible (and would it be a good solution) to get the CartApp
as an object and then make some function (like use
in above example) that would set/pass the data? (let's say to some globally provided service/ponent or anything else). Or is there any other way to do this? Like taking the variables from inside the application from the window object? (as they're going to be bound to the window anyways). Thank you.
1 Answer
Reset to default 16 +50So I was going to suggest using input bindings... I've done that before in AngularJS but was surprised to find that using input bindings on the root ponent isn't supported yet. You can have fun reading this giant issue: https://github./angular/angular/issues/1858
The best post I saw there was Rob Wormald's which had a couple of suggestions:
- to the initial question, if you really want to grab data from the root, that's possible via https://plnkr.co/edit/nOQuXE8hMkhakDNCNR9u?p=preview - note that it's not an input (because there's no angular context outside of it to do the input...) - its a simple string attribute which you'd need to parse yourself.
- ideally though, you'd do as @robtown suggested and actually pass the data in javascript, rather than passing it as a DOM string and retrieving / parsing it yourself (which has never really been a supported case in angular, despite the explicit-warned-against usage of ng-init in angular1 to acplish this) - see https://plnkr.co/edit/PoSd07IBvYm1EzeA2yJR?p=preview for a simple, testable example of how to do this.
So the 2 good options I saw were:
Add normal HTML attributes to the root ponent:
<app-root appData='important stuff'></app-root>
and use
ElementRef
to fetch them:@Component({ selector: 'app-root' }) export class AppComponent { constructor(el: ElementRef) { console.log(el.nativeElement.getAttribute('appData')); } }
Would probably work best if you are just dealing with strings or config flags. If you are passing JSON, you will need to manually parse it.
Have the server render the data as JavaScript and import it in your app:
Have the server render something like this to a script tag or JS file that is loaded before Angular is bootstrapped:
window.APP_DATA = { ids: [1, 2, 3] }
Tell your NgModule about it using a provider:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.ponent'; @NgModule({ providers: [{ provide: 'AppData', useValue: (<any> window).APP_DATA }], bootstrap: [AppComponent] }) export class AppModule { }
And then use it as a normal Angular service:
import {Component, Inject} from '@angular/core'; @Component({ selector: 'app-root' }) export class AppComponent { constructor(@Inject('AppData') appData) { console.log(appData.ids); } }