Basic CoffeeScript not firing events when run? - coffeescript

I'm having real problems writing a simple Backbone.js app using CoffeeScript and Zepto.js
This is the simplest Backbone view yet the events don't fire. I get no errors in the console either? Where am I going wrong?
#Main view
class AppView extends Backbone.View
constructor: ->
#el = $("#books")
#template = _.template("<div>New Item <a href='' id='addNew'> add new item</a></div>")
events: {
"click" : "createNew"
}
render: =>
#el.html(#template())
createNew : ->
console.log "new"
#Onload
$(document).ready ->
view = new AppView
view.render()
I've been following the only example I can find of CoffeeScript & Backbone together https://github.com/bnolan/Backbone-Mobile/blob/master/application.coffee
However if I add super into my view code above I get an undefined error, his code does not.

The class Backbone.View has its own constructor that does plenty of work, and you are overriding it and not calling super. Bad.
Instead, Backbone.View provides you the ability to define your own constructor-type function called initialize. Perform all your setup there. Backbone.View#constructor will call initialize.
#Main view
class AppView extends Backbone.View
initialize: ->
#el = $("#books")
#template = _.template(
"<div>New Item <a href='' id='addNew'> add new item</a></div>"
)

I had a similar problem (events not firing) and found that the problem was due to not setting #el. I set that:
#el: $("#content")
and it worked.

Related

Kotlin Multiplatform Compose ~ How to use events to control views

I have a Kotlin Multiplatform project where I want to use Events to control views.
The basic idea is this:
Buttons & Co fire an Event when clicked
These events get caught and handled by the responsible program components, which will in turn fire other events
Eventually, some kind of ViewEvent is fired, which is subscribed to by the ViewController
The ViewController then tells the program what should be drawn on the screen
In theory, that sounds like it should work. In practice, what happens is that while it gets to the point where the ViewController receives the event and reacts accordingly, the actual views are unaffected.
My ViewController looks like this:
import androidx.compose.runtime.Composable
import com.tri_tail.ceal_chronicler.events.OpenCharacterSelectionViewEvent
import com.tri_tail.ceal_chronicler.ui.main_view.MainView
import com.tri_tail.ceal_chronicler.ui.main_view.MainViewState
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
class ViewController {
private var mainViewState = MainViewState.TITLE
init {
val eventBus = EventBus.getDefault()
eventBus.register(this)
}
#Composable
fun draw() {
MainView(mainViewState)
}
#Subscribe
fun onOpenCharacterSelectionViewEvent(event: OpenCharacterSelectionViewEvent) {
mainViewState = MainViewState.CHARACTER
}
}
I debugged that, and was able to see that the mainViewState changes, as expected. However, the draw() function is never called again, and so the changed mainViewState never arrives in the MainView.
I've already tried making mainViewState a mutableStateOf(mainViewState), but that didn't change anything.
Furthermore, I can't just call draw() inside the onOpenCharacterSelectionViewEvent, because it is not #Composable, and adding that annotation to the method causes the build to fail.
At this point, I am not even sure whether what I am trying to do here can work this way. Can someone please help me out here?
I have also published a version of the code with the current non-working solution here: https://github.com/KiraResari/ceal-chronicler/tree/event-system
For my KMM project i use open source viewModel for KMM.
This one https://github.com/adeo-opensource/kviewmodel--mpp
I suggest transfer your MVC architecture to MVI and use this KMM viewModel to control your state as in usual android app.
Okay, so after worrying at this for several days, I have now come up with a solution that works.
Basically, the reason why it doesn't work as I tried it is that the frontend lives in its own little world, and it is very difficult for something from outside that world to affect it.
However, it can be done using delegates. Basically, what I called the ViewController in above is more of a MainViewModel, and it needs to look like this:
import com.tri_tail.ceal_chronicler.events.OpenCharacterSelectionViewEvent
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
class MainViewModel {
var state = MainViewState.TITLE
var updateState: ((MainViewState) -> Unit) = { }
set(value) {
field = value
updateState(state)
}
init {
val eventBus = EventBus.getDefault()
eventBus.register(this)
}
#Subscribe
fun onOpenCharacterSelectionViewEvent(event: OpenCharacterSelectionViewEvent) {
state = MainViewState.CHARACTER
updateState(state)
}
}
The other part of the magic happens in the MainView, where the state needs to be a remember with a mutableStateOf(..., policy = neverEqualPolicy()), and the delegate needs to be set like this:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.*
import com.tri_tail.ceal_chronicler.models.main_view.MainViewModel
import com.tri_tail.ceal_chronicler.models.main_view.MainViewState
import com.tri_tail.ceal_chronicler.theme.AppTheme
import com.tri_tail.ceal_chronicler.ui.TitleScreen
import com.tri_tail.ceal_chronicler.ui.characters.DisplayCharacterSelector
#Composable
fun MainView(model: MainViewModel = MainViewModel()) {
var state by remember {
mutableStateOf(
model.state,
policy = neverEqualPolicy()
)
}
model.updateState = {
state = it
}
AppTheme {
when (state) {
MainViewState.TITLE -> TitleScreen()
MainViewState.CHARACTER -> DisplayCharacterSelector()
}
}
}
And that's all there is to it! Works like a charm, no extra libraries required.

Best practice for testing for data-testid in a nested component with React Testing Library?

I'm trying to write a test to check if my app is rendering correctly. On the initial page Ive added a data-testid of "start". So my top level test checks that the initial component has been rendered.
import React from "react";
import { render } from "react-testing-library";
import App from "../App";
test("App - Check the choose form is rendered", () => {
const wrapper = render(<App />);
const start = wrapper.getByTestId("start");
// console.log(start)
// start.debug();
});
If I console.log(start) the I can see all the properties of the node. However if I try and debug() then it errors saying it's not a function.
My test above does seem to work. If I change the getByTestId from start to anything else then it does error. But I'm not using the expect function so am I violating best practices?
There are two parts to this question -
Why console.log(start) works and why not start.debug()?
getByTestId returns an HTMLElement. When you use console.log(start), the HTMLElement details are logged. But an HTMLElement does not have debug function. Instead, react-testing-library provides you with a debug function when you use render to render a component. So instead of using start.debug(), you should use wrapper.debug().
Because you don't have an expect function, is it a good practice to write such tests ?
I am not sure about what could be a great answer to this, but I will tell the way I use it. There are two variants for getting an element using data-testid - getByTestId and queryByTestId. The difference is that getByTestId throws error if an element with the test id is not found whereas queryByTestId returns null in such case. This means that getByTestId in itself is an assertion for presence of element. So having another expect which checks if the element was found or not will be redundant in case you are using getByTestId. I would rather use queryByTestId if I am to assert the presence/absence of an element. Example below -
test("App - Check the "Submit" button is rendered", () => {
const { queryByTestId } = render(<App />)
expect(queryByTestId('submit')).toBeTruthy()
});
I would use getByTestId in such tests where I know that the element is present and we have expects for the element's properties (not on the element's presence/absence). Example below -
test("App - Check the "Submit" button is disabled by default", () => {
const { getByTestId } = render(<App />)
expect(getByTestId('submit')).toHaveClass('disabled')
});
In the above test, if getByTestId is not able to find the submit button, it fails by throwing an error, and does not execute the toHaveClass. Here we don't need to test for presence/absence of the element, as this test is concerned only with the "disabled" state of the button.

How to lock Form during lunch a class?

I have a simple Form MyCustomForm, in a Form's Button in Clicked method I call a Class (method run), so I want to lock (or block) this form during run execution.
My code is look like this :
void clicked()
{// in Button clicked in **MyCustomForm**
MyClass myClass;
super();
myClass = new MyClass();
// here I want to freeze my FORM
myClass.run();
// here I want to unlock my Form
}
I need this because when class (MyClass) is running can display Dialog etc, but I don't want to touc/click and other on MyCustomForm
If I use :
element.wait(); // not work well - block all
myClass.run();
Thanks,
enjoy.
If your class displays dialog you can make this dialog modal using the following line of code dialog.parmIsModal(true).
Or formRun.wait(true) for forms.

Listen to Router events using ChaplinJS's mediator

I currently trying out ChaplinJS for a new project and I'm running into a problem.
I want to have a controller (for my navigation) that's listening to the changes of the router. I want to have callbacks for events like changeURL or route. I'm not sure how Chaplin's mediator works but I thought the router is throwing events using the mediator which I should be able to catch. I tried this:
mediator = require 'mediator'
Controller = require 'controllers/base/controller'
Menu = require 'models/menu'
MenuView = require 'views/menu-view'
module.exports = class MenuController extends Controller
listen:
'router:changeURL mediator': #test
initialize: ->
super
#menu = new Menu()
#view = new MenuView model: #menu
mediator.subscribe 'router:changeURL', #test
test: ->
console.log 'testlisten'
You should use beforeAction method to accomplish this.
The method receives all arguments actions receive. http://docs.chaplinjs.org/chaplin.controller.html
There is no listen in controllers.

'active' class not added when Spine.js controller stack used

I have two very simple Spine.js controllers:
class ListController extends Spine.Controller
className: 'list'
constructor: () ->
super
class DetailController extends Spine.Controller
className: 'detail'
constructor: () ->
super
controller stack
class Application extends Spine.Stack
className: 'mystack'
controllers:
list: ListController
detail: DetailController
and corresponding HTML markup
<div class="mystack">
<div class="list">list</div>
<div class="detail">detail</div>
</div>
My problem is that when controller stack instantiated
app = new Application()
app.list.active()
there is no active class added to the div.list element. Divs remain unchanged.
What is wrong with that?
I've just got it so I'll describe basic working example. There are several issues with the code above (caused by my misunderstanding of how Spine.js controller stack works :-)
First, appropriate HTML element have to be associated with every controller managed by the stack. When controller stack instantiates the controller it passes only stack (i.e. itself) instance as parameter to the constructor. So controller constructor have to take it into account (e.g. like the following):
class ListController extends Spine.Controller
constructor: (parameters) ->
#stack = parameters.stack
#el = $ #stack.settings.listSelector
super
class DetailController extends Spine.Controller
constructor: (parameters) ->
#stack = parameters.stack
#el = $ #stack.settings.detailSelector
super
and the stack:
class Application extends Spine.Stack
settings:
listSelector: '.list'
detailSelector: '.detail'
controllers:
list: ListController
detail: DetailController
default:
'list'
then the controller stack could be instantiated:
app = new Application
el: $ '.mystack'
ListController will be active (i.e. corresponding div has active class added) by default and anytime later you can call #stack.detail.active() or #stack.list.active() from controller instance method to activate required controller and 'hide' (i.e. remove active class) the other(s).
EDIT:
We discussed the issue with #aschmid00. In fact, controller constructor doesn't have to set its own property #stack manually. It is done automatically when base constructor called by super. But in case of this question #el have to be set before base constructor called due to the events delegation etc.