Component-Store. Sharing Store with children components - ngrx

Im trying to add this new library to a project, but Im having doubts regarding how to share the Page Component Store with its smart components children. I know that I could use Inputs and Outputs but I think that this approach were like tradicional ngrx used.
I think that I could use the component store with 'provide in root'. would this approach correct?

There are a couple of ways to do it. You could create a facade service that references it in its providers array, but in my opinion this is not ideal as it kind of defeats the purpose of having a simple single file component-store to manage your component's state.
I found that I had to add the providedIn: 'root' setting to my decorator in order to share state between a parent and children components. I know that this is not ideal, but if you simply add it to each component's providers, I found out the hard way, that you will find you are accessing different instantiations of the component store and when you navigate to a new child component (even if the parent is in the view still), that the state will not be maintained because it will be a new state container. That's why I like using providedIn: 'root'.

I had the same issue today...
If you provide the WhateverStoreService in the root, the default behaviour is that one instance of the service is shared in the whole application. (It's some kind of global state again)
If you want a separate instance of a dependency to be shared across each instance of a component and its children, configure it on the component’s providers property.
#Component({
selector: 'parent-view',
templateUrl: './parent-view.component.html',
styleUrls: ['./parent-view.component.scss'],
providers: [WhateverStoreService]
})
Now you can inject the WhateverStoreService in the constructors of your parent and child components.

It is all about the dependency providers and where you inject the instances.
You may use the root - then you will end up with a single instance of your ComponentStore, available for the entire app. It is a good way for global data, like the number of items in the cart, for some e-commerce app.
Then the code can look as follows:
#Injectable({
providedIn: 'root'
})
export class CartStore extends ComponentStore<CartState> {
You may provide ComponentStore in the parent component, this will make it available for the child components of that parent. By that I mean all child and parent components will share the same instance of the ComponentStore. For that you can use the following code:
#Component({
selector: 'app-parent',
providers: [ParentStore],
})
export class ParentComponent { ... }

Related

How to create a layout.js file in Next.js 13 if I use React Context providers?

In Nextjs 13 there is a reference to creating a layout.js file that replaces the app and document files, but they state that If you are using any React Context providers, they will need to be moved to a Client Component.
What does that mean exactly? The files should not be upgraded or you should mark 'use client' in the layout.js file?
next-13 app directory rendered on the server by default. Context api is used on client side. You do not have to use app directory. you can still use pages directory and wrap the app with Context Provider. But this approach is very expensive and cause to many unnecessary rerenders when the context value changes.
Looks like in next-13, will layout.js file, we are going to break the application state into smaller chunks.
Layouts: Easily share UI between routes while preserving state and
avoiding expensive re-renders.
Imagine you have blogs and 'dashboarddirectories insideappdirectory. In eachlayout.js`, you can fetch data, create some state and pass them to its own children via the prop drilling. In that way, we will kinda have a modular state. new layout system will avoid unnecessary re-renders.
Imagine your app was wrapped by ContextProvider and [slug] component was causing a state change in the ContextProvider, so your entire app will be rerendering. but if you keep the top level data for [slug] component inside blog layout, state change in layout will only rerender this layput's children components
If you want to opt out server component and choose to use use client directive, you can use createContext and this time each tree will have their own context

Avoid conflicts when using ThemeProvider [duplicate]

Wondering if anyone can lay out how one avoids a situation wherein a React application that uses material-ui classNames from makeStyles, and which includes a package that ALSO does that, hence two name generators, results in multiple, conflicting .jss1, .jss2, etc. in the production rendered page that includes both the app's components as well as the included package's.
I have control of both, so I can modify either to have a prefix, but I'm not at all clear on where one injects that into a v4 material-ui project.
Now, I know that createGenerateClassName allows one to pass in a prefix, but it's not clear to me how one injects that into the ThemeProvider. I wrapped the whole thing in a StylesProvider and passed the resulting generateClassName to that, but that seemed to be ignored.
Well, allow me to answer my own question for future generations. Turns out that you can indeed wrap the ThemeProvider with a StylesProvider with a generateClassName argument:
const generateClassName = createGenerateClassName({
productionPrefix: 'coreJss'
});
...
<StylesProvider generateClassName={generateClassName}>
<ThemeProvider theme={MyTheme}>
Stuff
</ThemeProvider>
</StylesProvider>
Now, your production classNames will be coreJss1, coreJss2... for that package and jss1, jss2...
Now, you might ask 'why didn't yours work in the first place?' and I would tell you to pay close attention to what you actually committed vs. what you typed and didn't save.

Dynamically changing the style of an Angular component using different CSS style sheets

I am trying to get a dynamic style-sheet change for a singlepage-application to work in Angular. The idea is to create different themes and let the users choose from them in a dedicated menu. There may be many themes, so nevermind that the example below only has two variants. The functionality is ready. As of now, I have collected all styles in single large scss-files, one for each theme. (Note that I am well aware that in Angular you often split the styles in many sub-scss files, for each component. But for this idea to work, I wanted to have it all in single files for each theme). I have the menu working, and use localstorage for the app to remember which theme has been chosen, since the site has to be reloaded for the changes to take effect.
Now for the problem:
In app.component.ts, the style sheet is defined, in standard form (i.e. styleUrls: [filename]). When using a static file path/name there, and when using a variable, defined just outside the #component-code, the styles works perfectly. But for the dynamic theme change, I have a simple fetch of the variable from localstorage:
var settingsString = localStorage.getItem('usergraphicsdata');
if (isDefined(settingsString)) {
let myUserSettings = JSON.parse(settingsString);
const themename = myUserSettings.theme;
It all works there too. Different console.logs confirms it understands everything it should. But then comes the problem.
if(themename == "theme1"){
var stylePath = "./app.component_theme1.scss";
var graphicFolder = '/assets/theme1/';
} else if(themename == "theme2"){
var stylePath = "./app.component_theme2.scss";
var graphicFolder = '/assets/theme2/';
}
}
Then comes the #component with its styleUrls: [stylePath]
For some reason the if-conditions are not regarded, and the result is always that the theme declared first (in the above case "theme1") will be active. So if I just change the order in the condition, and put the code for theme 2 first, that will be chosen for the site, disregarding which theme variable is actually fetched from localstorage
I have tried several other variants as well, but this one seems to be closest to a solution. Could it be an issue with Angular limiting style changes in runtime contra build? Or have I forgotten something basic in the if-condition?
Best regards and sorry for my limited english.
Per
why don't you solve it with routing? Duplicate the component for each theme, just with a different css-file but use for all those components the same html-file and put in the menu a link to that component (with a specific theme). The added value would be that the theme name also appears in the url and you can easily apply different logic without things getting to be convoluted (different typescript-files).
Something like this:
#Component({
selector: 'sunflower',
templateUrl: './detail.component.html',
styleUrls: ['./sunflower.component.scss']
})
export class SunflowerComponent {
#Component({
selector: 'roses',
templateUrl: './detail.component.html',
styleUrls: ['./roses.component.scss']
})
export class RosesComponent {
If you tried it, please share the problems you've encountered.
update
I made an extremely basic demo (that I've tested), you can find it here:
repo
Take care & good luck.

Conditional stylesheet in Nuxt SSR depending on a store value

I've been surfing StackOverflow and the Nuxt documentation, but I cannot get my head around this one.
So, I need to do RTL. My project is in Nuxt and we use SCSS. I'm setting the htmlAttrs to dir="rtl" conditionally depending on a store getter that tells me if the language is RTL or not. The requirement for this specific task is that a RTL stylesheet should be imported conditionally from the server side also if the country is RTL, so that it overrides the main.scss file.
Now, in nuxtServerInit(), I cannot set the stylesheet in the head, because the route will not direct me to the file, and, most importantly, Webpack won't compile it, as it's outside the regular flow of the application and not imported by main.scss, which is the stylesheet the Nuxt config is pointing to, and which contains all other styles. I realize that I could use a class and use it conditionally in components, but that is not the requirement. The nuxt.config.js file, in the relevant part, looks like this:
css: [
'#/assets/styles/main.scss'
]
There I obviously don't have access to the store.
What I said I tried was this:
if (isRTL) {
service.addEntryToArray('link', {
rel: 'stylesheet',
type: 'text/css',
href: '../assets/styles/main.rtl.scss'
});
}
(We use a service to add things to the head)
I understand that was naive on my part, because Webpack has no say there, so there is no transpilation, and there is no actual asset, which means the route is just wrong, and even if it were right, the browser would not be able to read it.
Any ideas on how this could be achieved at a non-component level? This needs to be part of the Webpack flow, has to be added server-side, and needs to be an import --I cannot just import it regularly and use just a class (again, I'm not stubborn, I'm working on a project, and this is a requirement by the client).
If you need more information, I'll update the post, but I don't know what else to provide.

Web components and shared styles

This is one of those "what should we do about this"-questions. As you know, web components are supposed to be small, contained applications for websites. However, sometimes these needs to be styled depending on the site they're embedded on.
Example: "Sign up to our newsletter"-component. This component would have a few key items:
An input box
A button
Maybe recaptcha
A method that talks to your service once the button is pressed (passing in the email)
We're going to use Google and YouTube as examples. Google's color scheme is blue (let's imagine that) and YouTube's color scheme is red. The component would then be something like <newsletter-signup></newsletter-signup> on the page you're embedding it in. Both Google and YouTube have this.
The problem comes in, when the component needs to inherit the styles from Google and YouTube. A few deprecated CSS selectors would be great for this, because Google and YouTube's style sheets could simply enable colors for the Shadow DOM, so we wouldn't have to copy/paste the styles. The component should theoretically not know anything about the styles from the host, because we want it to inherit from the host (Google and YouTube).
At the moment, I'm creating a web component using Angular 6, which has a lot of styles, because it has a lot of elements. I'm copy/pasting styles, Bootstrap, icons, and so on from the host site, then styling them based on <newsletter-signup brand="google"></newsletter-signup>. So if the brand is Google, the colors should be red, for example.
This is really bad, because of a few reasons:
Styles have to be updated on both the web component and on the host
Duplicated code is never a good idea
If all the styles are copied 1:1, the amount of bytes required for styles is doubled
How would I, as a developer, take this into account? How do I make styles on the host, then apply them on my web component (call it inheritance)? I'm sure someone has had the exact same problem with Shadow DOM as I am experiencing. Thanks for reading.
I realize you do not want to write same kind of rules for your common component(selector)
i.e. you want to do styling as where your common selector is placed.
Things you can do to handle this:
1. Create your own logical css framework
Write most commonly used CSS rules in global css.For example if you have integrated bootstrap and you want to override bootstrap, you will write most common overrides in app.css which overrides bootstrap.
"styles": [
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/styles/app.scss"
],
This app.scss should be written in way to which you can override.
Send Rules as input
send custom rules Obj and use in elements you want to override.
<newsletter [input]="customRulesObj"></newsletter>
component.ts
customRulesObj = new CustomRulesClass();
customRulesObj.color = 'red';
You can send rules in input in various component by creating a common class
as you know where you are embedding this component.
Extend this component from a common component
If you are too concerned for css you can extend your component from a common component which provides you with css logic as per need.
export class NewsLetterComponent extends CSSComponent implements OnInit
{
}
css-component.ts
In this component can logically define css as per host, current routerlink and
other multiple if else condition.
You can define rules by switch case conditions and bind those rules to component you have extended.
One of the biggest must-do's of web components is: My host (page where I'm embedding my web component) should not depend on the web component nor know about the web component.
What this basically means: Styles of my web component should not be shared with the host.
If my host decides to update the styles, it should affect my web component. Not the other way around. To solve this, I imported the external styles from my host directly inside the CSS file using #import. Here's an example:
import url("https://my-host.com/styles/core.css");
my-component {
//all styles goes here
}
I did this using SASS but can be done using regular CSS.
This is not a great solution at all, but it does what I want: Inherit the styles from the host. Although I would have to import every stylesheet there is, it still works.
A downside to my solution: When I load the page, it will send a request to the style from the <link> element inside the <head>-tag my host, but also to the style inside my import. So the styles are loaded twice. For our application, which is internal use only, it doesn't matter if we request additional ~200 KB data.
This question is a few years old and the situation has changed. The way to share styles with web components is now to use link tags to a shared stylesheet.
Inside each component:
<link rel="stylesheet" href="https://my-host.com/styles/core.css">
Reference:
https://github.com/WICG/webcomponents/issues/628

Resources