Is there a CSS selector for derived HTML custom elements? - css-selectors

Say there is a custom HTML element defined in the typical way, and another that inherits from it:
class CEParent extends HTMLElement { constructor() { super(); } }
class CEChild extends CEParent { constructor() { super(); } }
customElements.define("ce-parent", CEParent);
customElements.define("ce-child", CEChild);
Now suppose the HTML contains only references to <ce-child></ce-child> elements. Is it possible to select / style <ce-child> in terms of ce-parent?
Like
ce-parent:and-derived-too {
...
}
?

This assigns parent Element names as CSS class on every element
Requires some string handling, because constructor.name is "" (empty string) for Anonymous functions
So Bono (U2) in the DOM is:
<style>
body {font-size:1.2em }
.Homininae {background: lightgreen; display:block;clear:both }
.Hominini {background:green; }
.Homo {color: gold; }
*:after { content:attr(class); float:right}
</style>
<script>
class Homininae extends HTMLElement {
connectedCallback() {
function proto(ref, chain = '', str = ref.toString()) {
if (str.slice(0, 5) === "class")
return proto(ref.__proto__, chain + " " + str.match(/class (\w*)/)[1]);
else return chain;
}
let classes = proto(this.constructor.__proto__);
this.className = classes;
this.innerHTML = `<b>${this.innerHTML}</b> (${this.nodeName}) `;
}
}
class Gorillini extends Homininae {};
class Hominini extends Homininae {};
class Pan extends Hominini {};
class Homo extends Hominini {};
customElements.define('a-human' , class extends Homo {});
customElements.define('a-bonobo' , class extends Pan {});
customElements.define('a-chimpanzee', class extends Pan {});
customElements.define('a-gorilla' , class extends Gorillini {});
</script>
<a-human>Bono</a-human>
<a-bonobo>Kanzi</a-bonobo>
<a-chimpanzee>Ham the Astrochimp</a-chimpanzee>
<a-chimpanzee>Bonzo</a-chimpanzee>
<a-gorilla>Koko</a-gorilla>

Related

Array in service not found in method angular 2

hi i have array in servcie and I have a strange problem in the saveData array function is visible but in removeItems no
my service.ts
export class FormService {
myArray = []
constructor(){
}
saveData(key, value) {
this.myArray.push({
key: key,
value: value
});
//HERE IS VISIBLE and i can add may elements
console.log('myArray')
console.log(this.myArray)
}
removeItems(myIndex) {
//here is not visible is empty
console.log(this.myArray)
}
}
UPDATE
my component
export class Step3AdditionalServicesComponent implements OnInit {
constructor(private formService: FormService) {
}
ngOnInit() {
this.stepsGuard.getAccess(false);
}
addToMyArray(key,value) {
this.formService.saveData(key,value)
}
removeItemFromArray(event) {
var myIndex = event.target.id;
console.log('myIndex ' + myIndex);
this.formService.removeItems(myIndex);
}
}
my html
<button (click)="addToMyArray('key1','simple1')">Add Simle1</button>
<button (click)="addToMyArray('key2','simple2')">Add Simle2</button>
<button (click)="addToMyArray('key3','simple3')">Add Simle3</button>
<button id="1" (click)="removeItemFromArray($event)">Remove 1</button>
<button id="2" (click)="removeItemFromArray($event)">Remove 2</button>
<button id="3" (click)="removeItemFromArray($event)">Remove 3</button>
And when i click removeItemFromArray($event) i see index in console
----SOLUTION----
I declare my service in component providers and in app.module
my.component
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css'],
providers: [FormService] <---delete this and will be working
})
#brijmcq Thanks for help
Most of the time, declare your service in the module level and not in the component level unless you know what you are doing. You are probably getting the wrong data because if you declare it in component level, you will always get a new instance of FormService and the myArray = [] will always be empty.
You probably know what happens next if you access an empty array by index :)
Here is how I remove and add elements in my array:
elementsArray = [];
addElementList(event) {
let element = event.data;
if (!this.elementsArray.includes(element)) {
this.elementsArray.push(element);
console.log("Elements in array = " + this.elementsArray);
}
}
removeElementList(event) {
let element = event.data;
let index = this.elementsArray.indexOf(element, 0);
if (index > -1) {
this.elementsArray.splice(index, 1);
console.log("Elements in array = " + this.elementsArray);
}
}

Runtime Error Cannot read property 'element' of undefined in angular 2?

I have created form builder validators that do a validation perfectly but i need get a error message from validators so i have tried some method that i shown below
import { Injectable,Output,Component} from '#angular/core';
import { FormGroup, FormControl} from '#angular/forms';
import{Sharedata} from '../providers/sharedata';
export class NextPayDayValidator1 {
public error: any="Next Pay Date should be greater than todays date";
constructor(public Share:Sharedata){
}
public isValid(control: FormControl): any {
// console.log("false");
let formGroup = control.parent;
var IsValid=true;
var errormessage;
// this.Err="eerere";
if(formGroup) {
console.log("true");
var SelVal_Howpaid=formGroup.get('Frequency').value;
console.log(SelVal_Howpaid);
var today = new Date();
var today_date = today.getDate();
var today_month = today.getMonth();
var today_year = today.getFullYear();
var weekNo = 0;
for (var index = week[0]; index <= week[1]; index++) {
weekNo++;
}
var nextpaydaycontrol=formGroup.get('NextPayDate');
var date_next = new Date( formGroup.get('NextPayDate').value);
var date_nextpaydate = date_next.getDate();
var month_nextpaydate = date_next.getMonth();
var year_nextpaydate = date_next.getFullYear();
console.log("nextpaydaycontrol"+date_next +"date"+date_nextpaydate+"month"+month_nextpaydate+"year"+year_nextpaydate);
if (nextpaydaycontrol.value == "") {
IsValid = false;
errormessage = "* Please select Next Pay Date";
console.log(errormessage);
this.error=errormessage;
alert(this.error);
return{
" * Please select Next Pay Date":true
}
}
}
}
my addleads.html
<ion-item>
<ion-label>Next Pay Date:</ion-label>
<!--<ion-input formControlName="NextPayDate" type="number"></ion-input>-->
<ion-datetime displayFormat="MM/DD/YYYY" formControlName="NextPayDate" (ionChange)="npay()"></ion-datetime>
</ion-item>
<span style="color:red" *ngIf="!RegisterForm3.controls.NextPayDate.valid && (!RegisterForm3.controls.NextPayDate.dirty || submitAttempt)">ddd </span>
<span style="color:red" *ngIf="!RegisterForm3.controls.NextPayDate.valid && RegisterForm3.controls.NextPayDate.hasError('Paymustgreaterthentodaydate') && (!RegisterForm3.controls.NextPayDate.dirty || submitAttempt)"> * Next Payday Must Be Greater Than Today's Date </span>
<span style="color:red" *ngIf="!RegisterForm3.controls.NextPayDate.valid && RegisterForm3.controls.NextPayDate.hasError('Invalid') && (!RegisterForm3.controls.NextPayDate.dirty || submitAttempt)"> * Invalid Next Payday</span>
<hr/>
and my addlead ts
import { Component} from '#angular/core';
import { NavController,Platform } from 'ionic-angular';
import {ViewChild} from '#angular/core';
import {Content} from 'ionic-angular';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import{Sharedata} from '../../providers/sharedata';
import {NextPayDayValidator1} from '../../validators/NextPayDate'
import 'rxjs/add/operator/map';
/*
Generated class for the AddLeads page.
See http://ionicframework.com/docs/v2/components/#navigation for more info on
Ionic pages and navigation.
*/
#Component({
templateUrl: 'add-leads.html'
})
export class AddLeadsPage {
RegisterForm3: FormGroup;
public NextPayDateErrorMsg:any;
loading: any;
constructor(public navCtrl: NavController,platform: Platform,public np:NextPayDayValidator1 ,private datePipe: DatePipe, public formBuilder: FormBuilder, public toastCtrl: ToastController,public loginservice :Loginservice,public sharedata:Sharedata, public loadingCtrl: LoadingController,
public http: Http, public alertCtrl: AlertController) {
this.NextPayDateErrorMsg=this.np.error;
alert(this.NextPayDateErrorMsg)
this.RegisterForm3 = formBuilder.group({
LastPayDate: ['', Validators.required],
NextPayDate:['',np.isValid]//here i invoke my validator
});
}
}
the above code i have create error global variable.if any error occurs assign errormsg to error global variable;if i trying to execute validation validation will fires error messge shown in alert after closing alert me getting a error like
EXCEPTION: Error in ./AddLeadsPage class AddLeadsPage - inline
template:427:65 caused by: Cannot set property 'error' of undefined
any one help me to fix this bugs
You have set a custom validator NextPayDayValidator1 as a component instead of a directive. A component cannot be injected like a provider.
Check here for custom validators.
#Directive({
selector: '[next-pay-valid]',
providers: [{provide: NG_VALIDATORS, useExisting: NextPayDayValidator1, multi: true}]
})
export class NextPayDayValidator1 implements Validator, OnChanges {..}
Or create a provider for NextPayDayValidator1
#Injectable()
export class NextPayDayValidator1 {
public error: any="Next Pay Date should be greater than todays date";
constructor(public Share:Sharedata){
}
public isValid(control: FormControl): any {
// console.log("false");
let formGroup = control.parent;
var IsValid=true;
//....
}
}
and inject in your component constructor.

Refresh sortable when a new item is added to the array

Sortable component only shows initial array elements. When a new value is pushed into array, the sortable does not display it.
Component:
import { Component } from '#angular/core';
#Component({
selector: 'custom-item-template-demo',
templateUrl: './custom-item-template.html'
})
export class CustomItemTemplateDemoComponent {
public itemStringsLeft: any[] = [
'Windstorm',
'Bombasto',
'Magneta',
'Tornado'
];
public addItem() {
this.itemStringsLeft.push("new item");
}
}
Template:
<button type="button" (click)="addItem()">Add</button>
<template #itemTemplate let-item="item" let-index="index"><span>{{index}}: {{item.value}}</span></template>
{{itemStringsLeft.length}}
<pre>{{ itemStringsLeft | json }}</pre>
<bs-sortable
[(ngModel)]="itemStringsLeft"
[itemTemplate]="itemTemplate"
itemClass="sortable-item"
itemActiveClass="sortable-item-active"
placeholderItem="Drag here"
placeholderClass="placeholderStyle"
wrapperClass="sortable-wrapper"
></bs-sortable>
Workaround: Call manually to writeValue of the SortableComponent
import { Component, ViewChild } from '#angular/core';
#Component({
selector: 'custom-item-template-demo',
templateUrl: './custom-item-template.html'
})
export class CustomItemTemplateDemoComponent {
public itemStringsLeft: any[] = [
'Windstorm',
'Bombasto',
'Magneta',
'Tornado'
];
#ViewChild(SortableComponent, {static: false}) sortableComponent: SortableComponent;
public addItem() {
this.itemStringsLeft.push("new item");
this.sortableComponent.writeValue(this.itemStringsLeft);
}
}
Another workaround with re asigning list values:
public addItem() {
this.itemStringsLeft.push("new item");
this.itemStringsLeft = this.itemStringsLeft.slice();
}
should be add import { SortableComponent } from 'ngx-bootstrap';
It will working.
The spread operator worked well for me with this issue:
addItem(){
this.itemStringsLeft = [...this.itemStringsLeft, {name: 'Windstorm', range: 5}];
}

Ionic2 and AngularJS2 application using Typescript doesn't find type

I am trying to build an Ionic2 and AngularJS2 application using TypeScript, and I am getting the error: EXCEPTION: No provider for CalEvent!
// File event.ts
export class CalEvent {
name: string;
date: Date;
description: string;
isComplete: boolean;
constructor(n: string, d: Date){
this.name = n;
this.date= d;
}
toString(){
return this.name + ' at ' + this.date;
}
}
/*****************************/
// File event_card_large.ts
import {Component} from 'angular2/core'
import {CalEvent} from '../classes/event.ts';
#Component({
selector: 'event-card-large',
template: '<div style="color: red;">here</div>'
})
export class EventCardLarge{
constructor(public calEvent: CalEvent){}
}
/*****************************/
// File my_page.ts
import {Page} from 'ionic-angular';
import {CalEvent} from '../../classes/event.ts';
import {EventCardLarge} from '../../components/event_card_large.ts';
#Page({
templateUrl: 'build/pages/my_page/my_page.html',
directives:[EventCardLarge]
})
export class MyPage {
public pageName: string;
public testItems: CalEvent[];
selectedItem = 0;
constructor() {
// Test code
this.pageName = 'Test Page 2016-05-17';
this.testItems = [];
let d1 = new Date('2016-05-17');
let ce = new CalEvent('The name', d1);
ce.isComplete = true;
this.testItems.push();
}
}
/*****************************/
// File my_page.html
...
<ion-item *ngFor="#v of testItems; #i = index" >
<event-card-large [calEvent]="v">Loading...</event-card-large>
</ion-item>
...
Thanks for any help.
The problem is you are trying to inject CalEvent in EventCardLarge:
constructor(public calEvent: CalEvent){}
Just declare calEvent as a member of EventCardLarge:
export class EventCardLarge{
calEvent: CalEvent;
constructor(){}
}
It turned out that I needed to make the CalEvent class a "provider", and inject it in the EventCardLarge metadata.
#Component({
selector: 'event-card-large',
template: '<div style="color: red;">here</div>',
providers: [CalEvent]
})

AngularJS / Typescript - Directive with dynamic template loading and events

I've got the following directive (I've cut out the non-important part for the sake of brevity) :
I basically use the ng-include directive to dynamically load my templates.
Directive
module chat.directives {
"use strict";
export class ChatWindow implements ng.IDirective {
public restrict = 'E'
public static DirectoryName = "chatWindow";
public scope = {
chatModel: '=',
chatSection: '#chatSection'
}
public template(element : ng.IAugmentedJQuery, attrs : IChatWindowAtributes) {
return '<div ng-include="getTemplateUrl()"></div>';
}
public link(scope:chat.controllers.IChatScope, element:ng.IAugmentedJQuery, attrs:ng.IAttributes) {
scope.getTemplateUrl = () => {
return "/chat/directives/" + scope.chatSection + "-chat-window-template.html";
}
}
public static factory():ng.IDirectiveFactory {
var directive = () => new ChatWindow();
directive.$inject = [];
return directive;
}
}
interface IChatWindowAtributes extends ng.IAttributes {
chatSection : string;
}
angular
.module('chat')
.directive(chat.directives.ChatWindow.DirectoryName, chat.directives.ChatWindow.factory());
}
Problems do arise as soon as I try to implement an event in one of the templates, for example ng-click.
Because I am using the ng-include hack, element.html() of the linkage function refers to <div ng-include="getTemplateUrl()"></div> and thus doesn't allow me to setup an eventhandler programmatically.
HTML Template
<!-- Chat window-->
<div class="col-xs-4 chat-window">
<aside>
<div class="panel panel-default">
<div class="panel-heading">
<span class="glyphicon glyphicon-list-alt icon"></span>
</div>
<div class="chat-output">
</div>
<form action="">
<input id="msgInput" autocomplete="off"/>
<button ng-click="chat.sendMessage('hi')" stop-event>Send</button>
</form>
</div>
</aside>
</div>
Additionally, any call from the template with ng-click="chat.sendMessage('hi')" doesn't trigger the corresponding function in my controller.
Controller Implementation
module chat.controllers {
'use strict';
class ChatCtrl {
public chatHistory : Array<Common.Services.IMessage> = [];
public chatSection : string;
public currentMessage : string;
// $inject annotation.
// It provides $injector with information about dependencies to be injected into constructor
// it is better to have it close to the constructor, because the parameters must match in count and type.
// See http://docs.angularjs.org/guide/di
public static $inject = [
'$scope',
'UserService',
'MessageService'
];
// dependencies are injected via AngularJS $injector
constructor(private $scope: IChatScope, private userService : User.Services.IUserService, private messageService : Common.Services.IMessageService) {
this.$scope.chatModel = {
chatHistory: this.chatHistory,
userService : this.userService,
messageService: this.messageService,
storeChatSectionInCtrl : this.storeChatSectionInCtrl,
subscribeToChatSectionEvents : this.subscribeToChatSectionEvents,
fetchChatHistory : this.fetchChatHistory
}
}
public storeChatSectionInCtrl(section){
// Store the section additionally to the directive in the controller
this.chatSection = section;
}
public subscribeToChatSectionEvents(section : string){
var self = this;
// Subscribe for the chat section for incoming messages
this.messageService.addMessageListener(section + "ChatMessage", function(){
});
// Subscribe for incoming messages to load the chat history
this.messageService.addMessageListener(section + "ChatHistory", function(message : ChatHistoryMessage){
self.chatHistory = message.data.chatHistory;
});
}
// Send a chat message to the server
public sendMessage(message : string){
var messageObj = new ChatInputMessage({
chatSectionPrefix : this.chatSection,
chatMessageObj : new ChatMessage({
message : message
})
});
this.messageService.sendMessage(messageObj);
}
// Send a request for the chat history to the server
public fetchChatHistory(section : string){
var messageObj : ChatHistoryMessage = new ChatHistoryMessage({
chatSectionPrefix : section,
chatHistory : null
});
// Send a request in order to retrieve the chat history of the given section
this.messageService.sendMessage(messageObj);
}
}
export interface IChatScope extends ng.IScope {
getTemplateUrl : () => string;
chatModel : IChatModel;
chatSection : string;
}
export interface IChatModel {
chatHistory : Array<Common.Services.IMessage>;
userService: User.Services.IUserService;
messageService : Common.Services.IMessageService;
storeChatSectionInCtrl : (section : string) => void;
subscribeToChatSectionEvents: (nameOfEventListener : string) => void;
fetchChatHistory : (section: string) => void;
}
// Message
export interface IChatMessage {
message : string;
creationDate? : string;
from? : string;
to? : string;
}
export class ChatMessage implements IChatMessage {
public message : string;
public creationDate : string;
public from : string;
public to : string;
constructor(message : IChatMessage){
this.message = message.message;
this.creationDate = message.creationDate;
this.from = message.from;
this.to = message.to;
}
};
// Message Service
export interface IChatData{
chatSectionPrefix : string;
chatMessageObj : IChatMessage;
}
export class ChatInputMessage extends Common.Services.ClientMessage<IChatData> {
static NAME = "ChatMessage";
constructor (chatData: IChatData) {
super(chatData.chatSectionPrefix + ChatInputMessage.NAME, chatData);
}
}
// Chat history
export class ChatHistoryMessage extends Common.Services.ClientMessage<IChatHistory> {
static NAME = "ChatHistory";
constructor (chatData: IChatHistory) {
super(chatData.chatSectionPrefix + ChatHistoryMessage.NAME, chatData);
}
}
export interface IChatHistory{
chatSectionPrefix : string;
chatHistory : Array<IChatMessage>;
}
/**
* #ngdoc object
* #name chat.controller:ChatCtrl
*
* #description
*
*/
angular
.module('chat')
.controller('ChatCtrl', ChatCtrl);
}
HTML
<div ng-controller="ChatCtrl">
<chat-window chat-model="chatModel" chat-section='lobby'></chat-window>
</div>
Solution
For some reason I though I would operate on the controller instead of the directive - thus using sendMessage('hi') instead of chat.sendMessage('hi') solved the issue ...

Resources