I need to code a page that will have its template defined in the server. The page makes an HTTP GET to the server and get a string that will be the template.
I'm trying to follow an example I found here: runtime-content.component.ts
However, in this template I'm going to have references to other components, such as <app-hello></app-hello> and when I try to render that I get that 'app-hello' is not a known element.
How can I tell this component to use a certain module or recognize certain components?
You just have to include the component references here (declarations):
#NgModule({ imports: [CommonModule, RouterModule], declarations: [decoratedCmp, ...declarations] })
class RuntimeComponentModule {}
And to send data add on compileTemplate
(this.componentRef.instance as any).data = this.data;
So your wrapper will have data available to be used on your template.
And on your dynamic template you can use data to send info to other components:
<app-something [content]="data?.content"></app-something>
Related
I am new to Angular and I just started learning it recently. I came across the concept of Databinding in Angular. I was able to understand the syntax and stuff but there were some questions that I couldn't find an answer for. These are the queries I had:
When we export a class from the component TS file, we can use the class properties in HTML file. For eg: Databinding a class property to a HTML element works. But how does this HTML element know the class or the class attribute? How does the HTML file have access to it?
Why exactly are we exporting a class for a component to be used? Is the component a class too? If yes, then wehen we use the component are we calling that class and this leads to rendering the HTML and CSS mentioned in the component?
Please let me know.
Answering your question in details requires having an in-depth knowledge about how Angular internally works, but here's a starting point:
I've generated a component using angular CLI:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-example',
templateUrl: './example.component.html',
styleUrls: ['./example.component.scss']
})
export class ExampleComponent implements OnInit {
public myProperty: number = 0;
constructor() { }
ngOnInit(): void {
}
}
So:
Is the component a class too?
Yes, as you can see from the labels "export class", your component is first of all a regular JS class, which implements the OnInit interface, has an empty constructor and defines a public variable.
If yes, then when we use the component are we calling that class?
Exactly: Angular does a bit of magic (see the next point of the answer), so whenever finds a html tag named <app-example></app-example>, it creates an ExampleComponent instance and replaces that tag with the html you defined on example.component.html
and this leads to rendering the HTML and CSS mentioned in the component?
The magic happens just above the class definition: Angular heavily relies on Typescript Decorators, which are an (still) experimental feature of Typescript. Decorators allows you (or Angular in our case) to alter the behaviour of a class, for example by intercepting methods call, property changes (did you just say databinding?) and constructor parameters (and this is how Angular's dependency injection works).
In the #Component decorators, which is linked to the below ExampleComponent class, you're defining three things:
the selector, or tag name that Angular will search in the DOM and replace with your component's html
Where to find your component's html, which will be linked to each of your ExampleComponent instance
Stylesheets for your component's html
So, when a property on your component changes (for example myProperty), Angular intercepts that change thanks to the #Component decorators you've defined (to understand how, do a little search about decorators), and will re-render his html. Inserting that property value on a paragraph like <p>{{myProperty}}</p> is just a matter of string replacement.
So, now you have the answer to your first question:
But how does this HTML element know the class or the class attribute? How does the HTML file have access to it?
It's not the html that knows which component it belongs, it's the component (or Angular that handles that component) that knows which html has to render and which css needs to apply.
Why exactly are we exporting a class for a component to be used?
This is simply to let Angular know that we have defined a component. Exporting something from a .ts file makes it available on the rest of the project, and particularly on the AppModule file, where you will find your ExampleComponent among the declarations array:
#NgModule({
declarations: [
AppComponent,
ExampleComponent
],
// Something else
I am working on a Vue application that's living in a Laravel project. I bind my vue instance to an id that's placed in a blade file.
What I would like to do is to pass the logged user to my Vue instance from Laravel/blade. Is there a way to do this? I know you can pass data through props but this here is just a regular div with an id of #root that's binding the Vue instance. I know how to get the logged user, but I am specific looking for an way to directly pass the data from blade to my vue instance.
app.js
// Require the deps from laravel (jQuery, axios, Bootstrap)
require('./bootstrap');
// Import vue deps
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter)
// Import the router & routes
import router from './routes'
// Init a new vue instance
const root = new Vue({
el: '#root',
data: {
User: name here..
},
router
});
Blade
#extends('layouts.app')
#section('content')
<!-- pass user below -->
<div id="root"></div>
#endsection
In your blade file, pass the logged user information to a javascript global variable. Let's say you just want the user id and name:
<script>
window.auth_user = {!! json_encode([
'id' => auth()->user()->id,
'name' => auth()->user()->name
]) !!};
</script>
Then in your javascript, you can access the auth_user global variable.
For example, to log the user name:
console.log(auth_user.name)
or
console.log(window.auth_user.name)
You have few options (I think I not list all) e.g:
You can pass data by converting them to json and write as HTML element or attribute and then read it from vue using e.g. document.querySelector(...) - more info here: Best way to store JSON in an HTML attribute?
You can change a littlebit architecture and create separate (Restful) API which your vue components will be use via ajax to read data (e.g. you can create GET api/v1/currentUser do read current logged user)
Completly change your architecture - to "microservices" - so in laravel only create Restful API, and creatte SEPEARATE project with vue (and NO laravel) user interface which use that API (it is modern approach to separation backend from frontend). You will face CORS problem in this approach but its no so hard (only at first time).
You might want to take a look at the PHP-Vars-To-Js-Transformer
package. You can use it either in your controller or in a #php directive.
Probably not a good practice with VueJS though.
I am able to access DOM elements components like below
declare var jQuery: any;
declare var $: any;
//component stuff
$('.my_class').innerHeight();
I am trying to implement the same inside the service class, but dom elements and template is not accessible in the service class.
p.s: this is not duplicate of how to access them in components.
You can access DOM from an Angular service using the plain javascript document object, with some little additions to your service:
// We import not only "Injectable", but "Inject" too, from #angular/core
import { Injectable, Inject } from '#angular/core';
// We import DOCUMENT from #angular/common. Be careful, because the old import from '#angular/platform-browser' is deprecated.
import { DOCUMENT } from '#angular/common';
// Our standard service class in the usual way
#Injectable()
export class LoadingSpinnerService {
// In the constructor we inject a dependency to DOCUMENT, of type HTMLDocument
constructor(#Inject(DOCUMENT) private document: HTMLDocument) {
// We create a new div in the DOM, child of the body tag, <div id="LoadingSpinner"></div>
var NewDomElement = this.document.createElement("div");
NewDomElement.setAttribute("id", "LoadingSpinner");
document.body.appendChild(NewDomElement);
}
}
As you will probably know, you crete the service from the command line with something like:
ng g s loading-spinner
Don't forget to edit the app.module.ts to add the "import" to the service, and the item to the "providers" array in its "#NgModule" decorator:
import { LoadingSpinnerService } from './WHATEVER-DIRECTORY-YOU-CREATE-THE-SERVICE/loading-spinner.service';
(...)
providers: [LoadingSpinnerService,
(...)
About the topic of using angular services only for data, i don't agree. As you can read in the official architecture guide for services:
https://angular.io/guide/architecture-services
Service is a broad category encompassing any value, function, or feature that an app needs.
A component can delegate certain tasks to services, such as fetching data from the server, validating user input, or logging directly to the console.
The provided example just in this documentation is for a log data service.
Hope this helps someone.
You can't in services. You can do it javascript way like document.getElementById.
In components and directives You can use ViewChild from #angular/core
HTML:
<div class="my_class" #myElement></div>
TS:
import { ElementRef, ViewChild } from '#angular/core';
#ViewChild('myElement') myElement:ElementRef;
console.log(this.myElement.nativeElement.offsetHeight); // inside any function.
I guess you cannot directly access with CSS selectors by Angular way. Alternatively, you can just use plain old JavaScript
document.getElementsByClassName("my-class");
Note: You can only do this in components and directives not inside services
Why Services
Components shouldn't fetch or save data directly and they certainly shouldn't knowingly present fake data. They should focus on presenting data and delegate data access to a service.
Source: https://angular.io/tutorial/toh-pt4
In Simpler terms:
component, directive for presenting, manipulating and interacting with DOM
services are for data handling between your component and backend
In my angular app I use angular-redux for application state management. In my main module I defined my redux store. Like this:
export class MainModule {
constructor(private ngRedux: NgRedux<MainAppState>,
private devTools: DevToolsExtension) {
let enhancers = [];
if (environment.production === false && devTools.isEnabled()) {
enhancers = [...enhancers, devTools.enhancer()];
}
this.ngRedux.configureStore(
reducer,
{} as MainAppState,
[],
enhancers);
}
}
I created new child module, which contains some components. These components should access to application state. In one of these components I access via #select to store, but this doesn't work. Here is how I access to store:
export function getLanguage(state: LanguageState) { return state.userLanguage; }
And this code I have in my ChildComponent class:
export class ChildComponent implements OnInit {
#select(getLanguage) savedUserLanguage$: Observable<LanguageState>;
// more code
}
How can I access to application state store from child modules? What should I import in child module? Will It be better to create own module only for redux store handling? Maybe I forgot something?
I use Angular v4 and #angular-redux/store v6.
I'd recommend creating a separate module that just contains your store, e.g. StoreModule. You can then import your StoreModule into all your child modules and access your store from there.
This is the way they go in the official example app:
StoreModule: https://github.com/angular-redux/example-app/blob/master/src/app/store/module.ts
Child Module: https://github.com/angular-redux/example-app/blob/master/src/app/elephants/module.ts
Component in child module: https://github.com/angular-redux/example-app/blob/master/src/app/elephants/page.ts
I was thinking about refactoring some ugly old JavaScript code that uses prototypal inheritance into an Angular 7+ project. I was asking myself pretty much the same question. Inspired by my udemy Angular course, I tried an experiment with a ngrx store and lazy loaded modules.
(Keep in mind that ngrx is SIMILAR to #angular-redux, but it's NOT the same thing. See https://ngrx.io/docs for details.)
Here it is.
I create the store in the main module with StoreModule.forRoot and in each lazy loaded module, I create a reference to the store with StoreModule.forFeature.
(See https://ngrx.io/api/store/StoreModule for details.)
When I dispatch actions on the store with the lazy loaded components, those actions (and corresponding reducers) seem to change the value to which the main app component subscribes.
Also, when I dispatch actions on the store with the main app component, those actions (and corresponding reducers) seem to change the value to which the lazy loaded components subscribe.
Also, it's hard to explain what I did in a simple 200-500 character block so I had to use a github project.
I'm trying to create a Base Class for my controllers, so I can avoid duplication of code.
The problem here is that it is throwing me errors whenever I try to use it.
"Assertion Failed: You attempted to define a {{link-to "inventory"}} but did not pass the parameters required for generating its dynamic segments. Could not find module controllers/base-inventory imported from frontend/controllers/inventory"
To create my base controller I am using ember cli and this is what I did:
ember g controller base-inventory
Then
// base-inventory.js
const BaseInventory = Ember.Controller.extend({
//my code...
});
export default BaseInventory;
In the controller where I want to use this base class I did the following
import BaseInventory from 'controllers/base-inventory';
// also tried import { BaseInventory } from 'controllers/base-inventory';
// and export default new BaseInventory({});
export default BaseInventory.extend({
//more code here...
});
Any thoughts of what I am doing wrong?
I didn't plan to use mixins, because it doesn't seem the best option here at first. I am not really sure about the sharing content, which mixins provide. I don't think it would be a problem since I'm trying to inherit within controllers, but as I said I'm not sure about how it really works.
If it's not possible to do the way I'm trying to, I'll write a mixin.
Both files are in the same folder structure so import path should be like ./base-inventory
import BaseInventory from './base-inventory';