toggleClass() not working in Angular 4 - javascript

I've added jQuery in script and used
import * as $ from 'jquery';
as well. I've also added jQuery in the HTML file.
But my toggleClass() function is not working. When I checked the console, it didn't show any error. It was just empty.
Below is my HTML and ts code:
HTML Code:
<div class="web" (click) = "myFunc()">
<p>
Web Development
$300
</p>
</div>
<div class="design" (click) = "myFunc()">
<p>
Design
$400
</p>
</div>
<div class="integration" (click) = "myFunc()">
<p>
Integration
$20
</p>
</div>
<div class="training" (click) = "myFunc()">
<p>
Training
$500
</p>
</div>
</div>
<div class="total">
Total
$0
</div>
ts Code:
import { Component } from '#angular/core';
import * as $ from 'jquery';
//declare var jquery:any;
//declare var $ :any;
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
myFunc() {
$(this).toggleClass("active");
}
}

You don't need to use jquery, just change your click binding on view to (click)="myFunc($event)", and on myFunc change to:
myFunc(e) {
const target: HTMLElement = e.target;
target.classList.toggle('active');
}

It may not be working because $(this) is referring to the current class instance.
you need to refer the source element in the jquery to work properly. Pass '$event' object to your function from the template and get the target object inside the function:
<div class="web" (click) = "myFunc($event)">
myFunc(event: any) {
$(event.target).toggleClass("active");
}
See this stackblitz: https://stackblitz.com/edit/angular-dsely1

Related

Angular - bind InnerHTML with translate in TS file

I have a problem with binding into property to get html and translate it.
I have an innerHTML that I am trying to map to translate.
The problem is it is not translating and displaying the key as it is. P
Below is my code :-
let popupContainer = document.createElement('div');
popupContainer.innerHTML = require('html-loader!../html/myPopup.html').default;
popupContainer.addEventListener('click', clickEventHandler);
document.body.appendChild(popupContainer);
It does not translate and displays as below :- {{'fileverse-name-label' | translate}}
HTML :-
<div class="book__filters">
<hr />
<form class="my____form flex-row" id="filterForm">
<div class="me-3 col-md-3 checker__filters--field">
<label for="fileName" class="band__form--label">{{'fileverse-name-label' | translate}}</label>
<input
type="text"
class="drese__form--input colrs__filters--input"
placeholder="Search for a file verse"
name="fileverse"
id="fileverse"
/>
</div>
<div class="me-3 col-md-3 runner__filters--field">
<label for="chapterLabel" class="chapter__form--label">{{'chapter-label' | translate}}</label>
<select
class="chapter__form--input geeze__filters--input"
name="chapterLabel"
id="chapterLabel"
></select>
</div>
</form>
<hr />
</div>
So, adding html inside a div like you're doing will "just" add your html file into it, without any further logic.
But the pipe needs to be compiled, so you would need to do the following to make this work.
I'll just write step here, please let me know if I should provide more information
With *ngIf
Create a component out of the html you gave (module, component.ts, component.html)
Importing this component whoever you needs it.
Show/Hide it with an *ngIf.
With ViewContainerRef
Create a component out of the html you gave (module, component.ts, component.html)
// component.ts
#Component({
selector: 'foo-bar',
templateUrl: '../foo-bar.component.html', // File where you've added the html
})
export class fooBarComponent {
// ...
}
// module.ts
#NgModule({
declarations: [fooBarComponent],
imports: [
CommonModule,
TranslateModule.forChild()
],
exports: [fooBarComponent],
})
export class fooBarModule {}
Add id to where you need it
<ng-template #loaderContainer></ng-template>
get this element through the where-you-use-it.component.ts file.
#ViewChild('loaderContainer', { read: ViewContainerRef, static: true })
loaderContainer: ViewContainerRef
Add the wished element to it with the ViewContainerRef createComponent() method
this.loaderContainer.createComponent(fooBarComponent)
// This will be at the same place where you initially intended to add the div
Do not forget to add the fooBarModule inside the where-you-use-it.module.ts
Additional
If you're trying to create an "orverlay"
Then I would go with the *ngIf solution and add it into your app.component.html file, just after the <router-outlet> tag

Angular 8: detect if a ng-content has content in it (or exists)

I have a component whose template allows for 2 content areas: Text and "read more" text. If the consumer of the component adds the area for the "read more" text, I want to show the "read more" link the end-user would click to show the text. If they don't include/need any "read more" text I don't want to show the link.
How do I detect the presence of the template area, and act accordingly with an ngIf?
For example, the html might be:
<app-promohero-message-unit title="Title for messaging module">
<div description>
Include a short, informative description here.
</div>
<div readmoretext>
If you need to add more detail, include another sentence or two it in this section.
</div>
</app-promohero-message-unit>
Obviously, they might not need readmoretext, so if they've omitted it I should not show the readmore link.
The component code is, so far:
import { Component, Input } from '#angular/core';
#Component({
selector: 'app-promohero-message-unit',
template: `
<div>
<h3 class="text-white">{{ title }}</h3>
<p class="text-white">
<ng-content select="[description]"></ng-content>
</p>
<p class="text-white" *ngIf="readMore">
<ng-content select="[readmoretext]"></ng-content>
</p>
</div>
<p>
<a class="text-white" (click)="showReadMore()" *ngIf="something"><u>Read more</u></a>
</p>
`
})
export class PromoheroMessageUnitComponent {
#Input()
title: string;
readMore = false;
showReadMore() {
this.readMore = true;
}
}
In Angular 8 you dont have to use the ngAfterViewInit life cycle hook. You can use the ngOnInit as long as you set the "static" value of the viewchild to true.
import { Component, OnInit, ViewChild, TemplateRef, ElementRef } from '#angular/core';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit {
#ViewChild('content', { read: ElementRef, static: true }) content: ElementRef;
constructor() { }
ngOnInit() {
console.log(!!this.content.nativeElement.innerHTML); // return true if there is a content
}
}
Note that you must wrap the ng-content directive with html tag (such as div, span etc) and to set the templateRef on this outer tag.
<div #content>
<ng-content></ng-content>
</div>
I putted it on stackblitz: https://stackblitz.com/edit/angular-8-communicating-between-components-mzneaa?file=app/app.component.html
You can get a reference to the ng-content (Template Variable) and then access that variable in your component to check the length on the content of that ng-content using ViewChild
Then you can use the ngAfterViewInit life cycle hook to check for ng-content length
Your code will be like this:
import { Component, Input, ViewChild, ElementRef } from '#angular/core';
#Component({
selector: 'app-promohero-message-unit',
template: `
<div>
<h3 class="text-white">{{ title }}</h3>
<p class="text-white">
<ng-content select="[description]"></ng-content>
</p>
<p class="text-white" *ngIf="readMore">
<ng-content #readMoreContent select="[readmoretext]"></ng-content>
</p>
</div>
<p>
<a class="text-white" (click)="showReadMore()" *ngIf="something"><u>Read more</u></a>
</p>
`
})
export class PromoheroMessageUnitComponent {
#Input()
title: string;
#ViewChild('readMoreContent') readMoreContent: ElementRef;
readMore = false;
ngAfterViewInit() {
if (this.readMoreContent.nativeElement.childNodes.length.value == 0){
this.readMore = false
}
}
showReadMore() {
this.readMore = true;
}
}
You can use the ContentChild decorator for this, but will need to use an ng-template with a defined id as your content:
<app-promohero-message-unit title="Title for messaging module">
<div description>
Include a short, informative description here.
</div>
<ng-template #readmoretext>
If you need to add more detail, include another sentence or two it in this section.
</ng-template>
</app-promohero-message-unit>
Then in your component, you can use the ContentChild annotation like this:
export class PromoheroMessageUnitComponent {
#ContentChild('readmoretext')
readMoreContent: TemplateRef<any>;
// ...snip
}
Then finally in the HTML for your component:
<!-- snip -->
<p class="text-white" *ngIf="readMoreContent">
<ng-container *ngTemplateOutlet="readMoreContent"></ng-container>
</p>

How to make ngModel available between components

I am seriously getting mad. I have tried everything. FormsModules,ReactiveForms,FORMDIRECTIVES,Input,Output i've been searching everywhere on how to make ngModel available between components. I am trying to show in an h1 tag the value which is being typed/deleted in the input tag using string interpolation, however it isn't working, these are the files:
app.component.html:
<div class="container text-center" id="headerCont">
<span style="color: #6E2435" class="header">note</span><span style="color: #6BBFDE" class="header">it</span>
</div>
<div class="container">
<app-input></app-input>
<app-notes></app-notes>
</div>
app.component.ts
import { Component, OnInit, Input, Output } from '#angular/core';
import { NgModule } from '#angular/core';
import { FormsModule, ReactiveFormsModule } from '#angular/forms';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
}
notes.component.html
<div class="col-xs-2">
<h1>{{ TitleInput }}</h1>
<p>{{ noteInput }}</p>
</div>
input.component.html
<div class="container" id="noteCreate">
<form id="titleInputForm">
<input type="text" [(ngModel)]="TitleInput" name="TitleInput">
</form>
<form>
<textarea name="name" rows="8" cols="80">
</textarea>
</form>
</div>
If you can figure it out I would be so grateful.
You actually searched it but did not use it, the solution to your problem is #Input and #Output. I believe you did not use this effectively.
Since the other components is governed by a component called AppComponent you just simply need to put those data in it:
import { Component } from '#angular/core';
// The other imports are not necessary
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent
{
// declare those stuffs
title: string;
note: string;
constructor()
{
this.title = "";
this.note = "";
}
}
then in your AppComponent template
<div class="container">
<app-input [title]="title"
[note]="note"
// you should return $event always or else it will return undefined
(onTitleChange)="title = $event"
(onModelChange)="note = $event">
</app-input>
<app-notes [title]="title"
[note]="note">
</app-notes>
</div>
where [ ] are #Input and ( ) are #Output from the component
so your InputComponent will have:
// add these to your existing InputComponent
import { Input, Output, EventEmitter } from "#angular/core";
export class InputComponent
{
#Input("title") title: string;
#Input("note") note: string;
#Output() onTitleChange = new EventEmitter();
#Output() onNoteChange = new EventEmitter();
}
where #Input is the data your recieve and #Ouput is the data you send.
and your InputComponent template would be:
<div class="container" id="noteCreate">
<form id="titleInputForm">
<input type="text"
name="title"
[(ngModel)]="title"
(ngModelChange)="onTitleChange.emit(title)">
<textarea name="note"
rows="8"
cols="80"
[(ngModel)]="note"
(ngModelChange)="onNoteChange.emit(note)">
</textarea>
</form>
</div>
where setting [(ngModel)] with your #Input and (ngModelChange) to trigger #Output when modal has changed.
In this example you can actually set default values from AppComponent to InputComponent.
If you understand #Input correctly in these example I do not need to put what would be inside NotesComponent
hope that helps.

Angular2 add HTML to dynamic elements

I have this code:
import { Component, ElementRef, Renderer2 } from '#angular/core';
#Component({
selector: 'my-app',
template: '<button (click)="runR()">Run</button>
<div class="testme">
<div class="somediv">
<div class="dynamically_created_div unique_identifier"></div>
<div class="dynamically_created_div unique_identifier"></div>
<div class="dynamically_created_div unique_identifier"></div>
</div>
</div>',
})
export class AppComponent{
hostEl: any;
constructor(
private el:ElementRef,
private renderer:Renderer2,
) {
this.hostEl = el.nativeElement;
}
runR(){
let change_this;
change_this= this.renderer.createElement('span');
this.renderer.addClass(change_this, 'change_this');
this.renderer.appendChild(this.hostEl, change_this);
}
}
Is there any way in Angular2 to add HTML to the .dynamically_created_div?
Because the above only adds to the end of the HTML of the component.
I also tried with:
import { Component, ElementRef, ViewChild, Renderer, AfterViewInit } from '#angular/core';
#Component({
selector: 'my-app',
template: `<button (click)="runR()">Run</button>
<div class="testme">
<div class="somediv">
<div class="dynamically_created_div">
</div>
</div>
</div>
`,
})
export class AppComponent {
constructor(private renderer:Renderer) {}
runR() {
#ViewChild('dynamically_created_div') d1:ElementRef;
this.renderer.invokeElementMethod(this.d1.nativeElement, 'insertAdjacentHTML', ['beforeend', '<div class="new_div">new_div</div>'] );
}
}
But it's not working because the #ViewChild directive must be outside the function and I can't have control over it anymore
I also tried like this:
<div class="dynamically_created_div" [innerHtml]="newHTML"></div>
this.newHTML = '<div class="new_div">new_div</div>';
Thing I cannot do because my content is dynamic and uses unique IDs and I cannot use [innerHtml] dynamically ( it only works for what I put in themplate for the first time, then anything else that changes can't use innerHtml anymore.
I checked Angular2: Insert a dynamic component as child of a container in the DOM but there is the same problem, the placeholders aren't dynamic
UPDATE:
My code is a little bit more complex:
TS:
import { AfterContentInit, Component, OnInit, OnDestroy, ViewEncapsulation } from '#angular/core';
import { NgForm, FormsModule, ReactiveFormsModule, FormGroup, FormControl, FormBuilder, Validators } from '#angular/forms';
import { SFService } from '../services/sf.service';
import { Injectable, Pipe, PipeTransform } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
providers: [ SFService ],
})
export class AppComponent implements OnInit {
constructor(
private sfservice: SFService,
) {}
ngOnInit(){
this.sfservice.getMembers().subscribe(members => {
this.members = members.members;
});
}
members: Member[];
member_selector: Member[];
member_each: Member;
member_selector_each: Member[];
cases: Case;
runR(){
this.members.forEach(member_each => {
this.member_selector.forEach(member_selector_each => {
if(member_each.Id === member_selector_each.Id){
console.log(member_selector_each.Id);
this.sfservice.getCaseHistory(member_selector_each.Id, "2017-04-25T00:00:00", "2017-04-28T23:59:59").subscribe(cases => {
this.member_each['cases'] = cases;
console.log(this.member_each);
});
}
})
})
}
}
HTML:
<form #myForm="ngForm" novalidate>
<select name="member_selector_name" [(ngModel)]="member_selector" multiple ng-model="selectedValues" style="height:200px;">
<option *ngFor="let member of members" [ngValue]="member">{{member.Name}}</option>
</select>
<button (click)="runR()">Run</button>
</form>
<div id="results">
<div *ngFor="let mem of members" class="member-card-{{mem.Id}}">
<div class="card-container">
<div *ngFor="let case of mem.Cases" class="case-card" id="{{case.Id}}">{{case.Number}}
</div>
</div>
</div>
</div>
I was trying to use only ngFor but now I get
Cannot set property 'cases' of undefined
What's the problem with this approach?
export class AppComponent{
#ViewChild('d1') d1:ElementRef;
#ViewChild('d2') d2:ElementRef;
#ViewChild('d3') d3:ElementRef;
constructor(private renderer:Renderer2) { }
runR(){
let change_this;
change_this= this.renderer.createElement('span');
this.renderer.addClass(change_this, 'change_this');
this.renderer.appendChild(this.d1, change_this);
}
}
Template:
<div class="dynamically_created_div unique_identifier" #d1></div>
<div class="dynamically_created_div unique_identifier" #d2></div>
<div class="dynamically_created_div unique_identifier" #d3></div>
you can use ngfor and create you elements inside it and using index you can create different ids and names.
I do something like this i dont know if you want to do the same but here's my code to create some input's dynamically and add or access their values
<div *ngFor="let comp of templateVals | async;let i=index">
<md-input-container class="example-90" *ngIf="comp.type=='code'">
<textarea rows="4" mdInput name="desc{{i}}" [(ngModel)]="comp.data" placeholder="Description"></textarea>
</md-input-container>
<md-input-container class="example-90" *ngIf="comp.type=='text'">
<textarea rows="4" mdInput name="text{{i}}" [(ngModel)]="comp.data" placeholder="Text"></textarea>
</md-input-container>
<md-input-container class="example-90" *ngIf="comp.type=='title'">
<input mdInput name="title{{i}}" [(ngModel)]="comp.data" placeholder="Title">
</md-input-container>
<span class="example-90" *ngIf="comp.type=='upload'">
<input-file *ngIf="!comp.data" [acceptId]="comp.id" (onFileSelect)="addedFileInfo($event)"></input-file>
<span *ngIf="comp.data">{{comp.data}}</span>
</span>
<span class="example-10">
<button md-mini-fab (click)="removeThis(comp)"><md-icon>remove circle</md-icon></button>
</span>
</div>

Manipulate position of an HTML element in angular 2

I have an angular component template like this.
<my-app>
<nav id="nav-bar">
<ul>
<li id="item-1"></li>
<li>
<div id="item-2"></div>
</li>
<li id="item-3"></li>
<div id="middle-div">
</div>
<div id="side-div">
</div>
</nav>
</my-app>
Now here I want to take the div with id, 'item-2' and append it to nav-bar so that it appears below the div with id "side-div".
I can easily do this with javascript's appendChild function like this.
var item = document.getElementById("item-2");
var nav = document.getElementById("nav-bar");
//change position of searchform in DOM
nav.appendChild(item);
However, I want to be able to do this in component's typescript file. Is ElementRef an ideal solution to this? or is there any other way to achieve this?
the component looks like this:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: '/app/Templates/nav.html',
providers: [MyService]
})
export class MyComponent implements OnInit {
constructor(){
}
ngOnInit(){
}
}
You can use appendChild using ElementRef. Here is an example
#Component({
selector: 'app',
template: `
<div>
<button type="button" (click)="appendToChild()">Append Item to Child</button>
<div #item>Item</div>
<div #appendToChildEl></div>
</div>
`
})
export class App {
#ViewChild('item') item: ElementRef;
#ViewChild('appendToChildEl') appendToChildEl: ElementRef;
appendToChild() {
this.appendToChildEl.nativeElement.appendChild(this.item.nativeElement);
}
}
A plunker example:
https://embed.plnkr.co/7DJ1nWN6sl563hcR71FV/
You could also do something like this if you don't want to use ElementRef
User a service or component variable to control the position:
<my-app>
<nav id="nav-bar">
<ul>
<li id="item-1"></li>
<li>
<div id="item-2" *ngIf="defaultPos"></div>
</li>
<li id="item-3"></li>
<div id="middle-div">
</div>
<div id="side-div">
</div>
<div id="item-2" *ngIf="newPos"></div>
</nav>
</my-app>

Categories

Resources