<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[More Than Code]]></title><description><![CDATA[Blog by @knownasilya about life and web development.]]></description><link>https://ilyaradchenko.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 14 Sep 2026 08:40:22 GMT</lastBuildDate><atom:link href="https://ilyaradchenko.com" rel="self" type="application/rss+xml"/><pubDate>Wed, 13 Nov 2024 00:00:00 GMT</pubDate><item><title><![CDATA[Build on the URL]]></title><description><![CDATA[Feels like I keep coming back to the URL quite often as a topic that I write on, and there's a reason for that. Over the last 13 years of my professional software engineering career I've learned to lean on the URL when working with the web, and every time I've implemented a feature while leaning on it, I've been commended because of the unexpected "side effect features" that came out as a result. Here's a few reasons why you should build on the URL.

## Shareable

It's a little more work to build on the URL, but you'll be "using the platform" if you do. This allows users of your app to share snapshots of your app with other users. Features like this are used by all, but few apps actually provide good support for sharing. If you can go beyond the basics, and lean on the URL throughout your app it'll be a blessing to someone out there.

While usually we think of sharing as something done with other people, that isn't always the case, a user might want to share a URL with their future selves, via a bookmark. This allows for custom use-cases that you as the developer might not have thought of, but a bookmark solves a problem for a user because you provided access to changing that state externally.

## Debuggable

Ding! You glance at your phone, a new email has just arrived. Randy loves your MapMakers app, but today he ran into an issue. His email says that the map marker is not showing up in the right place for the latest listing he created. You ask him for more information, but you don't ask for the URL, because you know it isn't helpful.

Now what if you had a useful URL? You might be able to get into the same or very similar state that your user encountered the bug. It gives you more of a starting point to look from, and if your data loads based on the URL you have your next place you can look to confirm the correct data has loaded. Already you've eliminated a few possibilities, or maybe even found the bug. Now combine this with something like Sentry or DataDog, and you have a URL for all of the errors your users encounter but didn't send you an email.

## Scaffolding 

When you start thinking about a feature, start with the URLs you'll need, it'll help you flesh out the designs. You can even write tests for those URLs before you have the pages (doing some TDD) built, giving you a progress indicator of sorts. The URL is the smallest primitive you can use to think about a feature you're building, and it'll help you not miss critical pieces. 

Go out there, and build your first URL driven feature!
]]></description><link>https://ilyaradchenko.com/users/knownasilya/notes/cm37ckj6d0001lfpd8hwxwnq2</link><guid isPermaLink="false">users/knownasilya/notes/cm37ckj6d0001lfpd8hwxwnq2</guid><pubDate>Wed, 13 Nov 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[When Components Aren't Enough]]></title><description><![CDATA[Sometimes components are too high-level, and you need to get at a specific DOM element, either for `scrollTo` or `focus`, in these situations Ember provides a lower-level primitive called an element-modifier. To get started with element modifiers you can check out existing modifiers at [EmberObserver](https://emberobserver.com/categories/modifiers) or create your own using the `ember-modifier` [library](https://github.com/ember-modifier/ember-modifier).

## Some Examples

```handlebars
<form {{autofocus}}>
  <input />
</form>
```

Which will focus the first non-disabled input that it finds (from [ember-autofocus-modifier](https://github.com/qonto/ember-autofocus-modifier)).

```handlebars
<div {{scroll-to}}>
  Some content here
</div>
```

Which will scroll to the element once it's in the DOM, and is super easy to implement:

```js
// /app/modifiers/scroll-to.js
import { modifier } from "ember-modifier";

export default modifier((element) => {
  element.scrollIntoView({
    behavior: "smooth",
  });
});
```

There is also the `ref` modifier from [ember-ref-modifier](https://www.npmjs.com/package/ember-ref-modifier) which gives you access to the element:

```handlebars
<button {{ref this "button"}} data-name="foo">
  Click me baby, one more time!
</button>

{{this.button.dataset.name}} >> "foo"
```

Along with `{{on-click-outside}}` from [ember-click-outside](https://github.com/zeppelin/ember-click-outside).

```handlebars
<div {{on-click-outside @close}}>
  Your HTML...
</div>
```

## Why Are Element Modifiers Useful?

Mainly because you can do things like:

```handlebars
{{#if this.isEditorVisible}}
  <div
    class="wysiwig-editor"
    {{did-insert this.setupEditor}}
    {{will-destroy this.teardownEditor}}
  >
  </div>
{{/if}}
```

Which handles the setup and teardown of the element, so if `isEditorVisible` turns `false` the editor initialized on our element will be properly cleaned up. This is important because the new Glimmer Components allow multiple top-level elements without any kind of fragment syntax.

> Note: I'm using [ember-render-modifiers](https://github.com/emberjs/ember-render-modifiers) in the above example. But you could create a modifier to encapsulate both the setup and teardown so reusability is easier.]]></description><link>https://ilyaradchenko.com/users/knownasilya/notes/cltlv6sq2000vn3bbdhe2ygms</link><guid isPermaLink="false">users/knownasilya/notes/cltlv6sq2000vn3bbdhe2ygms</guid><pubDate>Sun, 10 Mar 2024 18:45:08 GMT</pubDate></item><item><title><![CDATA[Ember's Nested Routes and URLs Explored]]></title><description><![CDATA[Ember's router is powerful and a bit unique, in that it has a nested structure for the routes which build up the URL a user would see in the browser.
Given it's power, it is easy to be unsure about how to structure your routes, especially in scenarios where you have parent-child UI patterns or CRUD like
resources.

One of the patterns that I've learned is not using extra nesting when want to build out a multi-part URL structure, like `/users/1234`, which seems like it should be nested, but generally
a single level is enough. I'd structure it like this:

```js
this.route('user', { path: '/users/:userId' });
```

This puts your user "view" route at the `app/pods/user` directory (my examples will use PODS, since they are sane for routes).
The following is generally not the way I'd write my router map, even though it creates the same URL pattern.

```js
this.route('users', function () {
this.route('user', { path: ':userId' });
});
```

Unless you are showing the list of users at the same time as the selected user, which I think is a rare pattern, this is probably not the way your route should work.
The reason I wouldn't do this is because it adds unnecessary mental overhead between the list and a selected item from the list.
With my preferred pattern, if I wanted a list route, it would be a sibling to the resources.

```js
this.route('users');
this.route('user', { path: '/users/:userId' });
```

And the folder structure is sane as well:

```
app
pods
  users
  user
```

Since the two usually don't share any common UI, the common UI will probably be one route up or at the `application` route.
Well that's it! Let me know if this is how you do it or if you completely disagree.]]></description><link>https://ilyaradchenko.com/users/knownasilya/notes/cltlv5lep000tn3bbgg3zo0v9</link><guid isPermaLink="false">users/knownasilya/notes/cltlv5lep000tn3bbgg3zo0v9</guid><pubDate>Sun, 10 Mar 2024 18:44:12 GMT</pubDate></item><item><title><![CDATA[Sharing A Parent Model Across Route Boundaries]]></title><description><![CDATA[Just last week [Alex LaFroscia](https://mobile.twitter.com/alexlafroscia) released a new addon called [Ember Context](https://github.com/alexlafroscia/ember-context) and I wanted to share some patterns that I've used in the past that this addon changes. 

## The Problem

Sometimes you have a UI where at a certain level you have multiple nested routes and they deal with one parent item. For example if you had a
blog management system that supported multiple blogs, you could have a route like `dashboard.blog.post.edit` and the `Blog` model is found at the `dashboard.blog` route. So at this point you want to use that same blog model in the other routes. Historically there has never been a great way to do this, you might think put it on a service, but then you have to register and cleanup that service since services are global. Along with that inconvenience it's also not clear where the service gets its data, it seems a bit magic.

## The Past

In the past I've tackled this problem with the `modelFor` route method, something like:

```javascript
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class PostEditRoute extends Route {
  @service store;

  async model({ postId }) {
    let blog = this.modelFor('dashboard.blog');
    let post = await this.store.findRecord('post', postId)
    return {
      blog,
      post
    };
  }
}
```

Now I can access the blog in my template via `{{@model.blog}}`, and it all works. The issue here is that `modelFor` will return whatever data you returned in the `model` hook of the `dashboard.blog` route, and that could change to an object of items, like `{ config, blog }` and now you need to visit every `modelFor` and change the assumption there. In cases where a route doesn't have any other model data you need to fetch, you still have to add a route file and do this whole dance.

## The Present

With the new `ember-context` addon, we get to change this to something with less boilerplate, and a bit clearer about where the data is coming from.
With this addon we'd define the following in our `dashboard.blog` template:

```handlebars
<ContextProvider @key='blog' @value={{@model}}>
  {{outlet}}
</ContextProvider>
```

Now in our `dashboard.blog.post.edit` template we can use this blog model by using a helper: `{{consume-context 'blog'}}`:

```handlebars
{{#let (consume-context 'blog') as |blog|}}
  {{! work with blog here }}
{{/let}}
```

And if we wanted to access it in a controller or a component, it would be as simple as injecting the value:

```javascript
import Component from '@glimmer/component';
import { inject as context } from '@alexlafroscia/ember-context';

export default class PostEditor extends Component {
  @context('blog') blog;
}
```]]></description><link>https://ilyaradchenko.com/users/knownasilya/notes/cltlv39bn000nn3bb7stzbanm</link><guid isPermaLink="false">users/knownasilya/notes/cltlv39bn000nn3bb7stzbanm</guid><pubDate>Sun, 10 Mar 2024 18:42:23 GMT</pubDate></item><item><title><![CDATA[Data Down, Actions Up]]></title><description><![CDATA[I hear many people asking how they can compose components, and since the Ember
Guides [http://guides.emberjs.com/] don't help us in that respect, I wrote my
own guide as a PR [https://github.com/emberjs/guides/pull/66] to the guides. I
figured that I might as well get this out to the public while I wait on it
getting merged. This will allow us to improve the guide, so feel free to leave
comments/suggestions in the PR (inline, etc).

---

Components really shine when you use them to their full potential, which is when
you compose them.
Take for example the <ul> element, and the fact that only <li> elements are
appropriate as children.
If we want the same type of behavior, then we have to compose our components.

Just like we compose regular HTML elements, we can do the same with components.

```handlebars
{{#user-list users=model sortBy='name' as |user|}}
  {{user-card user=user}}
{{/user-list}}
```

Component Blocks
Components can be used in two forms, just like regular HTML elements.

Inline Form

```handlebars
{{user-list users=model}}
```

Block Form

```handlebars
{{#user-list users=model}}
  {{!-- custom template here --}}
{{/user-list}}
```

To compose components, we must use the block form, but we must also
be able to distinguish from within our component which form the user
is implementing. This can be done with the template property.

```handlebars
{{#if template}}
  {{yield}}
{{else}}
  <p>No Template Specified</p>
{{/if}}
```

We can check if template is truthy, and if it is that means that the user
specified a custom template.
Well, once we have a template, we probably want to use that in our component,
and that's exactly what {{yield}} does.

This helper can be used once, or many times. You can make your component into a
type of <ul> element,
that is a list that will repeat n times. Like the following example, where we
can output a custom summary.

```handlebars
{{#each posts as |post|}}
  <h3>{{post.title}}</h3>
  <p>{{yield}}</p>
{{/each}}
```

Which can be used like so:

```handlebars
{{#post-list posts=model}}
  Greatest post ever!
{{/post-list}}
```

And will result in the following HTML:

```html
<div id="ember123" class="ember-view">
  <h3>Tomster goes to town</h3>
  <p>Greatest post ever!</p>
  <h3>Tomster on vacation</h3>
  <p>Greatest post ever!</p>
</div>
```

But what use is it to just output the same thing over and over? Don't we want to
customize our posts,
and display the right content? Sure we do. Lets explore the {{yield}} helper a
bit.

Data Down
To accomplish composability beyond just simple templates, we need to pass
context to those templates. This can be done with the `{{yield}}` helper.

The `{{yield}}` defines where the template we defined inside our component block
will yield in the component's layout, as we saw in the previous section. Apart
from that, the yield helper also allows us to send data down, providing a
context for the templates.

```handlebars
{{yield}}
{{yield "hello"}}
{{yield item}}
{{yield this "bye"}}
```

By default yield does not send any context, but you can provide an arbitrary
number of arguments.
Once you are sending data down, the child components need to consume that data.
We can do this with the as operator. Let's take {{yield user "My Item"}} as an
example:

```handlebars
{{#user-list users=model as |user title|}}
  <h3>{{title}}</h3>
  {{user-card user=user}}
{{/user-list}}
```

Now `{{user-card}}` has access to the current user, which would change if
`{{user-list}}` placed it's yield helper inside an each block.
This opens up the possibility to use the `{{component}}` helper for different
templates, for example:

```handlebars
<h3>Profile</h3>
{{yield "user-avatar" user}}
{{yield "user-contact" user}}


{{#user-profile user=model as |section user|}}
  {{component section user=user}}
{{/user-profile}}
```

With the `{{component}}` helper, we can bind our context to names of components
dynamically, which in this case means that we can customize
the user profile with custom components bound to the relevant data. This means
we can have multiple extension points in our components, making them much more
versatile.

Actions Up
Now that we can send data down, we probably want to manipulate that data via
some user interaction,
like changing a user's avatar, or whatever it is you're doing. We can accomplish
this by using actions.

Actions are great, but for actions to work in the right context, we must use the
targetObject property to specify
where we want the action to go. Before we can specify the targetObject property
on our "acting" component, we need
to expose that target as the context.

```handlebars
{{yield this}}
```

The targetObject is the component that you want to handle the action, in this
case it's the parent component.

```handlebars
{{#user-profile user=model as |profile|}}
  {{user-avatar change="updateAvatar" targetObject=profile}}
{{/user-profile}}
```

Since profile is the instance of the {{user-profile}} component, that means it
can accept the "updateAvatar" action request.
The action must be defined on the user profile component instance.

Here's a mash-up of the possible scenarios with actions:

```handlebars
{{#full-post post=model as |fullPost|}}
  {{post-like like="like" targetObject=fullPost}}
  {{post-subscribe subscribe="subscribe" targetObject=fullPost}}
  {{comment-box submit="addComment" targetObject=post viewName="commentBox"}}

  <button type="button" {{action "fullScreen" target=commentBox}}>Zen Mode</button>
{{/full-post}}
```

Note: When using `{{action}}` helpers, instead of a component, you need to
specify target instead of targetObject.
Also, when working with actions and sibling components, use viewName to
"export" the sibling component instance as a possible target.]]></description><link>https://ilyaradchenko.com/users/knownasilya/notes/cltluzcna000dn3bbsbk7cyvv</link><guid isPermaLink="false">users/knownasilya/notes/cltluzcna000dn3bbsbk7cyvv</guid><pubDate>Sun, 10 Mar 2024 18:39:21 GMT</pubDate></item></channel></rss>