Skip to content

Commit

Permalink
Merge branch 'main' of github.com:avo-hq/avodocs
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianthedev committed Dec 6, 2023
2 parents cdb61ef + ec4eb83 commit e5e5b3b
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 89 deletions.
116 changes: 72 additions & 44 deletions docs/2.0/stimulus-integration.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
feedbackId: 943
version: '2.8'
version: "2.8"
demoVideo: https://www.youtube.com/watch?v=ZMOz22FaAUg
betaStatus: Beta
---
Expand Down Expand Up @@ -127,6 +127,29 @@ export default class extends Controller {

The possible values are `index`, `show`, `edit`, or `new`

## Assign Stimulus controllers to actions

Similarly as to resource, you can assign stimulus controller to an action. To do that you can use the `stimulus_controllers` option on the action file.

```ruby
class ShowCurrentTime < Avo::BaseAction
self.stimulus_controllers = "city-in-country"
end
```

You can add more and separate them by a space character.

```ruby
class ShowCurrentTime < Avo::BaseAction
self.stimulus_controllers = "course-resource select-field association-fields"
end
```

The same way as for the resources, Avo will add stimulus target data attributes to [all field wrappers](#field-wrappers-as-targets) and [all input fields](#field-inputs-as-targets).

Unlike with the resource, Avo will not add a specific default controller for each type of the view (`index`, `show`, `edit`).
Same way, the controllers will not receive the `view` attribute in the DOM, [as in case of resources](#all-controllers-receive-the-view-value).

## Attach HTML attributes

Using the `html` option you can attach `style`, `classes`, and `data` attributes. The `style` attribute adds the `style` tag to your element, `classes` adds the `class` tag, and the `data` attribute the `data` tag to the element you choose.
Expand Down Expand Up @@ -346,13 +369,13 @@ You can use the attributes together to make your fields more dynamic.

```js
// toggle_fields_controller.js
import { Controller } from '@hotwired/stimulus'
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
static targets = ['skillsTagsWrapper'] // use the target Avo prepared for you
static targets = ["skillsTagsWrapper"]; // use the target Avo prepared for you

toggleSkills() {
this.skillsTagsWrapperTarget.classList.toggle('hidden')
this.skillsTagsWrapperTarget.classList.toggle("hidden");
}
}
```
Expand Down Expand Up @@ -519,48 +542,50 @@ end
```

```js [course_resource_controller.js]
import { Controller } from '@hotwired/stimulus'
import { Controller } from "@hotwired/stimulus";

const LOADER_CLASSES = 'absolute bg-gray-100 opacity-10 w-full h-full'
const LOADER_CLASSES = "absolute bg-gray-100 opacity-10 w-full h-full";

export default class extends Controller {
static targets = ['countryFieldInput', 'cityFieldInput', 'citySelectWrapper'];
static targets = ["countryFieldInput", "cityFieldInput", "citySelectWrapper"];

static values = {
view: String,
}
};

// Te fields initial value
static initialValue
static initialValue;

get placeholder() {
return this.cityFieldInputTarget.ariaPlaceholder
return this.cityFieldInputTarget.ariaPlaceholder;
}

set loading(isLoading) {
if (isLoading) {
// create a loader overlay
const loadingDiv = document.createElement('div')
loadingDiv.className = LOADER_CLASSES
loadingDiv.dataset.target = 'city-loader'
const loadingDiv = document.createElement("div");
loadingDiv.className = LOADER_CLASSES;
loadingDiv.dataset.target = "city-loader";

// add the loader overlay
this.citySelectWrapperTarget.prepend(loadingDiv)
this.citySelectWrapperTarget.classList.add('opacity-50')
this.citySelectWrapperTarget.prepend(loadingDiv);
this.citySelectWrapperTarget.classList.add("opacity-50");
} else {
// remove the loader overlay
this.citySelectWrapperTarget.querySelector('[data-target="city-loader"]').remove()
this.citySelectWrapperTarget.classList.remove('opacity-50')
this.citySelectWrapperTarget
.querySelector('[data-target="city-loader"]')
.remove();
this.citySelectWrapperTarget.classList.remove("opacity-50");
}
}

async connect() {
// Add the controller functionality only on forms
if (['edit', 'new'].includes(this.viewValue)) {
this.captureTheInitialValue()
if (["edit", "new"].includes(this.viewValue)) {
this.captureTheInitialValue();

// Trigger the change on load
await this.onCountryChange()
await this.onCountryChange();
}
}

Expand All @@ -569,56 +594,59 @@ export default class extends Controller {
async onCountryChange() {
if (this.hasCountryFieldInputTarget && this.countryFieldInputTarget) {
// Get the country
const country = this.countryFieldInputTarget.value
const country = this.countryFieldInputTarget.value;
// Dynamically fetch the cities for this country
const cities = await this.fetchCitiesForCountry(country)
const cities = await this.fetchCitiesForCountry(country);

// Clear the select of options
Object.keys(this.cityFieldInputTarget.options).forEach(() => {
this.cityFieldInputTarget.options.remove(0)
})
this.cityFieldInputTarget.options.remove(0);
});

// Add blank option
this.cityFieldInputTarget.add(new Option(this.placeholder))
this.cityFieldInputTarget.add(new Option(this.placeholder));

// Add the new cities
cities.forEach((city) => {
this.cityFieldInputTarget.add(new Option(city, city))
})
this.cityFieldInputTarget.add(new Option(city, city));
});

// Check if the initial value is present in the cities array and select it.
// If not, select the first item
const currentOptions = Array.from(this.cityFieldInputTarget.options).map((item) => item.value)
const currentOptions = Array.from(this.cityFieldInputTarget.options).map(
(item) => item.value
);
if (currentOptions.includes(this.initialValue)) {
this.cityFieldInputTarget.value = this.initialValue
this.cityFieldInputTarget.value = this.initialValue;
} else {
// Select the first item
this.cityFieldInputTarget.value = this.cityFieldInputTarget.options[0].value
this.cityFieldInputTarget.value =
this.cityFieldInputTarget.options[0].value;
}
}
}

// Private

captureTheInitialValue() {
this.initialValue = this.cityFieldInputTarget.value
this.initialValue = this.cityFieldInputTarget.value;
}

async fetchCitiesForCountry(country) {
if (!country) {
return []
return [];
}

this.loading = true
this.loading = true;

const response = await fetch(
`${window.Avo.configuration.root_path}/resources/courses/cities?country=${country}`,
)
const data = await response.json()
`${window.Avo.configuration.root_path}/resources/courses/cities?country=${country}`
);
const data = await response.json();

this.loading = false
this.loading = false;

return data
return data;
}
}
```
Expand All @@ -639,11 +667,11 @@ First, you need to have a JS entrypoint (ex: `avo.custom.js`) and have that load

```js
// app/javascript/controllers/sample_controller.js
import { Controller } from '@hotwired/stimulus'
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
connect() {
console.log("Hey from sample controller 👋")
console.log("Hey from sample controller 👋");
}
}
```
Expand All @@ -652,14 +680,14 @@ export default class extends Controller {

```js
// app/javascript/avo.custom.js
import SampleController from 'controllers/sample_controller'
import SampleController from "controllers/sample_controller";

// Hook into the stimulus instance provided by Avo
const application = window.Stimulus
application.register('course-resource', SampleController)
const application = window.Stimulus;
application.register("course-resource", SampleController);

// eslint-disable-next-line no-console
console.log('Hi from Avo custom JS 👋')
console.log("Hi from Avo custom JS 👋");
```

### Use the controller in the Avo tool
Expand Down
12 changes: 12 additions & 0 deletions docs/3.0/associations/belongs_to.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ Any string.

<!-- @include: ./../common/associations_use_resource_option_common.md-->

:::option `can_create`
Controls the creation link visibility on forms.

#### Default

`true`

#### Possible values

`true`, `false`
:::

## Overview

On the `Index` and `Show` views, Avo will generate a link to the associated record containing the [`@title`](./../resources.html#setting-the-title-of-the-resource) value.
Expand Down
19 changes: 19 additions & 0 deletions docs/3.0/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,22 @@ end

![](/assets/img/customization/skip_show_view.gif)

## Logger

You may want to set a different output stream for avo logs, you can do that by returning it on a `config.logger` Proc

```ruby
## == Logger ==
config.logger = -> {
file_logger = ActiveSupport::Logger.new(Rails.root.join("log", "avo.log"))

file_logger.datetime_format = "%Y-%m-%d %H:%M:%S"
file_logger.formatter = proc do |severity, time, progname, msg|
"[Avo] #{time}: #{msg}\n".tap do |i|
puts i
end
end

file_logger
}
```
Loading

0 comments on commit e5e5b3b

Please sign in to comment.