Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replacer interface #925

Closed
wants to merge 31 commits into from
Closed

replacer interface #925

wants to merge 31 commits into from

Conversation

maxence-charriere
Copy link
Owner

Add a component interface to force dismount/mount on updates.

@oderwat does something like this solve your problem?

* rework condition to take functions instead of ui elements

* update ci go version

* improve condition test coverage

* test only pkg

* only test app pkg

* Update build.yml

* Update build.yml

* Update build.yml
…aul (#888)

* replace remaining strings argument by format

* use format with Style

* Improve generated documentation.

* improve generated doc C

* Improve generated doc D - E

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* update event handlers doc

* Update html.go

* Update html.go

* Update html.go

* modify fixed method doc

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* Update html.go

* update generated doc

* update generated wording
* make routes call only take factory functions

* add NewComponentFunc

* rename NewComponentFunc to NewZeroComponentFunc

* rename NewZeroComponentFunc to NewZeroComponentFactory
* update former FilterUIElems and its doc

* improve FilterUIElems unit tests

* cleanup test
* node manager declaration

* mount text

* remove node manager dismountText

* node manager can update

* node manager update text

* updade node manager doc

* mount html element

* node manager resolve url

* Update node_test.go

* node manager mount html attributes

* node manager mount html event handler

* test mount event handler

* node manager make context

* Update html.go

* node manager dismount html element

* html element setters to return the element

* node manager updateHTMLAttributes

* node manager update html event handlers

* node manage update html children

* Update node_test.go

* unexport Depth()

* node manager mount component partial implementation

* node manager component mount

* improve node manager mount test coverage

* Update node_test.go

* Update node.go

* node manager dismount component

* node manager update component

* can update value tests

* node manager mount raw html

* node manager dismount raw html

* node manager update raw html

* node manager notify component update on html events

* node manager NotifyComponentEvent

* refactor node manager to use context

* use function to get page in node context

* enginex navigate and load

* enginex url tests

* update manager

* enginex start implementation

* fix enginex context

* implement more node context methods

* browser app updatble

* Update browser.go

* comple browser init

* Update enginex.go

* refactor actions

* enginex start testing

* update context documentation

* add notifications to node context

* state manage implementation part1

* state manager broadcast

* state manager observer and get unit tests

* state manager test  set partial tests

* state manager remaining tests

* fix doc site

* cleanup

* node manage encode text

* node manage encode raw

* node manager encode component

* node manager encode html

* update node manager doc

* Update node.go

* node manage encode test

* enginex to load component intro body first child only

* change loading

* enginex root

* refactor node manager encode

* engine x encode

* fix set state

* context to be a single structure

* update context

* state manager delete

* Update context.go

* fix on anchor click

* fix fragment navigation

* fix js loading

* fix node manager foreach updatable component

* fix action post

* Update node.go

* update default rate

* Update app.go

* Update app.go

* revert oninit and prerender deprecation

* Update context.go

* remove emit back

* cleanup

* add back benchmarks

* add generated unit test

* add unit test to gen html

* fix context defer and add unit tests

* refactor testing

* refactor testing

* refactor HTMLString

* cleanup
* Update js_wasm.go

* cleanup
* resource resolver for local and remote dir

* fix macro run

* refactor http to use resource resolver

* cleanup

* cleanup

* Update go.sum

* Update http.go

* add domain to http handler

* Update http.go

* resolve of url

* resolve twitter image

* trim prefixed version path

* updat http

* wasm content length set

* WasmContentLength as a string

* Update http.go

* Update http.go

* resolve manifest urls
* http resource

* use parse http resource

* Update http.go

* Update http.go
* http resource

* use parse http resource

* Update http.go

* Update http.go

* replace update struct by counter

* Update update.go
* trigger on update whenever a component is updated

* update doc
@oderwat
Copy link
Sponsor Contributor

oderwat commented Feb 16, 2024

I looked into this, but I did not get it to work or did not understand how it is meant to be used.

I don't see how this solves the problem shown in the demo code at all. Did you checkout that demo and run it on your system? I wrote it, so it can be used as basis to test, implement and demonstrate the "fix".

From what I understand this extension could be used to replace my mounter implementation that uses two types with one that uses one type. But it would still need to use a proxy component for making this kind of node replacing work.

As I tried to write an implementation using the ReplaceOnUpdate() the engine crashed with "is already mounted" (which is by the way, similar to the problem I had, when I tried to implement something like this some month ago and had to give up).

I attach the code I wrote for testing this new interface trying to implement a replacer here, but please check out the original demo code at https://github.com/metatexx/go-app-pkgs

How I tried to use the new interface to replace the mountpoint:

package main

import (
	"fmt"
	"github.com/metatexx/go-app-pkgs/mountpoint"
	"log"
	"net/http"
	"strconv"

	"github.com/maxence-charriere/go-app/v9/pkg/app"
)

type tab struct {
	app.Compo
	Text string
}

func (c *tab) Render() app.UI {
	return app.Div().Body(
		app.Div().Text(c.Text),
	)
}

type replacer struct {
	app.Compo
	replace bool
	element app.UI
}

func (c *replacer) Render() app.UI {
	if c.element == nil {
		return app.Div()
	}
	return c.element
}

func (c *replacer) Replace(e app.UI) {
	c.element = e
	c.replace = true
}

var _ app.Replacer = (*replacer)(nil)

func (c *replacer) ReplaceOnUpdate() bool {
	if app.IsServer {
		return false
	}
	app.Log("replace on update is called")
	replace := c.replace
	c.replace = false
	return replace
}

type isolate struct {
	app.Compo
	tabs1  []app.UI
	tabs2  []app.UI
	rep    *replacer
	Active int
	mp     *mountpoint.UI
}

func (c *isolate) OnInit() {
	c.tabs1 = []app.UI{&tab{Text: "Content A"}, &tab{Text: "Content B"}, &tab{Text: "Content C"}}
	c.tabs2 = []app.UI{&tab{Text: "Content A"}, &tab{Text: "Content B"}, &tab{Text: "Content C"}}
	c.mp = mountpoint.New(c.tabs1[0])
	c.rep = &replacer{}
	c.rep.Replace(c.tabs2[0])
}

func (c *isolate) OnNav(ctx app.Context) {
	url := ctx.Page().URL()
	idx, err := strconv.Atoi(url.Fragment)
	if err != nil {
		idx = 0
	}
	c.Active = idx
	c.rep.Replace(c.tabs2[c.Active])
	c.mp.Switch(c.tabs1[c.Active])
}

func (c *isolate) Render() app.UI {
	return app.Div().Style("padding", "5px").Body(
		app.Div().Style("padding", "5px").Body(
			app.Span().Text("To see the problem click tabs 0,1,2 and then 0 again."),
			app.Br(),
			app.Span().Text("Notice how the 'intuitive' implementation does not work."),
		),
		app.Div().Body(
			app.A().Style("padding", "5px").Href("#0").Text("Tab 0"),
			app.A().Style("padding", "5px").Href("#1").Text("Tab 1"),
			app.A().Style("padding", "5px").Href("#2").Text("Tab 2"),
			app.Div().Style("margin-top", "10px").Text("Replacer implementation"),
			app.Div().Style("padding", "5px").Body(c.rep),
			app.Div().Style("margin-top", "10px").Text("Using mountpoint"),
			app.Div().Style("padding", "5px").Body(c.mp.UI()),
		),
	)
}

func main() {
	app.Route("/", func() app.Composer { return &isolate{} })
	app.RunWhenOnBrowser()
	http.Handle("/", &app.Handler{
		Name:        "Isolate",
		Description: "Isolated functionality test",
	})
	fmt.Println("open your browser at http://127.0.0.1:8000")
	if err := http.ListenAndServe(":8000", nil); err != nil {
		log.Fatal(err)
	}
}

@maxence-charriere
Copy link
Owner Author

maxence-charriere commented Feb 19, 2024

Taking a look here but reviewing your example, implementation would be safer if you store functions that return components rather than storing a reference to the component itself.

Here what is happening is that you keep updating the first component that was mounted (A) so the A value got lost forever.

@oderwat
Copy link
Sponsor Contributor

oderwat commented Feb 19, 2024

(deleted) I missed the code example you gave me.

Base automatically changed from v10 to master May 20, 2024 05:59
@maxence-charriere maxence-charriere deleted the ephemeral-component branch May 23, 2024 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants