Databinding dynamic value using $= does not work on "class" attribute - polymer

I have a problem with the $= operator.
I've got a component I've created, call it "somecomponent". This component works just fine but I want to add a class to help me customize some stuff. This class will be fetch from a datasource.
I create a property like this:
properties: {
somevar: {
type: String,
readOnly: true
},
...
I pass the value while creating my component in my webpage as such:
<somecomponent somevar="<?= $datasource['somevar'] ?>"></somecomponent>
and use it in my
<div class$="{{somevar}}"></div>
When I look in the Google Chrome inspector, I see the
<somecomponent somevar="somevalue"></somecomponent>
But in the div, I always see
<div class="style-scope somecomponent"></div>
What am I doing wrong?

Related

Html attribute camel-case with vue directive

I am working on a MVC project with Vue.js(Vue3) front-end.
For some reason when I am trying to use any directive that contains camel-case arguments, like in the example below, they are automatically turned to all lower-case, rendering my directive completely ineffective.
<label for="Name" class="required" v-init:camelCaseAttribute="'variableValue"> labelValue </label>
This also happens when I define said atributes with the use of a dictionary:
#Html.TextBoxFor(m => m.Registration.Section03.Address.ZipCode, htmlAttributes: new Dictionary<string, object> { { "v-init:camelCaseAttribute", "'variableValue'" } })
I know this is a HTML thing, but are there any known work-arounds for this?
Should have provided more context to the question:
const myComponent = { // my component
data: function () {
return {
camelCaseProperty: '' // property I am trying to initialise with some value
}
},
directives: {
init: { // custom init directive
mounted(el, binding, vnode) {
console.log(binding.arg)
binding.instance[binding.arg] = binding.value;
}
}
}
};
"init" is indeed not a built in Vue directive, so as I am trying to migrate the project from angularjs, which has an init directive, I tried to come up with something that offers a similar functionality to some extent, as seen in the snippet.
Writing the directive argument in kebab-case (v-init:camel-case-property="something") has no effect, as logging the binding.arg that actually reaches the directive is still in kebab-case.
This can happen; it is therefore recommended in to use kebab-case for all html attributes in Vue, including props.
HTML attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase
source

How to add a custom schema to Angular?

Problem
It's pretty common, that an Angular project wants to use some web-components, (aka. custom html tags). Of course, the compiler won't recognize these custom html tags and will complain:
1. If 'si-radio' is an Angular component, then verify that it is part of this module. 2. If 'si-radio' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '#NgModule.schemas' of this component to suppress this message.
If you put 'CUSTOM_ELEMENTS_SCHEMA' into play, your templates won't be validated anymore in that module and your code become extremely error-prone.
Solution (?)
It would be much reasonable, if you could define the tags and properties, which you really want to use, and they could pass the validation process.
As far as I know, Angular is using the ElementSchemaRegistry. If you use a web-component-library, where all the custom tags are prefixed like <custom-component-.. you could write a CustomSchemaRegistry like this:
export class CustomSchemaRegistry extends DomElementSchemaRegistry {
constructor() {
super();
}
hasElement(tagName: string, schemaMetas: SchemaMetadata[]): boolean {
const elementExists = tagName.includes('custom-component-') || super.hasElement(tagName, schemaMetas);
console.log(tagName, elementExists);
return elementExists;
}
hasProperty(tagName: string, propName: string, schemaMetas: SchemaMetadata[]): boolean {
return tagName.includes('custom-component-') || super.hasProperty(tagName, propName, schemaMetas);
}
}
And at bootstrapping you could provide it as a compilerOption:
platformBrowserDynamic()
.bootstrapModule(AppModule, {
providers: [{ provide: ElementSchemaRegistry, useValue: new CustomElementSchemaRegistry() }],
})
The problem is, it just doesn't work. And I don't even see any recommendation in the official Angular documentation for such cases. Looks like such a use-case wasn't foreseen, although it's extremely common.
#NgModule.schemas is an array of SchemaMetadata, which seems to be a pretty useless interface with a single name: string property...
https://angular.io/api/core/SchemaMetadata
Summing it up, is it actually possible to add a custom schema to Angular, allowing the usage of custom tags, while still preserving the template validation?

Subscribe to "after view change" when using [ngClass] in Angular [duplicate]

I have an Angular 2.4.0 application I'm working on with a form that has some backing Javascript validating/formatting a couple of the fields. When the formatting of the fields completes, the view does not update if the value returned from the formatting matches the original value attached to the model. Is there a way to force the view to update? Since there isn't a model change, forcing a component refresh hasn't had any effect. I'm guessing I'll need to update the view separately with something like jQuery, but I wanted to check if there was a better solution first.
Component:
export class Component {
field: string
formatField(updatedField: string) {
this.field = updatedField.replace(new Regexp("[^\\d]", "g"), ""); // remove everything except numbers
}
}
HTML:
<html>
<body>
<input type="text" [ngModel]="field" (ngModelChange)="formatField($event)">
</body>
</html>
In the above example, if the model is "1", then I enter "1;['];[", and formatField returns "1", the screen will still display "1;['];[" when I'm expecting "1" to display instead (which is the returned value of the formatField call).
Edit: fixed ngModelUpdate to ngModelChange in example (typo). Added Angular 2 version to description.
To refresh the view you need to explicitly run the change detector after you make a change to the model, then you can modify the model and ngModel will update the value.
constructor(private cd: ChangeDetectorRef) {}
formatField(updatedField: string) {
this.field = updatedField;
this.cd.detectChanges();
this.field = updatedField.replace(new RegExp("[^\\d]", "g"), ""); // remove everything except numbers
}

How to pass an object to child component while creating it in HTML template markup?

I'm doing the following (it's working as expected) in my parent component.
<app-textbox [info]="{caption:'Boink',value:'Oink'}"
... ></app-textbox>
In the receiving child component I have the following declaration.
#Input() info: any;
Now I want to improve the code and make it hard-typed, so I introduced and imported the following class.
export class TextBoxInfo { constructor(public caption: string, public value: string) { } }
Then, I updated the child component's input as follows.
#Input() info: TextBoxInfo;
Everything still works, as expected but I also wanted to improve the markup in HTML by switching to the following syntax.
<app-textbox [info]="new TextBoxInfo('Boink','Oink')"
... ></app-textbox>
That doesn't work and I'm getting the error message .
Uncaught Error: Template parse errors:
Parser Error: Unexpected token 'TextBoxInfo' at column 5 in [new TextBoxInfo('Boink', 'Oink')]
in ng:///AppModule/ParentComponent.html#45:24 ("
/div>
app-textbox [ERROR ->][info]="new TextBoxInfo('Boink', 'Oink')" ...
I've try to google to confirm or contradict that I can use the syntax like new Something(...) in the template's markup. Nothing conclusive this far. I also tried to google for the error but it's simply telling me that the syntax isn't recognized. I haven't found any viable examples of how to create an object and pass it in the template and googlearching it is complicated by the lack of good key words.
Am I approaching the object creation incorrectly?
Using type literals in templates is not supported. The scope of a template is the component instance, and therefore only properties of the component instance can be accessed.
If you need to reference identifiers outside of that scope, you need to move the code/expression to the components class and expose it to the template from there.
class MyComponent {
createTextBoxInfo(p1, p2):TextBoxInfo { return new TextBoxInfo(p1, p2); }
}
[info]="createTextBoxInfo('Boink','Oink')"
while this concrete case is a bad example in practice.
It would create a new TextBoxInfo every time change detection is run which is probably not what you want and will bring the performance of your app to its knees.
It's better to assign the value to a property and bind to that instead:
class MyComponent {
textBoxInfo = new TextBoxInfo('Boink','Oink'); }
}
[info]="textBoxInfo"

Angular2 Hiding element class member

I'm trying to use *ngIf to show and hide an element based on a member of the class its built on. But I keep encountering the following error despite being able to use other data members to display titles, etc.
TypeError: Cannot read property 'hidden' of undefined
My html looks like
...
<md-list-item *ngFor="let navigation of flatNavList"
(click)="onSelect(navigation)" *ngIf="navigation.hidden==false">
{{navigation.title}}
</md-list-item>
...
with a navigation array in my *component.ts file defined as
...
flatNavList: Navigation[];
...
and the Navigation class looking like
export class Navigation {
constructor(
public name: string,
public title: string,
public icon: string,
public location: string,
public hidden: boolean,
public roles: string[],
public children: Navigation[]
) { }
}
If I remove the *ngIf everything renders fine. Why can I use navigation.title to display the name, yet I can't use navigation.hidden to toggle whether or not the element is displayed?
EDIT------------------------------
[hidden]="navigation.hidden"
But several articles say that this is bad practice in Angular 2
It appears that navigation is undefined sometimes.
So first test for it and then check its hidden property:
*ngIf="navigation && !navigation.hidden"