How to fix <!--bindings={ "ng-reflect-ng-for-of": "" }--> - javascript

Im new to Angular. I am creating a button inside game-control component and using event binding and property binding. When i click the button numbers will be entered into an array continuously using setInterval method. I am passing the data between one component to another. The game-control component's selector is called inside the app component. The button worked fine with the click event but when i used the ngFor in order to iterate through the array and display the buttons also did not appear in the dom. Thanks in advance
game-control.component.html
<div class="row">
<div class="col-md-12">
<button
class="btn btn-primary"
type="button"
(click)="gameStart()">Start</button>
<button
class="btn btn-danger"
type="button"
(click)="gameStop()">Stop</button>
<br>
<p>{{element}}</p>
</div>
</div>
game-control.component.ts
export class GameControlComponent implements OnInit {
#Input() element:number;
#Output() createNumber= new EventEmitter<number>();
constructor() { }
ngOnInit() {
}
gameStart()
{
this.createNumber.emit(this.element);
}
}
app.component.html
<div class="container">
<div class="row">
<div class="col-md-12">
<app-game-control
(createNumber)='onStart()'
*ngFor="let myElement of myHoldings"
[element]="myElement"
>
</app-game-control>
</div>
</div>
</div>
app.component.ts
export class AppComponent {
myHoldings=[];
onStart()
{
setInterval(()=>{
this.myHoldings.push(this.myHoldings.length+1);
console.log("hello");
},1000);
}
}

The solution is to have an internal variable in the component that is assigned from the Input() variable
#Input() element:number;
internalNumber : number ;
ngOnInit() {
this.internalNumber = this.number;
}
And then use that variable to do bindings or emit events
For a similar question that might help
Click function not being called for a mat-menu with items generated with ngFor

Related

Angular, how to use directive based on an boolean

I use a ngFor to show different button. I need to use a directive on different button but not all.
So I make this code :
<div *ngFor="let btn of btns">
<button {{ btn.useDirective ? 'appMyDirective' : '' }} ></button>
</div>
But I get this error
Error: Template parse errors:
Unexpected closing tag "button". It may happen when the tag has already been closed by another tag.
update you directive to be trigger base on state as an example consider this πŸ‘‡
#Directive({
selector: '[appHello]'
})
export class HelloDirective {
#Input() appHello:boolean;
constructor(private elem: ElementRef, private renderer: Renderer2) { }
ngAfterViewInit() {
if (this.appHello !== false ) {
this.renderer.setProperty(this.elem.nativeElement, 'innerHTML', 'Hi 😎');
}
}
}
template
<div *ngFor="let btn of btns">
<button [appHello]="btn.useDirective">Hi </button>
</div>
if you set the value to be true the directive will work otherwise nothing will happen
demo πŸ”₯πŸ”₯
Your syntax is invalid. To archieve what you want, do something like this:
<div *ngFor="let btn of btns">
<button *ngIf="btn.useDirective" appMyDirective></button>
<button *ngIf="!btn.useDirective"></button>
</div>
Try using below instead.
<div *ngFor="let btn of btns">
<button appMyDirective *ngIf="btn.useDirective"></button>
<button *ngIf="!btn.useDirective"></button>
</div>

On a Keydown.enter event in input element within a child component is also calling a method that is defined in Parent Component

I have a parent component where user can select skills from a range of options and a child component where user can add their own skill if its not available on the parent component.
The issue is in child component, when a user enters skill into an input element on which I have an #keydown.enter event defined to call a method, to take the input and push it to an array and that all works. The only problem is when keydown.enter event is fired it's also calling a method that is defined in the parent component which changes the state of the options element.
// parent component
<div class="card-body">
<p class="card-subtitle font-weight-bold mb-1">Select Skills</p>
<button
v-for="skill in skills"
:key="skill.id"
:class="[skill.state ? skillSelectedState : skillNotSelectedState]"
class="btn btn-sm m-2" #click="addSkill(skill)"
:value="skill.category">
{{skill.skills}}
</button>
<clientInput></clientInput> // child component
</div>
<script>
import clientInput from './ClientSkillInput.vue'
export default {
data() {
return {
skills: [], // getting skills from an axios call
selectedSkills: [],
}
}
}
methods: {
addSkill(skill) { // this is the method getting called
if (!skill.state) {
this.selectedSkills.push(skill.skills);
skill.state = true;
} else {
let position = this.selectedSkills.indexOf(skill.skills);
this.selectedSkills.splice(position, 1);
// skill.state = false;
}
},
}
// child component
<template>
<div class="form-group mt-2">
<label class="d-block">Not what you're looking for?</label>
<div class="customWraper">
<div class="userSkillbox d-inline bg-secondary"> // will be using v-for to loop on userProvidedSkills and show the user inputs
Rrby on rails
<button class="userSkillboxBtn btn-sm border-0 text-white"> // to remove the input item
<i class="fas fa-times"></i>
</button>
</div>
<input v-model="userInput" type="text" class="d-inline border-0" placeholder="Type to add different skill" #Click="">
</div>
</div>
</template>
<script>
export default {
data() {
return {
isEditable: true,
userInput: '',
userProvidedSkills: [],
}
},
methods: {
addUserInput() {
this.userProvidedSkills.push(this.userSkill);
this.userSkill = '';
}
}
}
</script>
It is not clear where you’re adding the keydown event, but there 2 possible solutions:
1.Use a event modifier on the input to stop propagation
<input #keydown.enter.stop
2.Use the self event modifier on the parent component button
<button
v-for="skill in skills"
#click.self="addSkill(skill)"
:value="skill.category">
{{skill.skills}}
More about event modifiers here

Angular: Hiding/showing elements by ngIf or Toggling a class?

I'm wondering what the best way to approach this problem is and I'm very new to TypeScript and Angular. Using Angular 5.
Anyway. I have a list of elements on a page via a table.
This is the code that controls said list.
<tbody>
<tr class="text-center" *ngFor="let topic of topics">
<td *ngIf="!editTopicMode">{{ topic.name }}</td>
<td id="{{topic.id}}" *ngIf="editTopicMode">
<form>
<div class="form-group">
<input class="form-control" type="text" name="name" value="{{topic.name}}" />
</div>
</form>
</td>
<td>
<div *ngIf="!editTopicMode" class="btn-group btn-group-sm">
<button class="btn btn-link" (click)="editTopicBtnClick(topic.id)">
<i class="fa fa-pencil fa-2x" aria-hidden="true"></i>
</button>
<button class="btn btn-link">
<i class="fa fa-trash fa-2x" aria-hidden="true"></i>
</button>
</div>
<div *ngIf="editTopicMode" class="btn-group-sm">
<button class="ml-2 btn btn-sm btn-outline-secondary" (click)="cancelEditMode()">Cancel</button>
<button class="ml-2 btn btn-sm btn-outline-primary">Save</button>
</div>
</td>
</tr>
</tbody>
What I'm aiming to do is that if a user clicks on the pencil(edit) icon, then the adjacent div changes from just a regular td to an input and the edit/delete buttons to change to a cancelEdit/save edits button group. (I know that I need to change the html a bit because the buttons aren't in the form element currently, but I'm not there on the wiring it up part).
I've thought of two ways to do this. 1) with ngIf's so that I can conserve the elements that are rendered and the edit/cancel buttons toggle the editMode; or 2) use ngClass and toggle display:none css classes for the button clicked.
Right now, when you click the edit button, regardless of which edit button you click, it flips all the columns to inputs, rather than just the row the user wants to edit.
Here's my component ts:
import { Component, OnInit, TemplateRef, ElementRef, ViewChild, Inject } from '#angular/core';
import { Topic } from '../models/topic';
import { TopicService } from '../services/topicService/topics.service';
import { AlertifyService } from '../services/alertify/alertify.service';
import { ActivatedRoute } from '#angular/router';
import { DOCUMENT } from '#angular/common';
#Component({
selector: 'app-topics',
templateUrl: './topics.component.html',
styleUrls: ['./topics.component.css']
})
export class TopicComponent implements OnInit {
#ViewChild('topicId') topicId: ElementRef;
topics: Topic[];
newTopic: Topic = {
id: 0,
name: '',
};
editTopicMode = false;
constructor(
#Inject(DOCUMENT) document,
private topicsService: TopicService,
private alertify: AlertifyService,
private route: ActivatedRoute
) { }
ngOnInit() {
//this.route.data.subscribe(data => {
// this.topics = data['topics'];
//})
this.getTopics();
}
getTopics() {
this.topicsService.getAllTopics()
.subscribe(data => {
this.topics = data;
}, error => {
this.alertify.error(error);
});
}
addTopic() {
this.topicsService.createTopic(this.newTopic)
.subscribe((topic: Topic) => {
this.topics.push(topic);
this.alertify.success(this.newTopic.name + ' added as a new topic.');
this.newTopic.name = '';
},
(err: any) => {
this.alertify.error(err);
}
)
}
editTopicBtnClick(event) {
console.log(event);
this.editTopicMode = true;
console.log(document.getElementById(event));
}
cancelEditMode() {
this.editTopicMode = !this.editTopicMode;
}
}
Any thoughts on the best (most efficient) way to make this happen?
You've done all the hard work already.
For single item editing, all that's left is: change editTopicMode to something like editTopicId.
Then you can:
Set it to topic.id on edit enabled, and null for example on edit closed
Change your *ngIf to editTopicId === topic.id (or !== as needed)
And that should be all.
If you want to enable multiple editing, just add a property called isInEditMode to each topic.
Your *ngIf check becomes topic.isInEditMode
No isInEditMode property at all is just like false, because undefined is a falsy value
Set topic.isInEditMode to true on editing enabled, false on editing closed
Using *ngIf is fine just make sure that you set the *ngIf variable to point to somethign specific to the particular row of the *ngFor
this example could be cleaner but you could accomplish it as simply as
<button (click)="topic.edit = true" *ngIf="topic.edit === false">edit</button>
<button (click)="topic.edit = false" *ngIf="topic.edit === true">cancel</button>

Get list of all available <button> elements in component html

Say my component is simple, like:
<div>
<button id="one" #one>one</button>
<button id="two" #two>two</button>
<button id="three" #three>three</button>
</div>
Would it be possible to return a list of all of the buttons?
ie.
constructor() {
let buttonList = findByElement('button');
}
Use document.querySelectorAll()
var div=document.getElementById("div");
var a=div.querySelectorAll('button');
console.log(Object.values(a))
<div id="div">
<button id="one" #one>one</button>
<button id="two" #two>two</button>
<button id="three" #three>three</button>
</div>
If you add Renderer and ElementRef to your component, you should then be able to access the element in question:
constructor(private renderer: Renderer, private elem: ElementRef){}
I believe from within your component you can call:
const elements = this.elem.nativeElement.querySelectorAll('button');
Which would give you the elements you need. It's important when using this method to ensure that you're running your code at some point after the component is initialised, not in the constructor as was the case with your original code, as we need to ensure we have access to the DOM for this component. This question has more detail.

Inputting data into a modal from a parent component Angular 5

I am trying to create a cancel function that cancels a specific viewing based on its id. I have a viewings model that I retrieve from firebase as an observable, I then input it into a child div. The problem being that when I click cancel, it always cancels the first viewing on the page, not the specific one that I click.
The strange thing is, that I created a new component on the same level as my modal component, Input the data in the exact same way, and that works perfectly. It only seems to be with the modal that It does not work.
<div class="col-md-3 buttons" [hidden]='!hidden' [#sliderDiv]="out">
<button class="button button-primary button-sm button-green text-center" (click)="updateViewingStatus()">Update Time</button>
<button data-toggle="modal" data-target="#cancelModal" class="button button-primary button-sm button-previous text-center">Cancel Viewing</button>
</div >
<viewings-cancel-modal [viewings]="viewings"></viewings-cancel-modal>
<testing-cancel [viewings]="viewings"></testing-cancel>
</div>
this is where I input viewings property into the child components. Both identical.
this is my test logic (using just a button) that works perfectly:
export class TestingCancelComponent implements OnInit {
#Input() viewings: TenantViewingModel;
private viewingsId;
constructor(private _viewings: ViewingsService) { }
ngOnInit() {
this.viewingsId = this.viewings.viewings_id;
console.log(this.viewings.viewings_id)
}
updateViewingStatus() {
this._viewings.updateViewingStatus(this.viewingsId);
}
}
And this is my modal logic (that doesnt work) :
export class PropertyViewingsCancelModalComponent implements OnInit {
#Input() viewings: TenantViewingModel;
private viewingsId;
constructor(private _viewings: ViewingsService) { }
ngOnInit() {
this.viewingsId = this.viewings.viewings_id;
// console.log(this.viewings.viewings_id)
}
updateViewingStatus() {
this._viewings.updateViewingStatus(this.viewingsId);
}
}
Which again are identical. The html for the working button is as follows:
<button (click)="updateViewingStatus()"></button>
and the non-working modal:
<div class="modal" id="cancelModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="text-center modal_title"> Are you sure you want to </div>
<h1 class="text-center" id="exampleModalLongTitle">Cancel Viewing? {{viewingsId}}</h1>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
</button>
</div>
<div class="modal-body">
<div class="container">
<img src="/assets/img/site/cancel.svg" alt="" style="height: auto; width: 100%; padding: 25px">
<div class="row">
<div class="col-md-6">
<button data-dismiss="modal" class="button button-primary button-xs button-previous text-center" (click)="updateViewingStatus()">confirm</button>
</div>
<div class="col-md-6 text-right">
<button data-dismiss="modal" (click)="logViewings()" class="button button-primary button-xs button-blue text-center">back</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
So just as a re-cap:
The button test works perfectly, removing the specific viewing that I want to remove or cancel. But with the modal, it only removes the first viewing on the page, even though the logic is identical, and I pass the data through to each child component in the exact same way.
Here some working example let's try this,
Html File,
<!--Pass your id as argument to your method.-->
<button (click)="onCancel(id)" class="button button-primary button-sm button-previous text-center">Cancel Viewing</button>
<!--This button has been hiden element is used to call your method -->
<button type="button" class="hideElement" data-toggle="modal" data-target="#cancelModal" #cancelModal></button>
<viewings-cancel-modal [viewings]="viewings"></viewings-cancel-modal>
Typescript File,
import { ElementRef, ViewChild } from '#angular/core';
#ViewChild('cancelModal') private cancelModal: ElementRef;
public onCancel(id:number) {
//set some global variable has been id value
localstorage.setItem("Id",id); //here set value as local storage.
this.viewings.viewings_id=id;
this.cancelModal.nativeElement.click(); //then here iam doing call that modal
}
Modal typescript File,
export class PropertyViewingsCancelModalComponent implements OnInit {
#Input() viewings: TenantViewingModel;
private viewingsId;
constructor(private _viewings: ViewingsService) { }
ngOnInit() {
//this.viewingsId = this.viewings.viewings_id;
this.viewingsId = localstorage.getItem("Id"); //here iam getting id value in localstorage
console.log("View Id",this.viewingsId) //here you getting your id you can proceed your process in modal button click time.
}
updateViewingStatus() {
this._viewings.updateViewingStatus(this.viewingsId);
}
}
It is a simple logic you have to implement your convenient. I hope it's helpful to solve your problem.
Thanks,
Muthukumar

Categories

Resources