ContentChildren angular 2 and reference to a HTMLElement - angular-components

I have a Component and I use it in this way:
<ImgList>
<img src="../assets/img/file1.jpg" width="30px" height="20px" />
<img src="../assets/img/file2.jpg" width="30px" height="20px" />
</ImgList>
I want in export class ImgListComponent to take a reference to the first image, I tried:
#ContentChildren('img', {read: ElementRef}) imgEls: QueryList<ElementRef>;
and in:
ngAfterContentInit() {
console.log(this.imgEls.toArray[0])
}
but console displays undefined.

The first parameter of ContentChild and ContentChildren isn't a CSS selector but a component reference. You can create a fake component to reference all the images, like:
#Directive({selector: 'img[foo]'}) exports class ImageDirective {}
then you can implement your ContentChildren in this way:
#ContentChildren(ImageDirective) imgEls: QueryList<ImageDirective>;
remember to declare the new directive in your module.

Related

div tag class attribute contains lot of strings and cannot be replaced with css string definition string

I am trying to develop an UI and the first step is to create CssLayout. Each CssLayout component is added hierarchically with and many CssLayout component.
The problem is when i run the application and inspect the div tags, the class attribute has extra strings that needs to be removed.
<div class="v-csslayout v-layout v-widget .content-container v-
csslayout-.content-container v-has-width v-has-height" style="width: 100%;
height: 100%;"><div class="v-csslayout v-layout v-widget .inner-content-
container v-csslayout-.inner-content-container"></div></div>
and what I need is
<div class=".content-container">
<div class=".inner-content-container">
</div>
</div>
Java Code:
#StyleSheet("{css/spreadjsdefault.css}")
public class SpreadJSWidget extends CssLayout {
/**
*
*/
public SpreadJSWidget() {
super();
addStyleName(".content-container");
CssLayout mainBox = new CssLayout();
mainBox.addStyleName(".inner-content-container");
addComponent(mainBox);
}
spreadjsdefault.css (They are empty for now)
.content-container
{
}
.inner-content-container
{
}
Please advice !
Two things:
In order to be able to properly match the css rules, you have to omit the leading . when adding the style name, i.e. addStyleName("contentContainer"). This way, the css elements will match your style definition.
Css classes like v-csslayout are default classes defined by vaadin used by the default themes to provide a basic layout. They are there by default and can't (and actually shouldnt) be removed entirely. What you can do, however, is to define and overwrite these rules yourself. What's important: Either way, your custom classes will still match when you define them in your style sheet and can overwrite the default theming.

Using external VueJs components in a VueJS project

I'm trying to use VueStrap in a VueJS project, it looks like webpacker is loading it fine, I can see this in the terminal output, however, when I try use a component from vue-strap I get this error:
[Vue warn]: Property or method "input" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I tried including the VueStrap as a component in the Vue instance but couldn't get it to work. How do I include the VueStrap into as a component correctly?
Thanks!
This is my application.js:
import Vue from 'vue/dist/vue.js'
import App from '../components/app.vue'
import VueStrap from 'vue-strap'
document.addEventListener('DOMContentLoaded', () => {
document.body.appendChild(document.createElement('app'))
const app = new Vue({
el: 'app',
template: '<App/>',
components: { App }
})
console.log('app')
})
This is my app.vue file
<template>
<div id='app'>
<p> {{ message }} </p>
<bs-input :value.sync="input" label="Username" help="Only allows lowercase letters and numbers."
error="Insert username" placeholder="Username can't start with a number." pattern="^[a-z][a-z0-9]+$"
:mask="mask" minlength="5" readonly required icon>
</bs-input>
</div>
</template>
<script>
export default {
data: function () {
return {
message: "Hello World"
}
}
}
</script>
<style scoped>
p {
font-size: 2em;
text-align: left;
}
</style>
Please see the documentation on complication scope, notably:
Everything in the parent template is compiled in parent scope; everything in the child template is compiled in child scope.
Your template includes input and mask properties but your data function does not set those up. They need to be set up and reactive so if they change, Vue can pass them down to the child component (which it looks like your bs-input component exposes an input and mask properties).

Angular 2: How to link form elements across a dynamically created components?

I have a set of form fields that are in a dynamically created component. The parent Component owns the form tag. However, none of the form fields are being added to the Form. I'm using the ComponentFactoryResolver to create the component:
#Component({
selector: 'fieldset-container',
templateUrl: './fieldset-container.component.html',
styleUrls: ['./fieldset-container.component.scss'],
entryComponents: ALL_FIELD_SETS,
})
export class FieldsetContainerComponent<C> {
fieldsetComponent : ComponentRef<any> = null;
#Input() formGroup : FormGroup;
#ViewChild('fieldSetContainer', {read: ViewContainerRef})
fieldsetContainer : ViewContainerRef;
#Output() onComponentCreation = new EventEmitter<ComponentRef<any>>();
constructor(private resolver : ComponentFactoryResolver) {
}
#Input() set fieldset( fieldset : {component : any, resolve : any }) {
if( !fieldset ) return; // sorry not right
// Inputs need to be in the following format to be resolved properly
let inputProviders = Object.keys(fieldset.resolve).map((resolveName) => {return {provide: resolveName, useValue: fieldset.resolve[resolveName]};});
let resolvedInputs = ReflectiveInjector.resolve(inputProviders);
// We create an injector out of the data we want to pass down and this components injector
let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.fieldsetContainer.parentInjector);
// We create a factory out of the component we want to create
let factory = this.resolver.resolveComponentFactory(findComponentForFieldset(fieldset.component));
// We create the component using the factory and the injector
let component : ComponentRef<any> = factory.create(injector);
// We insert the component into the dom container
this.fieldsetContainer.insert(component.hostView);
// Destroy the previously created component
if (this.fieldsetComponent) {
this.fieldsetComponent.destroy();
}
this.fieldsetComponent = component;
this.onComponentCreation.emit( this.fieldsetComponent );
}
}
The template:
<div #fieldSetContainer [formGroup]="formGroup"></div>
The usage of the dynamic component:
<form class="form" #omaForm="ngForm">
<div *ngFor="let fieldset of page?.fieldsets">
<fieldset-container [fieldset]="{ component: fieldset, resolve: {} }" (onComponentCreation)="onComponentCreation($event)" [formGroup]="omaForm.form"></fieldset-container>
</div>
</form>
I suspect it has something to do with the injector not being hooked up correctly, but from what I can tell it is chained to the parent. I've set a breakpoint in NgModel and it is passed a null for parent which is the problem. I traced that back up into something that looks compiled and it was just hard coding a null. So I'm not sure how that was created with hard coded nulls in there.
Any ideas on how to fix this?
Ok it turns out it has nothing to do with the dynamic nature of this component. I removed it and defined all of my components inline and it still had the problem. The issue was that having form controls inside a Component that were nested within a form tag is just not supported by Angular out of the box. Once you nest a form control in a component it can't see the NgForm anymore which is crazy.
After reading solutions on the web and seeing that no one had a good solution I designed 2 of my own directives that registered the Form into the DI container up at the NgForm, then using DI hierarchy I could inject that into another Directive that would perform the registration below.
Parent Component Template:
<form nested>
<my-component .../>
</form>
Child Component Template:
<div>
<input name="street" [(ngModel)]="address.street" required nest/>
<input name="city" [(ngModel)]="address.city" required nest/>
<input name="state" [(ngModel)]="address.state" required nest/>
<input name="zip" [(ngModel)]="address.zip" required nest/>
</div>
Once I had this in place then I could bring back my dynamic component and it worked perfectly. It was just really hard to get there.
It's really elegant and simple and doesn't require me to pass the form instance down through the layers like so many suggestions on the web show. And the work to register a form control whether it's 1 layer or 999 layers removed is the same.
IMHO NgForm and NgModel should just do this out of the box for us, but they don't which leads to complicated architecture design to accomplish moderately advanced forms.

AngularDart dynamically change the page title

One of my components pulls a page title (and content) from MongoDB based off of the route and stores the title in an instance variable of that component's class. What would be the best way to give some mustaches in the <title> tag a variable to display such as <title>Site Title | {{pageName}}</title>? The <title> tag is outside of the scope of the component. Maybe a controller would fit this use case but I'm not really sure what the state of the controller directive or the root object is. I thought about making a service and injecting it in both but that seems like overkill. Is there a better way to do this? Thanks!
I ended up making a new component and a service.
My simplified index.html (Pretty much no changes):
<html ng-app>
<title>Site Title</title>
</head>
<body></body>
Service to store one variable:
import 'package:angular/angular.dart';
#Injectable()
class Global {
String pageTitle;
Global();
}
And component with a selector of title:
library title;
import 'package:angular/angular.dart';
import 'package:mypackage/service/global.dart';
#Component(
selector: 'title',
template: 'Site Title | {{titleComp.global.pageTitle}}',
publishAs: 'titleComp',
useShadowDom: false)
class TitleComponent {
final Global global;
TitleComponent(this.global);
}
I then injected Global into another component and modified global.pageTitle whenever a new page was loaded.
I would love to hear any improvements to this or ways to use a root controller or root scope.
I'm fairly certain this is not possible -- I don't think angular is able to do any interpolation outside the body tag. Your best bet is to use the Document.title() method.
You can use something like that
<title ng-bind="header"></title>
And then inside your Controller
testController = function($scope, $rootScope) {
$scope.someFunction = function() {
$rootScope.header = "Test";
}
}

Access Polymer inner element programmatically

I'm enclosing my app in a Polymer element and I want to use another polymer element inside it. To call all the method of the inner element I'm trying to use $[].
Insider the external polymer element I have this:
ImageEditor ime;
DivElement div2;
ImageTool.created(): super.created(){
div2 = $["secondDiv"];
ime = $["imageEditor1"]
}
In the Html I simply have:
<polymer-element name="da-imagetool">
<template>
<div class="images" id="mainDiv">
<da-imageeditor id="imageEditor1" name="ied"></da-imageeditor>
with the script src at the end.
For some reason I get an exception when I assign the imageEditor1 to ime.
Exception: type 'HtmlElement' is not a subtype of type 'ImageEditor' of 'value'.
It looks like the browser hasn't upgraded the <da-imageeditor> elements.
Make sure that you <import> the <da-imageeditor> element, and have the correct #CustomTag annotation on the ImageEditor class declaration.
This is most likely an issue with the import path.
If you don't use the right path the type is not recognized (canonicalization problem)
This bug should be solved since a while
https://code.google.com/p/dart/issues/detail?id=15953
but I haven't worked with Polymer since.
Show your import paths (HTML and Dart) and the directory structure of your app (where is your entry page and your Polymer elements) then I'll take a look.
Which version of dart-polymer are you using? With the 0.9.5, the following lines:
XElement.created(): super.created() { print($['el-id']); }
void enteredView() { print($['el-id']); }
In created(), the referred element gives nothing whereas in enteredView(), it does refer to the specific element of the shadow root.
The behavior disappears if shadowRoot.querySelector('#el-id') is used in lieu of the shorthand map $['el-id'].

Resources