u/MichaelSmallDev
MichaelSmallDev
MichaelSmallDev
Projects I work on have various loose SQL query files checked in, so something like Kysely would require rehashing them into its own format. That's as far as I understand, I have only looked into a bit here or there when this kind of thing comes up. The special format was my main hangup when I considered proposing bringing in something like Kysely in a previous project. But if I can basically use this new Reylo lib on our checked-in old fashioned queries plus just a couple lines for the DB driver, this may be way easier to onboard into a project. Even if it is just spot checking some types on occasion. I still don't see myself using some tool like these too often, so that is more cognitive overhead saved on the occasions I may reach for these.
MichaelSmallDev
I overlooked the content mappers in the TS 7.1 blog. Very cool, thanks for pointing that out. Link for those who are curious as well https://devblogs.microsoft.com/typescript/announcing-typescript-7-1-beta/#content-mappers
MichaelSmallDev
https://material.angular.dev/cdk/categories
https://angular.dev/guide/aria/overview
Angular Aria is the officially supported headless. And the CDK has a variety of helpful stuff, with the harness API for testing alone being a great tool for building your own components https://angular.dev/guide/testing/component-harnesses-overview
MichaelSmallDev
I know I mentioned in a more recent thread about the official approach using Material's new `MatFormFieldControl` API, but that was a bit lower level than I wanted. I found an approach and made an example that works for both signal forms and reactive forms consumers: https://github.com/michael-small/signal-forms-experimenting/pull/3/changes
edit: here is the code again, but it was bad practices for a few reasons lol https://github.com/michael-small/signal-forms-experimenting/commit/5151195c7ded0ab1e3a624bf5eabb05a8ab82506
MichaelSmallDev
Thanks for the Stackblitz to try this out. Nice to have this hands on and try stuff.
MichaelSmallDev
This is my biggest want with resources, but this is the solution I use: https://www.reddit.com/r/angular/comments/1wcw1kl/comment/p97pkki/. You do not need to pull in a library, but just drop in the folder for extensions. I have been doing this copy and paste of the extensions into projects which I do not pull in libraries to, and it works good. It does add boilerplate, but with the application configuration option, IMO this is as good as you will get while retaining full ResourceRef behavior like reload. The one caveat to mention now is that there is an outstanding fix for an edgecase with withPreviousValueOnLoading and withValueOnLoading that is currently approved, so if you like this solution, either wait on this to be merged or pull in that person's branch.
MichaelSmallDev
Considering that the Angular team is leaving query param and route params signals beyond route input binding to libraries, I would recommend the global params from injectParam or the injectQueryParams from ngxtension. Both are usable anywhere in the route tree, and even in root services. No need to have to have a component in the tree to sync the values to.
import { injectParams } from 'ngxtension/inject-params';
import { injectQueryParams } from 'ngxtension/inject-query-params';
@Service()
export class UserService {
idParam: Signal<string | null> = injectParams.global('id');
queryParam: Signal<string | null> = injectQueryParams('test');
user = httpResource(
() => `https://jsonplaceholder.typicode.com/users/${this.idParam()}`
);
}
Example project Stackblitz. Adapted from something else more in depth, kind of a mess, but that snippet is the key.
There is even linkedQueryParam, so you could link anything like a form or any function call and it reads and writes to query params. That can even go in a root service as well.
MichaelSmallDev
Update since my other comment:
fix(material/form-field): support signal forms in MatFormFieldControl
Updates MatFormFieldControl to allow an ngField property for those directives that support signal forms. It also makes the following changes:
docs: redo custom form field guide for signal forms
Rewrites the custom form field guide and related example to be based on signal forms.
https://next.material.angular.dev/guide/creating-a-custom-form-field-control
MichaelSmallDev
Really minimalist, makes me think.
MichaelSmallDev
There's a few aspects to this question I have thoughts about, so I'll break it down in no particular order
Are you wanting a signal forms version of the "Custom form field control" page where you can register something to be in a mat-form-field? I haven't made my own custom MatFormFieldControl for reactive forms, but I have been curious about doing so with signal forms. I don't know about how adaptable MatFormFieldControl itself is to signal forms, but at least a FormValueControl page for signal forms + Material is something I would hope someday is documented.
I am more accustomed to just making custom components that pass in the FormControl/FormGroup as inputs. And I agree that it is questionable if passing in the FieldTree is optimal. Though I love the greater signal forms docs and how fleshed out they are, this composition with signal forms in children components is definitely something I think is needed. Beyond just the FormValueControl page which is fairly detailed. That is good for if you have an internal or open source library, but for more coupled children forms in an app, there is a few more ways I consider more natural alternatives. I have experimented with a few approaches for child components for subsets of a form, and passing a FieldTree was just one of them. Here is an overall MD breakdown of the approaches, and here is the code for each. I break it down as 4 options: the new CVA for signal forms (FormValueControl), passing a model input and the child uses it as its writable signal input to its own form it houses, passing a FieldTree, and a form service you can inject.
I came to the conclusion that I probably would still just use the FieldTree input for my own reusable forms like in a project's library, Material or not, but form services otherwise for more tightly coupled forms in a given application. I found form services good for reactive forms as well, and just as nice for signal forms. Especially with the more opinionated and structured way of handling the form model vs domain model transformations, and the seperations of the writable signal model and the form object itself. But in the case of a custom component that is more re-usable, I would just do the tree input. But a case could be made for the model + children own forms, and a FormValueControl probably for a real formal reusable component that you may hash out in a library.
One last note on FieldTree as an input: I always just passed reactive forms as inputs, but that had so many subtle issues. Although passing a FieldTree still seems a bit strange on the surface, it hasn't had any of those issues I had with reactive form inputs. Having to double invoke those inputs to drill into the form is a bit strange, but @let helps that in the template at least.
As for the overall state of Material's docs on signal forms, it is catching up. All pages now mention FormField on the main page, and a few of them have more involved examples like custom ErrorStateMatcher configuration including signal forms (which I think is lowkey the best part about Material's form API). Some of them do have signal form examples in the dedicated "Examples", for example "Input with error messages (signal form)" or "Stepper using signal forms" or "Button-toggles with forms" (which includes all 3 types). I have been chipping away at adding some examples, and I hope to get at least one example in the "Examples" page for each as I have the time. I do have an example of each Material form component with signal forms in the same project as the form breakdown project, and am slowly trickling those into the docs as I have time. The names are based on existing reactive/template forms examples from the docs, but each are actually signal forms. A few of these have landed in the docs so far.
MichaelSmallDev
In a case like this, I like what johalternate said elsewhere in the thread
MichaelSmallDev
In what scenarios are books retrieved? I would assume that the selectedBookId would just be used to find that singular book by its ID from the loaded books. And if all are not loaded in, that the individual book page could trigger that.
MichaelSmallDev
That also seems pretty handy for future work :). I like the simplicity of it.
Same. If you want to experiment with it hands on, I have been working on an example to share on this sub soon: https://stackblitz.com/edit/stackblitz-starters-hxzqzhmk?file=src%2Fcomponents.ts
MichaelSmallDev
and rerouting when the selectedBook does not exist doesnt sound like something a resolver should do
Your other reasons for being wary of the selected book store + resolvers aside, I don't think that it would be wrong for a resolver to handle this in practice.
I don't do much with resolvers myself, but I wanted to learn a bit to help out, and it seems like redirecting in resolvers is intended for error scenarios: https://angular.dev/guide/routing/data-resolvers#handling-errors-directly-in-the-resolver. I would think that cases like yours encompass that.
And while you are using traditional resolvers now, this is a pattern with resolver type solutions in the upcoming router resource API as well. "If a blocking resource needs to redirect the user (for example, if an item is not found), throw a RedirectCommand inside the resource loader. The router will cancel the current navigation and redirect to the specified URL". I would think the continuation of this practice gives even the existing resolver API more credence to this usecase.
MichaelSmallDev
I know others who like the ControlContainer, but to be honest, I haven't put much stock into an alternative due to my own biases against it. In my opinion, the lack of typing to that API and the lack of docs made it hard for me to get into it. I have not seen the issues about ControlContainer alternatives, but I understand the demand for a signal forms equivalent. And perhaps it at least would be possible to hack one out if they don't make it official? I would think it would be an additive part of the API, not too risky to potential break other parts.
But I agree that passing a FieldTree as an input feel strange. The need for an @let for handling the form to not need to always do double signal invocation in the template is a bit weird, and probably a bit cumbersome in the class logic. I personally have been doing the service method because I found that to be optimal for tightly coupled form components with reactive forms, and I think it works great for signal forms, without the strangeness of passing a whole field tree.
MichaelSmallDev
Sorry, I forgot there was not more context/hands on examples of this in the video by the time I wrote that comment out.
https://ngrx.io/guide/signals/resource-extensions
4 extensions you wrap around resources
- withPreviousValueOnLoading
- withValueOnLoading
- withPreviousValueOnError
- withValueOnError
An example touched on in the video, in practice
import { signal } from '@angular/core';
import { httpResource } from '@angular/common/http';
import {
extendResource,
withPreviousValueOnLoading,
} from '@ngrx/signals/resource';
const page = signal(1);
const todosResource = extendResource(
httpResource<Todo[]>(() => `/api/todos?page=${page()}`),
withPreviousValueOnLoading()
);
// `value()` returns the previous page's
// data while the next page is loading,
// rather than `undefined`.
And they can be configured across the app
import { bootstrapApplication } from '@angular/platform-browser';
import {
provideResourceExtensions,
withValueOnError,
} from '@ngrx/signals/resource';
bootstrapApplication(App, {
providers: [
provideResourceExtensions(
withPreviousValueOnLoading()
)],
});
// allows the following
readonly todosResource = extendResource(
httpResource<Todo[]>(() => `/api/todos`)
// no need for explicit passing
// withPreviousValueOnLoading() anymore
);
This type of functionality is what I wanted snapshots to be like. Especially still having the ability to reload and whatnot.
I have been messing with these as a demo and just dropping them into my own projects since I couldn't pull down the v22 signal store package. That was really nice to have in some of my smaller projects where I didn't want to use the signal store. I feel like this functionality would be great to have in either Angular's core functionality, or at least something that doesn't come with a whole store package. I tend to pull in the signal store anyways, but I know it isn't everyone's thing.
MichaelSmallDev
Thanks for sharing this. Real cool looking app with a lot of work clearly put into it.
As a heads up to people reading, it appears the changes are still in an outstanding feature branch: https://github.com/Kareadita/Kavita/blob/feature/signals-and-reader/UI/Web/src/app/_single-module/edit-chapter-modal/edit-chapter-modal.component.ts
MichaelSmallDev
Over the evolution of the API, I worked through various examples mirroring various form problems I have used in production with reactive forms, and how I solve them with signal forms: https://github.com/michael-small/signal-forms-experimenting/tree/main/src/app/forms. None of these examples are interconnected like a real app, but they are studies of form interactions I have spent a lot of time working on over the years and will likely implement in greenfield projects once I update my work's project.
I have some basic GET calls from free APIs, and one full asnyc CRUD example from a mock API for TODOs. But even mocked data goes through resources with mock delays and errors. I have some submit/async validator examples as well. And throughout this exploration, I have contributed some of these lessons back into enhancing the documentation itself. If there are any scenarios you find in your exploration that you would think are interesting exercises, just let me know, as I am looking for new scenarios to practice in this project. One example that is not in the docs (yet) but is important is various approaches to splitting apart forms across various components. I have explored this topic in this folder: https://github.com/michael-small/signal-forms-experimenting/tree/main/src/app/large-form
edit: also, I gave a very brief overview of this project a few months ago on a stream. You may find this more approachable for an overview than pulling down a whole repo lol. Shout out to /u/johalternate for help on this.
MichaelSmallDev
Resource extensions are everything I wanted with snapshots and more. Especially the way to configure an app wide default. I wish it was just built into the framework. At least it is very easy to copy/paste the source into a project without needing to pull in dependencies.
MichaelSmallDev
I don't make my own directives that often. That said, if this PR lands, I may use them more: Draft feat(core): add support for styles in directives. There are some considerations in the attached issue, so even though no PR is a guarantee, I wouldn't hold my breath on this being guaranteed or soon.
But a util for directive testing is coming in 22.2 for sure https://github.com/angular/angular/pull/70453
edit: btw thanks for the article. One thing I would mention is that ngClass and ngStyle have been soft deprecated in favor of native [class] and [style] bindings. But there are migrations for ngClass --> class and ngStyle --> style.
MichaelSmallDev
PR for more context
https://github.com/angular/angular/pull/70188
The stated reason is related to isolatedDeclarations like the video mentions at the end. Though I am not very familiar with that one flag myself, judging by its release notes from TS 5.5, it seems very nice for different use cases https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations. And at a quick glance at some blogs I have seen talking about metrics, it sounds like it is great for monorepo build times.
I am used to used protected for template access to component state which is otherwise somewhat protected otherwise for class consumers, but I could buy getting used to private access in templates.
MichaelSmallDev
One example: recently someone on my team started using providedIn: SomeComponent. I thought that was kind of cool, compared to just Injectable(). You still need to add it to that component's providers, but I liked the explicit reference in the injectable. However, there was two issues. #1 was that that type of providing was considered deprecated, as it was more of a NgModules era usage. Secondly, it led to some circular references with some cases of providing services in some edge cases we ran into after using that pattern for not too long.
And for what it's worth, here is a doc on the tradeoffs of @Service vs @Injectable: https://angular.dev/guide/di/creating-and-using-services#using-the-service-vs-injectable-decorator
MichaelSmallDev
This has been nice for signal forms, and some things where I want some two way binding from a service. It's nice I can treat something like a normal writable signal but have the same service with a signal approach.
MichaelSmallDev
I didn't care for the new decorator at first, but it feels nice now that I am used to it. And the migration added recently like you highlighted helps too. Thanks for the video.
I just switched over from karma/jasmine to vitest and really liking it.
And I agree that the docs right now are great for testing. For OP being very data dependent, one doc that may seem missing at a glance from the testing sidebar is the HTTP testing section. But it is linked to from the end of the dedicated services testing doc: https://angular.dev/guide/testing/services#testing-http-services. But read that service testing doc first, since it is probably just as important and foundational for HTTP testing. Then go check out the whole article for HTTP testing that is housed under the HTTP subset of the docs https://angular.dev/guide/http/testing