Static #ViewChildren - angular11

I have a situation where I was to replace
#ViewChild(Foo, { static: true }) foo!: Foo;
with a version that gets several Foo in a list.
The expected solution is
#ViewChildren(Foo, { static: true }) foos!: QueryList<Foo>;
but #ViewChildren doesn’t support static.
Is there any work-around here?
The use-case is that I have something like this:
#ViewChild(Foo, { static: true }) foo!: Foo;
ngOnInit(): void {
onFooInit(this.foo);
}
ngAfterViewInit(): void {
afterViewFooInit(this.foo);
}
and I need it to become
#ViewChildren(Foo, { static: true }) foos!: QueryList<Foo>;
ngOnInit(): void {
this.foos.forEach(onFooInit);
}
ngAfterViewInit(): void {
this.foos.forEach(afterViewFooInit);
}
where onFooInit and afterViewFooInit are black-box library functions whose documentation says to call them in ngOnInit and ngAfterViewInit, respectively, noting that { static: true } is necessary to do so. (I have tested this, and yes, there are errors without { static: true }.)
I tried some hackery with #ViewChild(Foo, { static: true }) set foo(foo: Foo) { this.foos.push(foo); }, which unsurprisingly went nowhere, and I tried to create my own custom decorator that internally called ViewChild on each match of the selector, which I couldn’t get working. One thing I know I could do is just use ViewChild on each individual thing I want to include in the list, and then hard-code the actual list creation from those, but I want to re-use this code in several components which is going to get painful real fast.
Then I realized this should really be a directive, but the ElementRef in the directive has the same problems, being undefined in ngOnInit (I guess it isn’t static). The alternative to a directive is a wrapping component, but then I have to worry about all the inputs which is a huge pain. Probably better than the hard-coded polymorphic list, though.
I am open to suggestions on better ways to avoid this kind of initialization boilerplate on each of these components (that are very similar to one another, just with slightly different fields in each case).

Related

Is it possible to define and overwrite Sass/SCSS variables based on conditions

I have an I have a _overrides.scss file in which I want to provide a global variable based on conditions.
The variable should be true if my navigation with the .side-navigation class also contains the .-open class. If it contains the -closed class, the variable should have the value false.
Something like this:
$navbarOpen: false;
.side-navigation {
&.-open {
$navbarOpen: true;
}
&.-closed {
$navbarOpen: false;
}
}
I want to use the variable within another SCSS module in React, like:
#import 'overrides';
#if $navbarOpen == true {
footer {
background: red;
}
}
The variable is recognized, but the value is always false since it doesn't seem to be overridden by the condition set in _overrides.scss.
I think that the problem is that Sass variables can't be changed in the runtime, it'll be compiled to plain CSS and all vars will be replaced. Although as I see your condition depends on runtime events.
Check this for references: Dynamic Sass Variables

Dynamically switch global CSS for Angular8 app (client branding)

I want to dynamically switch Angulars global CSS files based on which client is connecting. This will be used for client-branding purposes, including fonts, colors, photos, headers, footers, button-styles, etc.
Each client has provided us with a CSS file, which we need to integrate into our app. We have hundreds of clients.
Current solution is to try and override the CSS of individual components at load. This is bad because it adds a lot of boilerplate:
Html:
<link id="theme" rel="stylesheet" href="./assets/stylesheets/{{cclientCode}}.css">
ts:
ngOnInit() {
this.service.clientCode.subscribe(clientCode => this.clientCode = clientCode);
}
My workaround isn't working because the link html is called before the {{}} has a chance to load in the value.
I'm also not motivated to fix my workaround because its just that -a workaround. Instead, I want to implement something that works globally, without any per-component boilerplate.
What I want is the ability to dynamically switch the global Angular style for each client. So something like:
"styles": [
"src/assets/stylesheets/angular_style.css",
"src/assets/stylesheets/client_style.css"
]
Where client_style.css is served differently to each client.
I've found a solution that I think is workable. It definitely has issues though, so if anyone has their own answer, please still share!
First, I added a clientCode String field to SessionDataService, a global service I use to move component-agnostic data around my app:
export class SessionDataService {
clientCode: BehaviorSubject<String>;
constructor(){
this.clientCode = new BehaviorSubject('client_default');
}
setClientCode(value: String) {
this.clientCode.next(value);
}
}
Then, inside app.component.ts, I added a BehaviorSubject listener to bring in the value of clientCode dynamically:
public clientCode: String;
constructor(private service : SessionDataService) {
this.service.clientCode.subscribe(clientCode => this.clientCode = clientCode);
}
Next, I added a wrapper around my entire app.component.html:
<div [ngClass]="clientCode">
--> ALL app components go here (including <router-outlet>)
</div>
So at this point, I've created a system that dynamically adds client-code CSS classes to my components, including all children :)
Finally, I just have to write CSS rules:
.ClientOne p {
color: red;
}
.ClientOne .btn {
background-color: red;
}
.ClientTwo.dashboard {
height: 15%;
}
I hope this helps somebody! Essentially the "trick" here is to add a ngClass that wraps the entire app, and then justify all client-specific CSS rules with their client code.

Flowtype "Not covered by Flow" on object property chains

I'm trying to use Flow, but I keep getting the "Not covered by Flow" warning, so my code is mostly underlined. I checked the Flow documentation, but it wasn't helpful regarding object property chaining, so how do you get something like this to work?
It appears that you are using a library that does not have type definitions.
With property lookups where the object is defined within the file, Flow has 100% code coverage without any types at all:
const foo = { bar: { baz: 2 } };
foo.bar.baz;
// 100% Flow coverage
Same goes for separate files:
1.js
// #flow
export default { bar: { baz: 2 } };
2.js
// #flow
import foo from './1.js'
foo.bar.baz;
// 100% code coverage
However, as soon as something is being imported from a file that Flow does not run on (either because it has flow turned off or because its a third-party library that does not use flow), Flow is not able to cover it.
1.js
// #noflow
export default { bar: { baz: 2 } };
2.js
// #flow
import foo from './1.js'
foo.bar.baz;
// 0% code coverage
In order to fix this, you need to give Flow information about the types.
You can do a couple of different things
Make a.js covered by Flow.
Add a a.js.flow file that declare's the types
If it's a third-party library add a flow-typed/a.js file that adds declarations.
But be sure to check flow-typed to see if a definition file already exists. (And contribute back!)
Hopefully this is helpful enough to give you at least a starting point
I'm new to Flow as well, but heres my take:
If you have two classes, A and B, and flow typechecking is not enabled on A, then B functions that call into it will be "uncovered".
// a.js
class A {
}
// b.js
/* #flow */
import A from './A'
class B {
buildA():void {
new A() // I'm un-covered by Flow!
}
}
Flow doesn't know anything about the structure of A, and so therefore can't provide any guarantees.

page transitions in meteor - not quite working?

so in the back of the 'discover meteor' book they explain how to do page transitions. i've got it working, however it causes problems with the loading of javascript functions and variables on other pages that its animating into. it seems they're not ready or simply don't exist at the time the page is routed.
Template.layout.onRendered(function() {
this.find('.pos-rel')._uihooks = {
insertElement: function(node, next) {
$(node).hide().insertBefore(next)
.delay(200)
.velocity("transition.slideUpIn", 1000)
},
removeElement: function(node) {
$(node).velocity({
opacity: 0,
},
{
duration: 100,
complete: function() {
$(this).remove();
}
});
}
}
});
if i remove the above code then all my javascript variables and functions work correctly. does anyone have another working solution to page transitions using velocity.js ? i did find this one but its a year old and i couldn't get it to work at all, it just makes the content where '{> yield}' is go blank :(
Just a note for asking questions on stack overflow: "causes problems with the loading of javascript functions and variables" is pretty vague. Its best to give more specifics.
But anyways, you said here that you're using isotope to render items in a grid. I'm assuming you're calling $elements.isotope() within a Template[name].onRendered callback.
This is probably the issue because its trying to compute and rearrange into a grid the elements while they're hidden. Using display: none actually removed the elements, thus isotope can't compute the sizes, etc. for the layout. Try this:
insertElement: function(node, next) {
$(node).css("opacity", 0).insertBefore(next)
.delay(200)
.velocity("transition.slideUpIn", {duration:1000, display:null})
},
opacity: 0 should do what you're looking for. It will make them transparent without removing them from the transition.slideUpIn should animate opacity so you're good there.
Also, velocity transitions mess with the display property. Setting display: null in the animation options prevents it from setting the display to block or whatever it wants to do. This may or may not be necessary, but I pretty much always use it.
You could use:
onAfterAction
onBeforeAction
. The solution should be something like this:
animateContentOut = function() {
$('#content').css('display', 'none');
this.next();
}
fadeContentIn = function() {
$('#content').velocity('transition.fadeIn',1000);
}
Router.onBeforeAction(animateContentOut)
Router.onAfterAction(fadeContentIn)

How to override mixins in LESS CSS 1.4+

I've been using what I thought was a very elegant pattern for defining the styles of reusable components/widgets, using LESS. It works beautifully in LESS 1.3-, but after upgrading recently, my whole library is broken. Does anyone know a way to accomplish something like this in 1.4+?
Here's a very simple example of a component:
#componentName {
.loadMixins(){
.text() {}
.header() {}
}
.apply(){
> h3 {
// markup-specific styles
padding: 3px;
margin-bottom: 0;
// custom styles
.header();
}
> div.body, > div.popup p {
color: red;
// custom styles
.text()
}
}
}
And here's how it would be used:
.coolWidget {
#componentName.loadMixins();
// override mixins here
.text(){
color: green;
}
#componentName.apply();
}
This keeps all the markup-dependent styles abstracted from the user. I could completely change my markup and the user's styles would still work. According to the less.js changelog, 1.4.0 Beta 1 has a line "variables in mixins no longer 'leak' into their calling scope"
Is there any way around this?
Strictly speaking nested variables and mixins are still expanded into calling scope unless this scope already has those names defined.
Your example above results in a error:
SyntaxError: .header is undefined...
and it's expected as no .header() is actually defined within the .coolWidget (or anywhere else).
This can be fixed by providing "default" definitions for .text and .header somewhere inside #componentName.
For example if you modify .loadMixins() to:
.loadMixins() {
.text();
.header();
// default properties in case a caller does not provide its own:
.text() {}
.header() {}
}
then the example compiles OK and all text/header properties are overridden as expected.
I can imagine how your library may become broken because of new scope rules but this particular example you gave above does not illustrate the problem.

Resources