Skip to content

Commit e9baf0f

Browse files
committed
fixing static component UIDs in dynamically added nested forms
1 parent d73e27c commit e9baf0f

File tree

2 files changed

+132
-5
lines changed

2 files changed

+132
-5
lines changed

lib/matestack/ui/vue_js/components/form/fields_for_add_item.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,19 @@ const componentDef = {
1515
methods: {
1616
addItem: function(key){
1717
var templateString = JSON.parse(this.getTemplateElement().querySelector('#prototype-template-for-'+key).dataset[":template"])
18-
var tmp_dom_elem = document.createElement('div')
19-
tmp_dom_elem.innerHTML = templateString
20-
var static_prototype_template_uid = tmp_dom_elem.querySelector('matestack-component-template').id.replace("uid-", "")
21-
var dynamic_prototype_template_uid = Math.floor(Math.random() * 1000000000);
22-
var templateString = templateString.replaceAll(static_prototype_template_uid, dynamic_prototype_template_uid);
18+
var regex = /"component_uid":"(.+?)"/g
19+
var staticComponentUidMatches = templateString.matchAll(regex)
20+
var staticComponentUids = []
21+
for (const match of staticComponentUidMatches) {
22+
staticComponentUids.push(match[1])
23+
}
24+
staticComponentUids.forEach(function(uid){
25+
var newUid = Math.floor(Math.random() * 1000000000);
26+
templateString = templateString.replaceAll(uid, newUid);
27+
})
28+
var tmpDomElem = document.createElement('div')
29+
tmpDomElem.innerHTML = templateString
30+
2331
if (this.parentNestedFormRuntimeTemplateDomElements[key] == null){
2432
var dom_elem = document.createElement('div')
2533
dom_elem.innerHTML = templateString

spec/test/vue_js/form/nested_forms_spec.rb

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,125 @@ def form_config
15131513

15141514
end
15151515

1516+
describe "supports custom form input components on dynamically added child forms" do
1517+
1518+
before do
1519+
@dummy_model = DummyModel.create(title: "existing-dummy-model-title")
1520+
1521+
class Components::CustomFormInputTest < Matestack::Ui::VueJs::Components::Form::Input
1522+
vue_name "custom-form-input-test" # defined in app/spec/dummy/javascript/components.js
1523+
1524+
def response
1525+
div class: "custom-input-markup" do
1526+
label "my form input"
1527+
span ":id": "'input_uid'+vc.$parent.nestedFormRuntimeId" do
1528+
plain "UID: {{ vc.props['component_uid'] }}"
1529+
end
1530+
input input_attributes
1531+
button "change value", "@click": "vc.changeValueViaJs(42)", type: :button
1532+
render_errors
1533+
end
1534+
end
1535+
1536+
register_self_as(:custom_form_input_test)
1537+
end
1538+
1539+
class ExamplePage < Matestack::Ui::Page
1540+
1541+
def prepare
1542+
@dummy_model = DummyModel.last
1543+
end
1544+
1545+
def response
1546+
matestack_form form_config do
1547+
form_input key: :title, type: :text, label: "dummy_model_title_input", id: "dummy_model_title_input"
1548+
1549+
@dummy_model.dummy_child_models.each do |dummy_child_model|
1550+
dummy_child_model_form dummy_child_model
1551+
end
1552+
1553+
form_fields_for_add_item key: :dummy_child_models_attributes, prototype: method(:dummy_child_model_form) do
1554+
button "add", type: :button # type: :button is important! otherwise remove on first item is triggered on enter
1555+
end
1556+
1557+
br
1558+
plain "data: {{vc.data}}"
1559+
br
1560+
1561+
button "Submit me!"
1562+
1563+
toggle show_on: "success", hide_after: 1000 do
1564+
plain "success!"
1565+
end
1566+
toggle show_on: "failure", hide_after: 1000 do
1567+
plain "failure!"
1568+
end
1569+
end
1570+
end
1571+
1572+
def dummy_child_model_form dummy_child_model = DummyChildModel.new(title: "init-value")
1573+
form_fields_for dummy_child_model, key: :dummy_child_models_attributes do
1574+
span ":id": "'uid'+vc.nestedFormRuntimeId" do
1575+
plain "UID: {{ vc.props['component_uid'] }}"
1576+
end
1577+
form_input key: :title, type: :text, label: "dummy-child-model-title-input"
1578+
custom_form_input_test key: :bar, type: :text, id: "bar"
1579+
form_fields_for_remove_item do
1580+
button "remove", ":id": "'remove'+vc.nestedFormRuntimeId", type: :button # id is just required in this spec, but type: :button is important! otherwise remove on first item is triggered on enter
1581+
end
1582+
end
1583+
end
1584+
1585+
def form_config
1586+
{
1587+
for: @dummy_model,
1588+
method: :put,
1589+
path: nested_forms_spec_submit_update_path(id: @dummy_model.id),
1590+
success: { emit: "success" },
1591+
failure: { emit: "failure" }
1592+
}
1593+
end
1594+
end
1595+
end
1596+
1597+
it "dynamically adds unlimited new nested forms with different UIDs enabling proper getElement() usage when working with child form DOM" do
1598+
# relevant form form input components accessing their child form DOM via UID
1599+
1600+
dummy_model = DummyModel.last
1601+
child_model_0 = dummy_model.dummy_child_models.create(title: "existing-child-model-title")
1602+
1603+
visit "/example"
1604+
# sleep
1605+
uids = []
1606+
expect(page).to have_selector('#dummy_model_title_input')
1607+
expect(page).to have_selector('#title_dummy_child_models_attributes_child_0')
1608+
within "#dummy_child_models_attributes_child_0" do
1609+
uids << page.find_by_id("uid_dummy_child_models_attributes_child_0")
1610+
uids << page.find_by_id("input_uid_dummy_child_models_attributes_child_0")
1611+
end
1612+
expect(page).to have_xpath('//matestack-component-template[@id="dummy_child_models_attributes_child_0"]//div[@class="custom-input-markup"]/input[@id="bar" and @class="js-added-class"]')
1613+
click_on "add"
1614+
within "#dummy_child_models_attributes_child_1" do
1615+
uids << page.find_by_id("uid_dummy_child_models_attributes_child_1")
1616+
uids << page.find_by_id("input_uid_dummy_child_models_attributes_child_1")
1617+
click_on "change value"
1618+
end
1619+
expect(page).to have_xpath('//div[@class="matestack-form-fields-for" and @id="dummy_child_models_attributes_child_1"]//div[@class="custom-input-markup"]/input[@id="bar" and @class="js-added-class"]')
1620+
click_on "add"
1621+
within "#dummy_child_models_attributes_child_2" do
1622+
uids << page.find_by_id("uid_dummy_child_models_attributes_child_2")
1623+
uids << page.find_by_id("input_uid_dummy_child_models_attributes_child_2")
1624+
end
1625+
expect(page).to have_xpath('//div[@class="matestack-form-fields-for" and @id="dummy_child_models_attributes_child_2"]//div[@class="custom-input-markup"]/input[@id="bar" and @class="js-added-class"]')
1626+
1627+
expect(uids.count).to eq(uids.uniq.count)
1628+
1629+
expect(page).to have_content("data: { \"dummy_child_models_attributes\": [ { \"_destroy\": false, \"id\": \"#{child_model_0.id}\", \"title\": \"existing-child-model-title\", \"bar\": null }, { \"_destroy\": false, \"id\": null, \"title\": \"init-value\", \"bar\": 42 }, { \"_destroy\": false, \"id\": null, \"title\": \"init-value\", \"bar\": null } ], \"title\": \"existing-dummy-model-title\" }")
1630+
1631+
end
1632+
1633+
end
1634+
15161635
end
15171636

15181637
end

0 commit comments

Comments
 (0)