![Cover Image for AngularDart as a Development Environment for Small Web Apps](https://res.cloudinary.com/purucloud/image/upload/c_limit,w_3840/f_auto/q_auto/v1/blog/misc/components.webp?_a=BAVAZGBz0)
AngularDart as a Development Environment for Small Web Apps
Table of Contents
When you want to create a small web app, which framework do you use?
Of course, since the requirements for "small" can vary widely, it's not possible to say definitively, but I often used create-react-app or Next.js. However, for a small app created on a whim, you want to develop quickly.
Personally, having TypeScript and Material Design components makes development much easier, so I definitely want to use them. TypeScript can be solved by using react-scripts-ts, but for Material Design libraries, you need to choose and configure them yourself. While doing this, the thought of creating your own template and uploading it to GitHub starts to grow. A mistake of youth. A few months later, security warnings appear on Github, and when you upgrade the packages, they conflict and break... this is a summer tradition.
So, tired of all this, it was just the right time to try a different framework. There are (also) several alternatives, but when you think of "typed and Material Design", it's natural for AngularDart to come to mind. Thanks to Flutter, Dart also seems likely to gain momentum in the future.
Therefore, in the following, I'll talk about creating a web app that generates the card below using AngularDart, and discuss the advantages and disadvantages I experienced in the process. I've published the source, so it might be easier to understand if you read while referring to it.
Advantages of AngularDart
As mentioned in the introduction, one of AngularDart's strengths is that it "comes with many features to support development by default (+ officially)". Particularly, being able to use types naturally is a big advantage for me personally. Personally!
Personally, it felt like I was working with a js project that already had the following features:
- TypeScript
- prettier
- Material Design
- SCSS
Code formatting is built-in by default (like Go), so there's no need for prettier-equivalent settings. Material Design and SCSS are supported by external libraries, but you only need to add one line. You don't have to exhaust yourself in the early stages of development.
I haven't delved too deeply into Angular itself, so I won't write much comparison. Perhaps because I have experience with React and Vue.js, I didn't feel any particular discomfort. I haven't created state management or large-scale components this time, but I'd like to investigate if I have the opportunity.
Disadvantages of AngularDart
Everything has its ups and downs, risks have rewards, and advantages have disadvantages.
The main reason AngularDart is shunned is probably because "it's not js". While it has features built-in by default because it's separated from js, moving away from the existing huge ecosystem means a drastic reduction in followers and a great deal of effort. Moreover, at present, the community is (compared to js) small, and the disadvantages of this cannot be ignored.
Here's a summary of the issues I felt during this development:
- Some famous libraries can't be used
- Deployment services etc. don't support it
- It's difficult to find solutions through search
- (Currently) webpack's help is needed to achieve hot-replace
This project was very small, so it wasn't much affected, but in larger-scale projects with no increase in personnel, these problems can often be fatal.
Production Log or RTA
Now, from here on, I'll write about the production of a web app with a rather limited use: extracting Open Graph data and outputting static HTML cards. Through this, I explored how easily small apps can be created. I didn't time it, but rather than development, it's more appropriate to think of it as an RTA.
At the start, I had zero knowledge of Angular, and had only touched Dart a bit with Flutter. I've written React and Vue.js quite a bit, so let's believe that experience will come in handy.
Preparation
First, decide on a rough specification. This is important.
I decided on a rough requirement like "The main function is to enter a URL and press a button to extract meta tags from external sites and display HTML. Also, I want a preview of that HTML. The components should be Material Design".
The setup is described in the guide, so follow this.
Next, casually launch WebStorm and check if it's the latest version. This is important. If it's the latest version, as a warm-up, run the official sample.
The main file is only 9 lines long and looks great on Instagram.
main file
The procedure is just to run git clone
, pub get
, webdev serve
and Hello Angular!
will appear.
Read the Official Guide
Read the kindly prepared learning guide... but this time it's an RTA, so extract the basic elements from the guide and read only those. Let's skip the Architecture that they've kindly prepared for this challenge. We'll read it later. Also, we'll skip Displaying Data.
Placing Inputs
Around the 4th page of the guide is a description about inputs. This is relevant to the current app, so let's read and create while going through it.
First, let's create a simple input function using the HTML input tag. Using keyup.enter
etc., you can create a basic input flow even without buttons.
HTTP Communication and CORS
Let's quickly create the part that retrieves data from external sources, which is the main function.
The most troublesome part of the implementation in this app is "How do you get HTML from external sites?". Since the app runs only in the client's browser, it will always hit CORS. This time, we'll use Rob--W/cors-anywhere, a publicly available CORS avoidance server. When this API server goes down, that's the end of this app's life. Let's refrain from making old-fashioned statements like "There used to be something called YQL...".
GET processing can be written very straightforwardly. Of course, you can also write it using .then
. The sense of security when writing the Future
type is unparalleled.
import 'package:http/http.dart' as http;
Future<String> requestCrossDomain(String url) async {
final corsGateway = 'https://cors-anywhere.herokuapp.com/' + url;
final jsonString = await http.get(corsGateway);
final body = jsonString.body;
...
Component Separation
Now, the remaining function is the preview. This is independent, so let's try component separation.
However, it's just a matter of reading about file separation and writing it the same way as the main component. Generally, it seems to be placed under src/
. Also, what's equivalent to props in React is completed just by declaring with @Input
attached.
Also, it's interesting that the target of @Input
can be a setter.
()
set setHtml(String html) {
final _htmlValidator = PermissiveNodeValidator();
querySelector('#html-preview')
?.setInnerHtml(html, validator: _htmlValidator);
}
Calling side
<preview [setHtml]="result" [width]="previewWidth" [height]="previewHeight"></preview>
By doing this, setHtml
is called every time the result
variable in the calling component changes. Convenient.
setInnerHTML and sanitizer
This is quite a tricky part, and you'll definitely get stuck if you don't know it. Or rather, it's a function that's not necessary in most web apps, so it feels futile to write it.
In Angular, security-sensitive functions like setInnerHTML are well-managed by policy. By default, it's set to reject, and the content you try to set is deleted. In this app, it's not a problem to allow everything, so as done in the dart-pad repository, we create a PermissiveValidator and allow it.
class PermissiveNodeValidator implements NodeValidator {
bool allowsElement(Element element) => true;
bool allowsAttribute(Element element, String attributeName, String value) {
return true;
}
}
Usage example
final _htmlValidator = PermissiveNodeValidator();
querySelector('#html-preview')
?.setInnerHtml(html, validator: _htmlValidator);
CSS Sanitizer
This time, there's an even more niche process of changing the CSS of the preview component according to user input. This also hits the security policy, so we write a sanitizer to bypass it.
There were few examples of how to use Pipe, so I ported an example from Angular to Dart.
As shown below, it can be used exactly like a pipe. In the code below, the css in the template is passed through as harmless as is.
('safe')
class Safe extends PipeTransform {
DomSanitizationService sanitizer;
Safe(this.sanitizer);
transform(style) {
return this.sanitizer.bypassSecurityTrustStyle(style);
}
}
(
pipes: [Safe],
template: '''
[style]="'width: ' + width + 'px; height: ' + height + 'px; border: 1px dashed black;' | safe"
>
''',
To Material Design
Now, up to this point, the functionality is complete. All that's left is to tweak the appearance. An official package is prepared, so introduction is easy.
The basic usage of angular_components is also in the official guide. For example, for buttons, you just need to add to the directive and rewrite the tag.
directives: [PreviewComponent, MaterialButtonComponent]
<material-button raised (trigger)="onEnter(box.value)">submit</material-button>
Also, to learn how to use material-input, refer to the package documentation. There's also an example that you'll probably rely on a lot during development.
The HTML input tag becomes a material-input
tag as follows:
<material-input
floatingLabel
type="number"
checkPositive
[(ngModel)]="previewWidth"
leadingText="width: "
trailingText="px"
[rightAlign]="true"
></material-input>
A somewhat easy-to-forget point is that you need to add materialInputDirectives
to directives. If you forget this, ngModel won't work and you'll end up struggling.
Also, the right-hand side of ngModelChange must be included in the class.
Bad
(ngModelChange)="previewWidth=int.parse(\$event)">
Good
(ngModelChange)="previewWidth=intParser(\$event)">
...
int intParser(String str) => int.parse(str);
To include a submit button, you need to create a form.
The common process of "disabling the submit button until validation passes" is completed just by specifying ngControl
within the form.
Extracting HTML
As the template has grown, let's extract it to an html file. If you're not using symbols like $ in particular, you can just extract it to a separate file and specify it.
templateUrl: 'app_component.html',
In WebStorm, when you extract, formatting becomes effective, saving you the trouble of formatting.
SCSS
Writing in css can often be painful, so you want to use SCSS even for small apps. Such a need is completed just by following the sass_builder package Readme. Note that what you load is .css
, not .scss
.
In the old days, it seems you needed to write a transformer, but now it's as easy as just adding a package.
Finally
Don't forget to update the description etc. in pubspec.yaml
, add a favicon, tweak the head tag, etc.
Deployment
Where to deploy depends on the situation, but for static apps like this, Netlify is easy. I wrote about it in a separate article, so if you prepare a script file according to this, it's complete.
Impressions
I was able to proceed very smoothly. The learning cost wasn't as high as I thought, and if you've used other FWs to some extent, you can develop without getting stuck. This time, there was little dependence on libraries, so the disadvantages of not being js hardly materialized, and I was able to enjoy many advantages.
Summary
I felt that if the requirements meet the conditions of "sufficiently small app", "not using many external libraries", and "somewhat concerned about appearance", it can be developed more quickly than using React projects like create-react-app or next.js. Dart is expected to develop further in the future, and I'd like to use it actively.
On the other hand, when creating larger-scale apps, different perspectives are needed, and disadvantages may become non-negligible. This is beyond the scope of this creation, so I'll experience it if the opportunity arises someday.