Disable Angular 2 sanitization - html

I've seen a few answers to this question and tried all of them, but it still isn't working. I'm kinda trying to write a CMS in angular 2; so a backend where someone puts HTML and a frontend that renders said HTML. I am able to display some HTML with DomSanitizer.bypassSecurityTrustHtml() but not others. For example, I am able to add an <img class="center" src=""> but a <button md-button>test</button> is rendered as a plain HTML button. I also have a LessonComponent w/a lesson selector, but it isn't rendered at all on the page, I do see the <lesson></lesson> HTML in the inspector.
How (if possible) do I display components on the page from a DB? Code below:
lesson.ts
import { Component, Inject, Input, SecurityContext } from '#angular/core';
import { MdDialog, MD_DIALOG_DATA } from '#angular/material';
import { Lesson, LessonService } from '../lesson.service';
import { DomSanitizer, SafeHtml} from '#angular/platform-browser';
#Component({
selector: 'lesson',
templateUrl: './lesson.html',
styleUrls: ['./lesson.scss']
})
export class LessonComponent {
#Input() lessonID: number = 0;
#Input() questionID: string = '';
lesson: Lesson;
constructor(public service: LessonService, public dialog: MdDialog, private sanitizer: DomSanitizer) { }
getLesson(): object {
if(!this.service.getLesson(this.lessonID)) { return {}; }
let lesson = this.service.getLesson(this.lessonID)['pages'].filter(lesson => {
return lesson['id'] === this.questionID;
})[0];
if(lesson && lesson['buttons']) {
this.service.buttons = lesson['buttons'];
} else {
this.service.buttons = [];
}
lesson['test'] = this.sanitizer.bypassSecurityTrustHtml(lesson['content']);
return lesson;
}
doClick(btnID) {
let button = this.getLesson()['buttons'].filter(btn => {
return btn['id'] === btnID;
})[0];
this.dialog.open(ExtraDialog, { data: button['content'] });
}
}
#Component({
selector: 'extra-dialog',
template: `{{data}}`
})
export class ExtraDialog {
constructor(#Inject(MD_DIALOG_DATA) public data: any) {}
}
lesson.html
<div *ngIf="!getLesson()" >
Error: Lesson {{lessonID}} -> question {{questionID}} is not a known lesson or question. Please click <a routerLink="/lesson/1">here</a> to go to the first lesson.
</div>
<div *ngIf="getLesson() && getLesson()['buttons']">
<span *ngFor="let button of getLesson()['buttons']">
<button md-mini-fab (click)="doClick(button.id)"><md-icon>{{button.icon}}</md-icon></button>
</span>
</div>
<div *ngIf="getLesson()">
<h2>{{getLesson()['title']}}</h2>
<p [innerHTML]="getLesson()['test']"></p>
</div>
lesson.service.ts
import { Injectable } from '#angular/core';
import { AngularFireDatabase, FirebaseObjectObservable } from 'angularfire2/database';
export class Lesson {
constructor(public id: number, public pages: Object[]) { }
}
#Injectable()
export class LessonService {
private lessons: Lesson[] = [];
public buttons: object[];
constructor(public db: AngularFireDatabase) {
this.db.object('/lessons', { preserveSnapshot: true }).subscribe(lessons => {
this.lessons = [];
lessons.forEach(lesson => {
let pages = [];
if(lesson.val()['pages']) {
Object.keys(lesson.val()['pages']).forEach(key => {
let page = {
id: key,
title: lesson.val()['pages'][key]['title'],
content: lesson.val()['pages'][key]['content']
};
let buttons = lesson.val()['pages'][key]['buttons'];
if(buttons) {
page['buttons'] = [];
Object.keys(buttons).forEach(key => {
page['buttons'].push({
content: buttons[key]['content'],
icon: buttons[key]['icon'],
id: key
});
});
}
pages.push(page);
});
}
this.lessons.push(new Lesson(
lesson.key,
pages
));
});
});
}
getLessons() { return this.lessons; }
getLesson(id: number): Lesson {
return this.lessons.filter(lesson => {
return lesson['id'] == id;
})[0];
}
}

Related

#viewChild and #ViewChildern gives undefined

I'm working on Angular 9 and want to access an input field after clicking on a button. right now it gives me undefined. I have tried #ViewChild and #viewChildern because I'm using ngIf.
Template.html file
<div class="search-input" #searchDiv *ngIf="serachActive">
<input
#searched
autofocus
type="text"
class="serach-term"
placeholder="Search"
[(ngModel)]="searchTerms"
(ngModelChange)="applySearch()"
/>
<button (click)="toggleSearch(!serachActive)">
<span class="material-icons"> search </span>
</button>
<ul class="search-list">
<li *ngFor="let result of results">
<a [routerLink]="['/', 'video', 'details', result._id]">{{
result.title ? result.title : ''
}}</a>
</li>
</ul>
</div>
Template.ts file
import { Component, OnInit,AfterViewInit,ElementRef,ViewChild,ViewChildren } from '#angular/core';
import { UserService } from '../../../user.service';
import { VideoService } from '../../../services/video.service';
import { Subject } from 'rxjs';
import { distinctUntilChanged, debounceTime } from 'rxjs/operators';
import { Router } from '#angular/router';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css'],
})
export class HeaderComponent implements OnInit,AfterViewInit{
serachActive: boolean = false;
#ViewChildren('searched') searchElement: ElementRef;
#ViewChildren("searched") input: ElementRef;
user;
subject = new Subject<string>();
results = [];
searchTerms;
loggedIn: Boolean = false;
constructor(
private userService: UserService,
private videoService: VideoService,
private router: Router
) {
this.user = this.userService.getUser();
this.loggedIn = this.userService.isAuthenticated();
}
ngOnInit() {
console.log('on init', this.input); //undefined
this.subject
.pipe(debounceTime(400), distinctUntilChanged())
.subscribe((value) => {
this.router.navigate(['search'], { queryParams: { term: value } });
});
}
ngAfterViewInit() {
console.log('on after', this.input); //undefined
}
toggleSearch(toggledata) {
this.serachActive = toggledata;
this.results = [];
this.searchTerms = '';
console.log(this.input) //undefined
console.log(this.searchElement.nativeElement) //undefined
}
applySearch() {
const searchText = this.searchTerms;
this.subject.next(searchText);
this.searchElement.nativeElement.focus(); //undefined
}
menuButtonClick(button){
if(button === "history"){
this.router.navigate(['history'])
}
}
}
Use ViewChild since you're only searching for 1 element ID.
If adding { static: true } or { static: false } in your ViewChild options doesn't work as what is stipulated on Angular Static Query Migration Documentation
Use ChangeDetectorRef instead:
#Component({...})
export class AppComponent {
#ViewChild('searchInput') input: ElementRef;
isShow: boolean = false;
constructor(private cdr: ChangeDetectorRef) {}
toggle(): void {
this.isShow = !this.isShow;
this.cdr.detectChanges(); // Detects changes which this.isShow is responsible on showing / hiding
// the element you're referencing to in ViewChild
if (this.isShow) // If element is shown, console the referenced element
console.log(this.input);
}
}
Have created a Stackblitz Demo for your reference

Angular, Displays a list multiple times on the same html page, that is updated each round of the loop through c#

I want to display a list as the options in select when in each round in the loop the list will be updated according to the current div
It works in terms of concept, but the html is updated only once according to the last entry in the list and does not display a different list for each loop rotation
my html
<div *ngFor="let item of listConstraint" [value]="item.id">
<p>{{item.name_constraint}}</p>
<select>
<option *ngFor="let item1 of listConstraintDetails" [value]="item1.id">
{{item1.name_constraint}}</option>
</select>
</div>
my ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { HttpClient } from '#angular/common/http';
import { AjaxRequestService } from 'src/app/services/Request/ajax-request.service';
import { ConstraintKind } from 'src/app/class/constraintKind';
import { ConstraintDetails } from 'src/app/class/constraint-details';
#Component({
selector: 'app-constraints',
templateUrl: './constraints.component.html',
styleUrls: ['./constraints.component.css']
})
export class ConstraintsComponent implements OnInit {
constraintForm: FormGroup;
listConstraint: ConstraintKind[] = [];
listConstraintDetails: ConstraintDetails[] = [];
constructor(private http: AjaxRequestService, private httpClient: HttpClient, private route: Router) {
}
ngOnInit(): void {
this.GetConstraintsKind();
}
GetConstraintsKind() {
return this.http.GetConstraintsKind().subscribe(data => {
this.listConstraint = data;
data.forEach(element => {
this.GetConstraintsDetails(element.id);
})
console.log(data);
})
}
GetConstraintsDetails(constraintId) {
return this.http.GetConstraintsDetails(constraintId).subscribe(data => {
this.listConstraintDetails = data;
console.log(data);
})
}
}
my functions ajax service
GetConstraintsKind() {
return this.http.get<any>('http://localhost:11818/Api/Constraint/getConstraintKind', { headers: this.header });
}
GetConstraintsDetails(constraintId: number) {
return this.http.get<ConstraintDetails[]>('http://localhost:11818/Api/Constraint/GetConstraintsDetails/' + constraintId);
}
the server works well, and send the correct data, but the html display the same list the whole time
Thanks so much for any help
You are performing inner loop operation all at ngOnInit inside a single array, thats overwriting previously fetched data in the listConstraintDetails array.
What you want can be achieved, if you modify your code a little, like this
ngOnInit(): void {
this.GetConstraintsKind();
}
GetConstraintsKind() {
return this.http.GetConstraintsKind().subscribe(data => {
this.listConstraint = data;
})
}
GetConstraintsDetails(constraintId):ConstraintDetails[]{
let itemsarr: ConstraintDetails[] = [];
if(constraintId)
{
this.http.GetConstraintsDetails(constraintId).subscribe(data => {
itemsarr = data;
})
}
return itemsarr;
}
And your html would get modified like
<div *ngFor="let item of listConstraint" [value]="item.id">
<p>{{item.name_constraint}}</p>
<select>
<option *ngFor="let item1 of GetConstraintsDetails(item.id)" [value]="item1.id">
{{item1.name_constraint}}</option>
</select>
</div>
Thanks.
my html
<div *ngFor="let item of listConstraint" [attr.data-value]="item.id">
<p>{{item.name_constraint}}</p>
<select *ngIf="constraintDetails[item.id]">
<option *ngFor="let item1 of constraintDetails[item.id]" [value]="item1.id">
{{item1.name_constraint}}</option>
</select>
</div>
my ts
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormControl, Validators } from '#angular/forms';
import { Router } from '#angular/router';
import { HttpClient } from '#angular/common/http';
import { AjaxRequestService } from 'src/app/services/Request/ajax-request.service';
import { ConstraintKind } from 'src/app/class/constraintKind';
import { ConstraintDetails } from 'src/app/class/constraint-details';
import { Observable } from 'rxjs';
#Component({
selector: 'app-constraints',
templateUrl: './constraints.component.html',
styleUrls: ['./constraints.component.css']
})
export class ConstraintsComponent implements OnInit {
constraintForm: FormGroup;
listConstraint: ConstraintKind[] = [];
listConstraintDetails: ConstraintDetails[] = [];
constraintDetails = {};
constructor(private http: AjaxRequestService, private httpClient: HttpClient, private route: Router) {
}
ngOnInit(): void {
this.GetConstraintsKind();
}
GetConstraintsKind() {
return this.http.GetConstraintsKind().subscribe(data => {
this.listConstraint = data;
for (let i = 0; i < this.listConstraint.length; i++) {
const element = this.listConstraint[i].id;
this.http.GetConstraintsDetails(element)
.subscribe(cd => {
this.constraintDetails[element] = cd;
console.log(this.constraintDetails)
});
}
})
}
GetConstraintsDetails(constraintId): ConstraintDetails[] {
console.log(constraintId);
let itemsarr: ConstraintDetails[] = [];
if (!itemsarr) {
this.http.GetConstraintsDetails(constraintId).subscribe(data => {
itemsarr = data;
})
}
return itemsarr;
}
}

Trying to implement a checkbox complete into To Do List

Trying to implement a checkbox complete to my To Do List but unsure why it is not working.
Whenever my code compiles down to Javascript I get this error:
" ERROR in src/app/ToDo/todo.component.ts(89,32): error TS2339: Property 'item' does not exist on type 'ToDoComponent'. "
Also i'm unsure why my IDE is saying the task is considered an any statement.
UPDATE:
My console in my browser is displaying this error:
Unexpected closing tag "li". It may happen when the tag has already been closed by another tag.
TypeScript:
import { Component, OnInit, EventEmitter, Output } from '#angular/core';
import { ToDo, IToDo } from './todo.model';
import { HttpClient } from '#angular/common/http';
import { LocalStorageService } from '../localStorageService';
import { ActivatedRoute, Router } from '#angular/router';
import { IUser } from '../login/login.component';
import { ToastService } from '../toast/toast.service';
#Component({
// tslint:disable-next-line: component-selector
selector: 'todolist',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.css']
})
export class ToDoComponent implements OnInit {
[x: string]: any;
todos: Array<IToDo> = [];
inputtask = "";
toDoParams = '';
localStorageService: LocalStorageService<IToDo>;
currentUser: IUser;
#Output() update: EventEmitter<any> = new EventEmitter();
constructor(
private http: HttpClient,
private activatedRoute: ActivatedRoute,
private router: Router) {
this.localStorageService = new LocalStorageService('todos');
}
private toastService: ToastService;
async ngOnInit() {
const currentUser = this.localStorageService.getItemsFromLocalStorage('user');
console.log('from todos component', currentUser);
if (currentUser == null) {
await this.router.navigate(['login']);
} else {
// if user is logged in go and find any items from local storage and bind
// to the view
const toDoItems = this.localStorageService.getItemsFromLocalStorage('todos');
if (toDoItems && Array.isArray(toDoItems)) {
this.todos = toDoItems;
}
}
}
addToDo(todo: string, cm?: boolean) {
const td = {
id: null,
task: todo,
completed: cm,
}
if (todo === '') {
alert('You must enter in a task TO DO!')
} else {
this.todos.push(td);
}
this.saveItemsToLocalStorage(this.todos);
}
delete(index: number) {
this.todos.splice(index, 1);
console.log("index", index);
this.saveItemsToLocalStorage(this.todos);
}
clear() {
this.todos = [];
console.log('index', this.todos)
this.saveItemsToLocalStorage(this.todos);
}
getItemsFromLocalStorage(key: string) {
const savedToDo = JSON.parse(localStorage.getItem(key));
console.log('from getItemsFromLocalStorage savedItems', savedToDo);
return this.localStorageService.getItemsFromLocalStorage(key);
return savedToDo;
}
completeItem() {
this.update.emit({
task: this.todos,
changes: {completed: this.task.completed}
});
}
saveItemsToLocalStorage(todos: Array<IToDo>) {
todos = this.sortByID(todos);
return this.localStorageService.saveItemsToLocalStorage(todos);
const savedToDo = localStorage.setItem('todos', JSON.stringify(todos));
console.log('from saveItemsToLocalStorage savedToDos: ', savedToDo);
return savedToDo;
}
sortByID(todos: Array<IToDo>) {
todos.sort((prevToDo: IToDo, presToDo: IToDo) => {
return prevToDo.id > presToDo.id ? 1 : -1;
});
console.log('the sorted ToDos', this.todos);
return this.todos;
}
logout() {
// clear localStorage
this.localStorageService.clearItemFromLocalStorage('user');
// navigate to login page
this.router.navigate(['']);
}
}
HTML Code:
<ul class="list-group">
<li *ngFor="let todo of todos; let i = index"
class="list-group-item shadow p-3 mb-5 bg-white rounded border border-dark rounded" id="myTask">
<div class="todo-item">
{{todo.task}} <button type="button" class="btn btn-danger" (click)="delete()">X</button>
<input type="checkbox" class="todo-checkbox" (click)="completeItem()">
<span class="todo-title" [ngClass]="{'todo-complete': item.completed}">
</li>
</ul>
<span class="todo-title" [ngClass]="{'todo-complete': item.completed}">
Here you are using item which doesn't exist in your typescript file. Did you mean to use todo from your *ngFor ?

How can I get the real image value from each item in my list and subscribe it to another list?

I have a list of services that have multiple property like serviceId, serviceName and photoProfile called from a database using a spring REST API.
The 'photoProfile' property only has the id of the profile picture which if you use the 'localhost:8082/downloadFile/'+photoProfile would get you the image which is in turn is stored in a folder in the spring project.
After looking for a while online, I've found how I can actually get the real image to display on my website but now I'm stuck since I need to do this for the whole list.
Here's my angular code:
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { LoginComponent } from '../login/login.component';
import { UserService } from '../user.service';
import { Observable, forkJoin } from 'rxjs';
import { HttpHeaders, HttpClient } from '#angular/common/http';
import { combineLatest } from 'rxjs/operators';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
loggedIn: boolean;
services: any[] = [];
imgSrc: any;
newList: any[] = [];
constructor(private router: Router, private service: UserService, private http: HttpClient) {
}
ngOnInit() {
this.service.getServices().subscribe(res => {
this.services = res;
console.log('services: ', this.services);
});
for (let i = 0; i < this.services.length; i++) {
const element = this.services[i];
this.getImage('http://localhost:4200/downloadFile/' + element.photoProfile).subscribe(data => {
this.createImageFromBlob(data);
});
this.newList.push(this.imgSrc);
console.log(this.newList);
//I want to add the element from the services list and the image value after being converted to the new list
}
}
getImage(imageUrl: string): Observable<Blob> {
return this.http.get(imageUrl, {responseType: 'blob'});
}
createImageFromBlob(image: Blob) {
const reader = new FileReader();
reader.addEventListener('load', () => {
this.imgSrc = reader.result;
}, false);
if (image) {
reader.readAsDataURL(image);
}
}
}
Thank you for your help.
You need to add the new list inside the ngOnInit after you are subscribing to the services list. Because currently. You don't have the services when the for loop runs. You need to run the for loop after you have the result from services. Like this:
ngOnInit() {
this.service.getServices().subscribe(res => {
this.services = res;
console.log('services: ', this.services);
for (let i = 0; i < this.services.length; i++) {
const element = this.services[i];
this.getImage('http://localhost:4200/downloadFile/' + element.photoProfile).subscribe(data => {
this.createImageFromBlob(data);
element.imgSrc = this.imgSrc;
this.newList.push(element);
});
console.log(this.newList);
}
}
});
I had similar situation, and method of Muhammad Kamran work particularry for me, because images loaded into array absolutely randomly. As i understand, the speed of FOR cycle is faster than picture download speed. The solution is - pushing into array in createImageFromBlob (in case of i'mgnome). In my case it was like this:
export interface File {
...
original_name: name of file;
linkToPicture: string;
image: any;
}
...
files: File[] = [];//array for incoming data with link to pictures
this.getTable(...)
...
getTable(sendingQueryForm: QueryForm){
this.queryFormService.getTable(sendingQueryForm)
.subscribe(
(data) => {
this.files=data as File[];
for (let i = 0; i < this.files.length; i++) {
this.getImage('/api/auth/getFileImage/' + this.files[i].linkToPicture).subscribe(blob => {
this.createImageFromBlob(blob,i);
});
}
},
error => console.log(error)
);
}
getImage(imageUrl: string): Observable<Blob> {
return this.http.get(imageUrl, {responseType: 'blob'});
}
createImageFromBlob(image: Blob, index:number) {
const reader = new FileReader();
reader.addEventListener('load', () => {
this.files[index].image = reader.result;
}, false);
if (image) {
reader.readAsDataURL(image);
}
}
and in HTML:
<div *ngFor="let block of files; let i = index" >
<mat-card class="grid-card">
<div>
<img [src]="block.image" width=120>
<p>{{block.original_name}}</p>
</div>
</mat-card>
</div>
I hope it will useful for someone and thanks for topic!

Error: Cannot find module "./../providers/auth-service" [duplicate]

I am trying to create Login/SignUp in ionic angular 3.3.0.
I get the error Cannot find module '../providers/auth-service/auth-service'. in the login.ts file. Please Help!
auth-service.ts
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
/*
Generated class for the AuthServiceProvider provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
export class User {
name: string;
email: string;
constructor(name: string, email: string) {
this.name = name;
this.email = email;
}
}
#Injectable()
export class AuthServiceProvider {
currentUser: User;
public login(credentials) {
if (credentials.email === null || credentials.password === null) {
return Observable.throw("Please insert credentials");
} else {
return Observable.create(observer => {
// At this point make a request to your backend to make a real check!
let access = (credentials.password === "pass" && credentials.email === "email");
this.currentUser = new User('ian', 'ianlikono#gmail.com');
observer.next(access);
observer.complete();
});
}
}
public register(credentials) {
if (credentials.email === null || credentials.password === null) {
return Observable.throw("Please insert credentials");
} else {
// At this point store the credentials to your backend!
return Observable.create(observer => {
observer.next(true);
observer.complete();
});
}
}
public getUserInfo() : User {
return this.currentUser;
}
public logout() {
return Observable.create(observer => {
this.currentUser = null;
observer.next(true);
observer.complete();
});
}
}
login.ts
import { Component } from '#angular/core';
import { NavController, AlertController, LoadingController, Loading, IonicPage } from 'ionic-angular';
import { AuthServiceProvider } from '../providers/auth-service/auth-service';
#IonicPage()
#Component({
selector: 'page-login',
templateUrl: 'login.html',
})
export class LoginPage {
loading: Loading;
registerCredentials = { email: '', password: '' };
constructor(private nav: NavController, private auth: AuthServiceProvider, private alertCtrl: AlertController, private loadingCtrl: LoadingController) { }
public createAccount() {
this.nav.push('RegisterPage');
}
public login() {
this.showLoading()
this.auth.login(this.registerCredentials).subscribe(allowed => {
if (allowed) {
this.nav.setRoot('HomePage');
} else {
this.showError("Access Denied");
}
},
error => {
this.showError(error);
});
}
showLoading() {
this.loading = this.loadingCtrl.create({
content: 'Please wait...',
dismissOnPageChange: true
});
this.loading.present();
}
showError(text) {
this.loading.dismiss();
let alert = this.alertCtrl.create({
title: 'Fail',
subTitle: text,
buttons: ['OK']
});
alert.present(prompt);
}
}
ScreenShot Program structure:
From your project structure, your login.ts is inside login folder, and login folder is inside pages folder.
So in order to reach providers folder, you need to write
'../../providers/auth-service/auth-service'
This should move you out of two folders which should solve the issue.
If you are using VS Code, install the plugin called "Typescript Hero" and "Typescript Toolbox", will help you with your imports.
Actually "Typescript Toolbox" shows a lightbulb when you focus your cursor on an imported element and you can select from the lightbulb the import. Very usefull.