Is there a posibility to set variables dynamically?
My code looks like this. The if gets true but how do I (if possible) set the variable to true dynamically?
import { Component, OnInit, } from '#angular/core';
import {forEach} from "#angular/router/src/utils/collection";
#Component({
selector: 'app-menu',
templateUrl: './menu.component.html',
styleUrls: ['./menu.component.css']
})
export class MenuComponent implements OnInit {
menuContentSize = false;
menuContentBackground = false;
menuContentImages = false;
menuContentText = false;
menuContentFrame = false;
menuOptions: string[] = ['menuContentSize',
'menuContentBackground',
'menuContentImages',
'menuContentText',
'menuContentFrame'];
constructor() {
}
ngOnInit() {
}
menuOptionSelected(event){
this.menuOptions.forEach(function(element){
if(element == event){
// Set name of element(variable) to true
// In my dreamworld this.element = true; will be e.g. this.menuContentSize = true;
}
});
}
}
this.menuOptions.forEach(function(element){
needs to be
this.menuOptions.forEach((element) => {
if you want to use this to reference to the current component instance
See also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
I'm not sure about the rest of your question.
I guess what you want is
this[element] = true;
which sets this.menuContentSize to true if element holds the string value 'menuContentSize'
Related
I am trying to change the URL when someone clicks on a choosen language using ngx-translate. I guess I should do it by subscribing to language change events and then modifying the current url to reflect the chosen language, Since I am a newbie I am not sure if I need a service to do it, or may be another way to solve it.
I want to change from this:
https://amarello.cloud/es/
To this:
https://amarello.cloud/en/
,depending on the choosen language.
This is my 'header.component.ts'
import { Component, OnInit } from '#angular/core';
//For translate language
import { TranslateService } from '#ngx-translate/core';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
menu: boolean = false;
submenu: boolean = false;
submin: boolean = false;
constructor(private translate: TranslateService)
{ translate.addLangs(['es','en']);
translate.setDefaultLang('es');
}
ngOnInit(): void {
}
open() {
if (this.menu == false) {
this.menu = true;
} else {
this.menu = false
}
if (this.submin == false) {
this.submin = true;
} else {
this.submin = false
}
}
openSub() {
if (this.submenu == false) {
this.submenu = true;
} else {
this.submenu = false
}
}
useLanguage(language: string): void {
this.translate.use(language);
// this.translate.onLangChange().subscribe(trans => {
// })
}
}
I am making custom component for dropdown. I have one config object which I am initializing in ngOnInit(), and I am combining the default configs and configs provided by user as an #Input(), But at run time from parent component, If I am making any changes in my config object, it is not updating in ngOnChanges() method of my child.
I tried this:
child component
#Input() config: MyConfig;
#Input() disabled: boolean
ngOnChanges() {
console.log('config', this.config); // this is not
console.log('disabled', this.disabled); // this is detecting
}
parent component html
<button (click)="changeConfig()">Change Config</button>
<app-child [config]="customConfig" [disabled]="newDisabled"></app-child>
parent component ts
newDisabled = false;
customConfig = {
value: 'code',
label: 'name',
floatLabel: 'Select'
};
changeConfig() {
this.customConfig.value = 'name';
this.newDisabled = true;
}
for disbale variable it is working, but for config it is not, Am I doing something wrong? please help
You problem is that you ngOnInit is setting the config variable to a new object. Since the #Input() is called once, this breaks your reference to the original object, and changes will not be detected.
You can fix this by using a setter and getter. I have added a stack blitz to demo this bellow.
Blockquote
parent component
import { ChangeDetectorRef, Component, VERSION } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
newDisabled = false;
customConfig = {
value: 'code',
label: 'name',
floatLabel: 'Select',
};
changeConfig1() {
this.customConfig.value = 'name1';
this.newDisabled = true;
console.log('Change Config 1');
}
changeConfig2() {
this.customConfig.value = 'name2';
this.newDisabled = true;
console.log('Change Config 2');
}
}
child component
import { Component, Input } from '#angular/core';
class MyConfig {}
#Component({
selector: 'hello',
template: `<h1> config: {{config | json}}</h1><h1> disabled: {{disabled}}</h1>`,
styles: [],
})
export class HelloComponent {
private _defaultConfig = {
key: 'default',
};
#Input() disabled: boolean;
private _config: MyConfig;
#Input() config: MyConfig;
set () {
if (!this.config) {
this.config = new MyConfig(); // it is a class
} else {
this._config = {
...this.config,
...this._defaultConfig,
};
}
}
get () {
return this._config;
}
ngOnChanges() {
console.log('config', this.config);
console.log('config', this._config);
console.log('disabled', this.disabled);
}
}
The problem is that the change detection is only triggered if the object customConfig is changed. In you example, only the value property is updated. What you can do is the following in the parent.component.ts:
changeConfig() {
this.customConfig = Object.assign(this.customConfig, { value: 'name'});
this.newDisabled = true;
}
This will create a new config object which contains the updated value property and all the other old properties of the old customConfig.
Input object are compared by reference, so if you want to reflect changes in your child component and trigger ngOnChanges do this:
changeConfig() {
this.customConfig = {...this.customConfig, value: 'name'};;
this.newDisabled = true;
}
And also move your below code from ngOnInit to ngOnChanges, chances are that at the time of initialisation input chagnes may not be available.
if (!this.config) {
this.config = new MyConfig(); // it is a class
} else {
this.config = {
...this._defaultConfig,
...this.config
};
}
If I apply a child component (which uses ControlValueAccessor) in the parent and the write something in the child component, everything is passed on to the parent accordingly.
However if I try to write something in the parent component and then pass it on to the child component, nothing is available in the input. How can I fix this?
Just to be clear this is what the preferred behavior is supposed to be:
child string value = written in child
parent string value = written in child
but whenever I type in text in the parent input I get the following:
child string value = (empty)
parent string value = written in parent
[app.component.ts]
import { Component, VERSION } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular ' + VERSION.major;
external = "";
}
[app.component.html]
<p>
Start editing to see some magic happen :)
</p>
<app-custom-input [(ngModel)]="external" name="externalVal"></app-custom-input>
<input [(ngModel)]="external"/>
external: {{ external }}
[custom-input.component.ts]
import { Component, forwardRef, HostBinding, Input, SimpleChanges } from "#angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "#angular/forms";
#Component({
selector: 'app-custom-input',
template: '<input [(ngModel)]="value"/>local: {{val}}',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CustomInputComponent),
multi: true
}
]
})
export class CustomInputComponent implements ControlValueAccessor {
onChange: any = () => {};
onTouch: any = () => {};
val = "";
set value(val) {
if (val !== undefined && this.val !== val) {
this.val = val;
this.onChange(val);
this.onTouch(val);
}
}
writeValue(value: any) {
this.value = value;
}
registerOnChange(fn: any) {
this.onChange = fn;
}
registerOnTouched(fn: any) {
this.onTouch = fn;
}
}
Also available at: https://stackblitz.com/edit/angular-ivy-ker4g5?file=src%2Fapp%2Fcustom-input%2Fcustom-input.component.ts
I think the problem is that you did not define a getter for value. So you'd have to modify your code as follows:
custom-input.component.ts
set value (val) {
/* ... */
}
get value () {
return this.val
}
This is a simplified example:
o = {
_n: null,
set name (v) {
this._n = v;
},
get name () {
return this._n;
}
}
// Setter
o.name = 'foo'
// Getter
// Without `get name()` -> `undefined`
o.name
I dont know if thats possible or not, but here is my ideia:
I have a class with methods and i want to present the method in a h1 Html component that matches the selected string.
Basically, i have a class of Pokemons, that each method is a pokemon, and i want to switch methods when my user select one type of pokemon.
Here is the code:
The service class (data) :
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class GenIService {
swampert = {
hp: 138,
atk: 121,
def: 110.63,
type: ' Water Ground',
};
}
The main page :
import { Component, OnInit } from '#angular/core';
import {GenIService} from "../Pokemons/gen-i.service";
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
genOne = [];
button1clicked= false;
button2clicked= false;
pokemon1Selected = '';
pokemon2Selected = '';
constructor(private gen1: GenIService ) {}
ngOnInit(): void {
console.log(this.gen1.swampert.hp);
}
buttonOneSelected() {
this.button1clicked= true;
this.button2clicked=false;
}
buttonTwoSelected() {
this.button1clicked= false;
this.button2clicked=true;
}
pokemon1SeletectedSwampert() {
this.pokemon1Selected = "Swampert";
}
pokemon2SeletectedVenusaur(){
this.pokemon2Selected = 'Venusaur';
}
The Html code:
<ion-item>
<ion-label>Name: {{}}</ion-label>
<ion-label>HP: {{this.gen1.{pokemon1Selected}.hp}}</ion-label>
<ion-label>ATK: {{}}</ion-label>
<ion-label>DEF: {{}}</ion-label>
<ion-label>Type: {{}}</ion-label>
</ion-item>
So, in the Html code i am tring to have this variable = this.gen1.swampert.hp, but switch the pokemon name with the variable of pokemon1Selected, that in this case is equal to "Swampert".
How can i do that?
Based on your declaration genOne is an array so I'm not sure what you are trying to get when you call
console.log(this.gen1.swampert.hp);
If I understand correctly what you need is something like this (provided you add name property to your pokemon object)
.ts
public getPokemonOneSelected() {
return this.genOne.find(x => x.name === this.pokemon1Selected);
}
HTML
<ion-label>HP: {{this.getPokemonOneSelected()?.hp}}</ion-label>
That '?.' is to prevent error if .find() returns undefined
As Phix pointed out it would be better to track selected pokemon with just 1 variable so instead of
pokemon1Selected = '';
pokemon2Selected = '';
just have
selectedPokemon = '';
...
public getSelectedPokemon() {
return this.genOne.find(x => x.name === this.selectedPokemon);
}
...
<ion-label>HP: {{this.getSelectedPokemon()?.hp}}</ion-label>
I am trying to implement the tab view component of Prime NG. but my tabs are dynamic in nature ie.
So when the container is loaded it sends multiple AJAX requests for data inside the component.(Maybe the component is initialized multiple times?)
Another thing, in one of the components, moving mouse gives Thousands of errors on the console.
ERROR Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed
ERROR CONTEXT [object Object]
Not sure why. Used the same component in another place and there was no issue.
Even if I remove the dynamic nature of the components and just place 4 static tabs, everything works perfectly.(Right now the same 4 components are coming from server).
Html Template:
<div class="col-md-12 padding0">
<div class="tabViewWrapper">
<p-tabView (onChange)="handleChange($event)">
<p-tabPanel header="{{tab.tabName}}" *ngFor="let tab of tabs" >
<dynamic-component [componentData]="componentData"></dynamic-component>
</p-tabPanel>
</p-tabView>
<div>
</div>
Component:
#Component({
selector: 'tab-view',
templateUrl: './tab-view.component.html',
styleUrls: ['./tab-view.component.scss'],
encapsulation: ViewEncapsulation.None,
entryComponents: [GenericDataTableComponent, SingleEditCategoryExplorerComponent, AssetsDataTableComponent]
})
export class TabViewComponent implements OnInit {
private ngUnsubscribe: Subject<void> = new Subject<void>();
private componentData = null;
private tabs: Array<any>;
private index:number;
private disabledTabs:Array<any>;
private disabledTabsWhenMetaDataClicked:Array<any>;
versionConfig = {
url: AppSettingProperties.DATA_TABLE_VALUES.VERSIONS_URL,
dateLocale: AppSettingProperties.DATA_TABLE_VALUES.LOCALE,
header: AppSettingProperties.DATA_TABLE_VALUES.VERSIONS_HEADER
};
relatedConfig = {
url: AppSettingProperties.BASEURL + AppSettingProperties.DATA_TABLE_VALUES.RELATED_ENDPOINT,
header: AppSettingProperties.DATA_TABLE_VALUES.RELATED_HEADER
};
constructor(private assetDataLoadedService: AssetDataLoadedService, private assetDetailsService: AssetDetailsService, private assetDetailDataModel:AssetDetailDataModel) { }
#ViewChildren(DynamicContainerComponent) dynamicContainers: QueryList<DynamicContainerComponent>;
ngOnInit() {
this.disabledTabs = [];
//Set items to be disabled when Metadata button is clicked
this.disabledTabsWhenMetaDataClicked = [AppSettingProperties.TAB_RELATEDITEMS, AppSettingProperties.TAB_VERSIONS];
//Disable the tabs as per the condistions
this.disableTabsAsPerRequirement();
//Assigning tabs
this.tabs = this.assetDetailsService.systemTabs;
}
getInitialSelected(tab){
return this.selectedTab == this.tabs.indexOf(tab);
}
get selectedTab():number{
return this.index;
}
set selectedTab(val:number){
this.index = val;
var defaultTab = this.tabs[this.index]['tabName'];
if(!this.assetDetailDataModel.catalogId){
this.assetDataLoadedService.assetDetailPublisher.subscribe(data=>{
this.loadComponentByTab(defaultTab);
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
});
}
else{
this.loadComponentByTab(defaultTab);
}
}
handleChange(e) {
let tabName: string = e.originalEvent.currentTarget.innerText;
this.selectedTab = e.index;
//this.loadComponentByTab(tabName);
}
loadComponentByTab(tabName:string){
switch (tabName) {
case AppSettingProperties.TAB_METADATA:
this.componentData = { component: AssetsDataTableComponent, inputs: {} }
break;
case AppSettingProperties.TAB_CATEGORY:
let categoryConfig: object = {"catalog_id":this.assetDetailDataModel.catalogId,"item_id":this.assetDetailDataModel.assetId};
console.log(categoryConfig);
this.componentData = { component: SingleEditCategoryExplorerComponent, inputs: { tabConfig: categoryConfig } }
break;
case AppSettingProperties.TAB_RELATEDITEMS:
this.componentData = { component: GenericDataTableComponent, inputs: { tabConfig: this.relatedConfig } }
break;
case AppSettingProperties.TAB_VERSIONS:
this.componentData = { component: GenericDataTableComponent, inputs: { tabConfig: this.versionConfig } }
break;
}
}
}
Dynamic Component:
import { Component, Input, ViewContainerRef, ViewChild, ReflectiveInjector, ComponentFactoryResolver } from '#angular/core';
#Component({
selector: 'dynamic-component',
template: `<div #dynamicComponentContainer></div>`,
})
export class DynamicComponent {
private currentComponent = null;
#ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) { }
// component: Class for the component you want to create
// inputs: An object with key/value pairs mapped to input name/input value
#Input() set componentData(data: { component: any, inputs: any }) {
console.log("Building Component Start");
if (!data) {
return;
}
// Inputs need to be in the following format to be resolved properly
let inputProviders = Object.keys(data.inputs).map((inputName) => { return { provide: inputName, useValue: data.inputs[inputName] }; });
let resolvedInputs = ReflectiveInjector.resolve(inputProviders);
// We create an injector out of the data we want to pass down and this components injector
let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicComponentContainer.parentInjector);
// We create a factory out of the component we want to create
let factory = this.resolver.resolveComponentFactory(data.component);
// We create the component using the factory and the injector
let component = factory.create(injector);
// We insert the component into the dom container
this.dynamicComponentContainer.insert(component.hostView);
// We can destroy the old component is we like by calling destroy
if (this.currentComponent) {
this.currentComponent.destroy();
}
this.currentComponent = component;
console.log("Building Component Finish");
}
}
Another thing is that the console start in dynamic component is shown 8 times.
While console finish is shown 4-5 times.
Seems really weird behavior.
As #echonax wrote in comment.
This is because you are trying to iterate something that is not an array.
Most probably this.tabs.
You can try and write out {{tabs|json}} in a div instead of the *ngFor
Since your response takes sometime to load your DOM will have tabs variable as undefined array.
To solve this initialize the variable to an empty array as below
tabs:Array<any> = []
or inside the constructor as
constructor(){
this.tabs = [];
}