Get ng-template from component angular 2 - angular2-template

how can i get the element in angular 2?
in case i have this in html
<ng-template #content let-c="close" let-d="dismiss">
<div class="modal-header">Header</div>
<div class="modal-body">Body</div>
<div class="modal-footer">footer</div>
</ng-template>
i use that for ngBmodal ng-bootstrap
if i use button for open content its work = button
(click)="open(content,data.id)"
then i would like open content from component
in this case, im redirect from other page and open content
ngOnInit() {
this.activatedRoute.queryParams.subscribe((params: Params) => {
let id = params['id'];
if(id != undefined){
this.open('content',id);
}
});
}
open(content, id) {
this.dataModal = {};
this.getDataModal(id);
this.mr = this.modalService.open(content, { size: 'lg' });
}
modal open but not with the html, i try afterviewinit to get #content it doesnt work
thanks,sorry for my english :v

First import NgbModal and ModalDismissReasons
import { NgbModal, ModalDismissReasons } from '#ng-bootstrap/ng-bootstrap';
and add modalservice to constructor
private modalService: NgbModal
See:
https://ng-bootstrap.github.io/#/components/modal/examples#options
Then in your typescript file:
1 - Import TemplateRef and ViewChild, example:
import { TemplateRef, ViewChild } from '#angular/core';
2 - Create the variable that binds the ngtemplate (add before constructor):
#ViewChild('content')
private content: TemplateRef<any>;
3 - Open modal from typescript:
this.modalService.open(this.content);

Related

How to leave existing class attribute on image element - now it is being moved to a generated enclosing span

Background: Trying to use ckeditor5 as a replacement for my homegrown editor in a non-invasive way - meaning without changing my edited content or its class definitions. Would like to have WYSIWYG in the editor. Using django_ckeditor_5 as a base with my own ckeditor5 build that includes ckedito5-inspector and my extraPlugins and custom CSS. This works nicely.
Problem: When I load the following HTML into ClassicEditor (edited textarea.value):
<p>Text with inline image: <img class="someclass" src="/media/uploads/some.jpeg"></p>
in the editor view area, browser-inspection of the DOM shows:
...
<p>Text with an inline image:
<span class="image-inline ck-widget someclass ck-widget_with-resizer" contenteditable="false">
<img src="/media/uploads/some.jpeg">
<div class="ck ck-reset_all ck-widget__resizer ck-hidden">
<div ...></div></span></p>
...
Because the "someclass" class has been removed from and moved to the enclosing class attributes, my stylesheets are not able to size this image element as they would appear before editing.
If, within the ckeditor5 view, I edit the element using the browser inspector 'by hand' and add back class="someclass" to the image, ckeditor5 displays my page as I'd expect it with "someclass" and with the editing frame/tools also there. Switching to source-editing and back shows the class="someclass" on the and keeps it there after switching back to document editing mode.
(To get all this, I enabled the GeneralHtmlSupport plugin in the editor config with all allowed per instructions, and that seems to work fine.) I also added the following simple plugin:
export default class Extend extends Plugin {
static get pluginName() {
return 'Extend';
}
#updateSchema() {
const schema = this.editor.model.schema;
schema.extend('imageInline', {
allowAttributes: ['class']
});
}
init() {
const editor = this.editor;
this.#updateSchema();
}
}
to extend the imageInline model hoping that would make the Image plugin keep this class attribute.
This is the part where I need some direction on how to proceed - what should be added/modified in the Image Plugin or in my Extend plugin to keep the class attribute with the element while editing - basically to fulfill the WYSIWYG desire?
The following version does not rely on GeneralHtmlSupport but creates an imageClassAttribute model element and uses that to convert only the image class attribute and place it on the imageInline model view widget element.
import Plugin from '#ckeditor/ckeditor5-core/src/plugin';
export default class Extend extends Plugin {
static get pluginName() {
return 'Extend';
}
#updateSchema() {
const schema = this.editor.model.schema;
schema.register( 'imageClassAttribute', {
isBlock: false,
isInline: false,
isObject: true,
isSelectable: false,
isContent: true,
allowWhere: 'imageInline',
});
schema.extend('imageInline', {
allowAttributes: ['imageClassAttribute' ]
});
}
init() {
const editor = this.editor;
this.#updateSchema();
this.#setupConversion();
}
#setupConversion() {
const editor = this.editor;
const t = editor.t;
const conversion = editor.conversion;
conversion.for( 'upcast' )
.attributeToAttribute({
view: 'class',
model: 'imageClassAttribute'
});
conversion.for( 'dataDowncast' )
.attributeToAttribute({
model: 'imageClassAttribute',
view: 'class'
});
conversion.for ( 'editingDowncast' ).add( // Custom conversion helper
dispatcher =>
dispatcher.on( 'attribute:imageClassAttribute:imageInline', (evt, data, { writer, consumable, mapper }) => {
if ( !consumable.consume(data.item, evt.name) ) {
return;
}
const imageContainer = mapper.toViewElement(data.item);
const imageElement = imageContainer.getChild(0);
if ( data.attributeNewValue !== null ) {
writer.setAttribute('class', data.attributeNewValue, imageElement);
} else {
writer.removeAttribute('class', imageElement);
}
})
);
}
}
Well, Mr. Nose Tothegrind found two solutions after digging through ckeditor5 code, here's the first one. This extension Plugin restores all image attributes that are collected by GeneralHtmlSupport. It can be imported and added to a custom ckeditor5 build app.js file by adding config.extraPlugins = [ Extend ]; before the editor.create(...) statement.
import Plugin from '#ckeditor/ckeditor5-core/src/plugin';
import GeneralHtmlSupport from '#ckeditor/ckeditor5-html-support/src/generalhtmlsupport';
export default class Extend extends Plugin {
static get pluginName() {
return 'Extend';
}
static get requires() {
return [ GeneralHtmlSupport ];
}
init() {
const editor = this.editor;
this.#setupConversion();
}
#setupConversion() {
const editor = this.editor;
const t = editor.t;
const conversion = editor.conversion;
conversion.for ( 'editingDowncast' ).add( // Custom conversion helper
dispatcher =>
dispatcher.on( 'attribute:htmlAttributes:imageInline', (evt, data, { writer, mapper }) => {
const imageContainer = mapper.toViewElement(data.item);
const imageElement = imageContainer.getChild(0);
if ( data.attributeNewValue !== null ) {
const newValue = data.attributeNewValue;
if ( newValue.classes ) {
writer.setAttribute('class', newValue.classes.join(' '), imageElement);
}
if ( newValue.attributes ) {
for (const name of Object.keys(newValue.attributes)) {
writer.setAttribute( name, newValue.attributes[name], imageElement);
}
}
} else {
writer.removeAttribute('class', imageElement);
}
})
);
}a
}

ion-slides methods not working in ionic 4

Unable to use methods provided by ion-slides in official documentation. checked other answers in here but all seems to confuse ionic 4 with ionic 3 and providing answers applicable in ionic 3.
I want to get active index of slide. online documentation is not complete about how to implement it.
Note: Use IonSlides and don't use ElementRef and nativeElement
Just follow the code below and it will work fine to get the active index from getActiveIndex()
import { IonSlides } from '#ionic/angular';
#ViewChild('slides', {static: true}) slides: IonSlides;
slideChanged(e: any) {
this.slides.getActiveIndex().then((index: number) => {
console.log(index);
});
}
In ionic 4, the return type of the getActiveIndex() method is Promise<number>, so the code you were using in ionic 3 will not work anymore. You could at a bare minimum switch it out for somehting like:
this.slider.getActiveIndex()
.then(activeIndex => {
console.log('active index = ', activeIndex );
if (activeIndex < this.slides.length) {
this.selectedSegment = this.slides[activeIndex ].id;
}
});
Or whatever you want to use it for. The official doc is actually pretty awesome on this: https://ionicframework.com/docs/api/slides
Build the slider in your html with a slides ID and a function which is emitted when the active slide has changed.
<ion-slides #slides (ionSlideDidChange)="getIndex()">
<ion-slide></ion-slide>
</ion-slides>
In the .ts file you import the slider ID with ViewChild and set the function to get the active index.
import { Component, OnInit, ViewChild } from '#angular/core';
import { Slides } from '#ionic/angular';
export class Page implements OnInit {
#ViewChild('slides') slides: Slides;
constructor() {}
ngOnInit() {
}
async getIndex() {
console.log(await this.slides.getActiveIndex());
}
}
I had the same issue, but I solved it with the following code:
My .ts file:
export class RegistroPage implements OnInit {
#ViewChild('registroWizard') registroWizard: IonSlides;
slideOpts: any;
constructor() {
this.slideOpts = {
effect: 'fade'
};
}
ngOnInit() {
this.registroWizard.lockSwipeToNext(true);
}
}
My HTML file:
<ion-slides #registroWizard pager="true" [options]="slideOpts">
<ion-slide>
<h1>Slide 1</h1>
<ion-button>Hola</ion-button>
</ion-slide>
<ion-slide>
<h1>Slide 2</h1>
<ion-button>Hola</ion-button>
</ion-slide>
<ion-slide>
<h1>Slide 3</h1>
<ion-button>Hola</ion-button>
</ion-slide>
</ion-slides>
I solved the problem like this:
page.ts:
import { IonSlides } from '#ionic/angular';
...
#ViewChild('slides') slides: IonSlides;
nextSlide() {
this.slides.slideNext();
}
page.html:
<ion-slides #slides pager="true" [options]="slideOpts">
<ion-slide>slide 1</ion-slide>
<ion-slide>slide 2</ion-slide>
</ion-slides>
<ion-button (click)="nextSlide()" class="register-buttons">go next</ion-button>
exact the same thing goes for the back action
static: true
#ViewChild('ionSlides', { static: true }) ionSlides: IonSlides;
u need declaration class to app.module.ts
#NgModule({
declarations: [MySliderComponent]
})
I used IonSlides as type but it didn't help.
For me, the above mentioned solutions didnt work (ionic v6.17.1). What worked was:
#ViewChild('slides', {static: true}) slides: ElementRef;
swipeRight() {
this.slides.nativeElement.slideNext();
All methods working this way. Altering ```{static: true} didn't throw any error
If you console.log after declaring slides as IonSlides type, it shows ElementRef type

Vue.js attach text after clicking. how?

I have this Vue Material (Vue.js) tag, with the function
<md-button id="" v-on:click.native="requestSelected(request)">
methods: {
requestSelected: function(request) {
request.accepted = true;
console.log(request);
var card = document.getElementById('text');
var accept = document.createTextNode("Job selected");
card.appendChild(accept);
}
I'm trying to add some text on the DOM after clicking, could someone recommend to me some Vue js documentacion to check info please
In your Vue component, create a data property for your display text:
data() {
return {
displayText: '',
}
}
Then, just put a reference to displayText in your template like so:
{{ displayText }}
Vue will initially display nothing, since displayText is empty, and the automatically update the DOM when displayText changes.
You would change the text in the requestSelected method like so:
requestSelected: function(request) {
request.accepted = true;
this.displayText = "Job selected";
}
Here's an example in codepen.

Aurelia, check when DOM is compiled?

How to check when DOM is compiled and inserted from Aurelia repeat cycle when the model is updated?
I have the following html:
<div clas="parent">
<div class="list-group">
<a repeat.for="$item of treeData">${$item.label}</a>
</div>
</div>
Here I need to know when all <a> tags are listed in the DOM, in order to run jquery scroll plugin on the parent <div> container.
At first load, I do that from the attached() method and all is fine.
When I update the treeData model from a listener, and try to update the jquery scroll plugin, it looks that the DOM is not compiled, so my scroll plugin can not update properly.
If I put timeout with some minimum value like 200ms it works, but I don't think it is a reliable workaround.
So is there a way to solve that?
Thanks!
My View Model:
#customElement('tree-view')
#inject(Element, ViewResources, BindingEngine)
export class TreeView {
#bindable data = [];
#bindable filterFunc = null;
#bindable filter = false;
#bindable selectedItem;
constructor(element, viewResources, bindingEngine) {
this.element = element;
this.viewResources = viewResources;
this.bindingEngine = bindingEngine;
}
bind(bindingContext, overrideContext) {
this.dataPropertySubscription = this.bindingEngine
.propertyObserver(this, 'data')
.subscribe((newItems, oldItems) => {
this.dataCollectionSubscription.dispose();
this._subscribeToDataCollectionChanges();
this.refresh();
});
this.refresh();
if (this.filter === true) {
this.filterChanged(this.filter);
}
if (this.selectedItem) {
this.selectedItemChanged(this.selectedItem);
}
}
attached() {
$(this.element).perfectScrollbar();
}
refresh() {
this.treeData = processData(this.data, this.filterFunc);
this.listItemMap = new WeakMap();
this.treeData.forEach(li => this.listItemMap.set(li.item, li));
this.filterChanged(this.filter);
$(this.element).perfectScrollbar('update');
}
This is only part of the code, but most valuable I think.
I attach the jq plugin in attached function and try to update it in refresh function. In general I have listener that track model in other view, which then update that one without triggering bind method.
An approach would be to use something called window.requestAnimationFrame (https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).
In your view-model, when you modify your treeData array, try calling
window.requestAnimationFrame(()=>{
$.fn.somePlugin();
});
Haven't tested this out, but based off what you're telling me, this might do what you need.
You could push your code onto the microTaskQueue, which will schedule your function to be executed on the next event loop. For instance:
import { TaskQueue } from 'aurelia-task-queue';
//...
#inject(Element, ViewResources, BindingEngine, TaskQueue)
export class TreeView {
constructor(element, viewResources, bindingEngine, taskQueue) {
this.element = element;
this.viewResources = viewResources;
this.bindingEngine = bindingEngine;
this.taskQueue = taskQueue;
}
refresh() {
this.treeData = processData(this.data, this.filterFunc);
this.listItemMap = new WeakMap();
this.treeData.forEach(li => this.listItemMap.set(li.item, li));
this.filterChanged(this.filter);
// queue another task, which will execute after the tasks queued above ^^^
this.taskQueue.queueMicroTask(() => {
$(this.element).perfectScrollbar('update');
});
}
}

Aurelia dialog not getting the view easy-webpack skeleton

Somehow when I let the framework load dialog view automatically, it said:
Failed to load view exception
However, when using inlineView it works as expected.
How can I make it load the view ?
It's a little late, but it may be useful for other users.
This error is because Webpack loader.
More info where: https://github.com/aurelia/dialog/issues/127
I don't like using string to reference the dialog ViewModel, because this I suggest using the option to force the View in dialog ViewModel. I don't have to change anything else.
Example:
Dialog ViewModel:
import {autoinject, useView} from 'aurelia-framework';
import {DialogController} from 'aurelia-dialog';
#autoinject()
#useView('./dialog-message.html') //This is the important line!!!!!
export class DialogMessage {
message: string = 'Default message for dialog';
constructor(private controller: DialogController){
controller.settings.centerHorizontalOnly = true;
}
activate(message) {
this.message = message;
}
}
Dialog View:
<template>
<ai-dialog>
<ai-dialog-body>
<h2>${message}</h2>
</ai-dialog-body>
<ai-dialog-footer>
<button click.trigger = "controller.cancel()">Cancel</button>
<button click.trigger = "controller.ok(message)">Ok</button>
</ai-dialog-footer>
</ai-dialog>
</template>
Method to show Dialog:
import {DialogMessage} from './dialog-message';
(...)
showDialog(){
this.dialogService.open({viewModel: DialogMessage, model: 'Hi, how are you?' }).then(response => {
if (!response.wasCancelled) {
console.log('good');
} else {
console.log('bad');
}
console.log(response.output);
});
}