Ember component constructor args is Proxy {} - ember.js

In a fresh Ember 3.20 project (also same in 3.19), after adding a new component.
application.hbs:
<Test #foo="hello" />
components/test.js:
import Component from '#glimmer/component';
export default class TestComponent extends Component {
constructor(){
super(...arguments)
console.log(this.args)
}
}
Console: Proxy {}
How can I access this.args within the constructor?

the args of a component is a Proxy so that special behavior can wrap accesses to the args. For example, when you access one of the args -- only then will the arg be "consumed" and your getters/actions entangled with the state of that arg. This has the advantage of optimizing your change tracking by default, so if you pass 100 args, and only use one or two of them in your component, you don't have to pay the cost of those 100 args causing updates to your component.
It works kinda like this:
args = new Proxy(actualArgs, {
get(target, argName) {
consumeTag(tagFor(target, argName));
return target[argName];
}
});
Where, normally, if you only had a vanilla object, accessing the arg would only get you the value. This proxy demonstrates interaction with the tracking system so that your component can now be entangled with the arg's updates
For more information on autotracking, #pzuraq goes in to great depth here: https://www.pzuraq.com/how-autotracking-works/ (not necessarily auto-tracking in ember, but more in general)
Also, documentation on Proxy, if interested: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Note: this information is for Ember 3.13+

Related

How trigger a function inside component whenever arguments change in Emnber octane

so i have a parent in controller like this
import Controller from '#ember/controller';
export default class IndexController extends Controller {
#service firebaseApp;
#service fastboot;
#tracked user =false;
async init(){
super.init(...arguments);
if (!this.fastboot.isFastBoot){
const auth = await this.firebaseApp.auth();
auth.onAuthStateChanged((user)=>{
if(user){
this.user = true
} else {
this.user = false
}
})
}
}
then call a component loadData like this
<LoadData #user={{this.user}}/>
the question is how to execute a function inside component loadData when #user change?
Triggering an action when an argument changes is not that well supported by Ember Octane primitives yet. A common approach is to use either #ember/render-modifiers or ember-render-helpers.
#ember/render-modifiers provide a {{did-update}} modifier.
ember-render-helpers provide a {{did-update}} helper. Both the modifier and the helper except a function as first position argument. That function is executed whenever one of the other positional arguments changes.
{{did-update}} modifier is helpful when the function executed needs access to a DOM element. It sets the DOM element, which it is attached to, as an argument on the function when called.
{{did-update}} helper is helpful when the function executed does not need access to a DOM element.
{{! A Glimmer template }}
{{! did-update helper }}
{{did-update this.loadData #user}}
{{! did-update modifier }}
<div class="loading" {{did-update this.showLoadingSpinner #user}} />
The main use case for {{did-update}} modifier is to ease the migration from classical #ember/component to #glimmer/component. In nearly all cases a specific modifier containing the logic, which should be executed, itself is a better solution. It provides better reusability, can be tested in isolation and has clear boundaries to the components in which it is used.
The main use case for {{did-update}} helper is to fill a gap in current Ember Octance programming model. Ember Octance provides an awesome developer experience for derived state thanks to autotracking and native getters. It provides a great experience to modify a DOM element depending on a value. But it does not have great primitives yet to trigger actions like data loading when an argument changes.
The community current experiments with different approaches to fill that gap. It seems to settle on #use decorator and resources as proposed by Chris Garret (pzuraq) in an RFC and in a recent blog post. It's available for experiments as part of ember-could-get-used-to-this package.
The {{did-update}} helper provided by ember-render-helpers is the most established solution to fill that gap until something like resources settle in Ember.

Difference between Ember.get() and this.get()

I'm new to Ember and it keeps confusing me about the difference between this.get() and Ember.get(). Can someone explain them briefly?
Welcome to Ember ;-)
Every object that extends Ember Observable mixin supports the get() method (among others).
When you call this.get(), the this must refer to such an object (Route, Controller, Component, your own class that extends Ember.Object and so on). Calling get() on plain object would cause a failure. Let me show the difference:
const emberObjectInstance = Ember.Object.create({
name: 'Bala'
});
emberObjectInstance.get('name'); // returns 'Bala'
const plainObject = { name: 'Bala'};
plainObject.get('name'); // causes a failure since get() is not a function
However, using Ember.get() successes in both cases:
Ember.get(emberObjectInstance, 'name'); // returns 'Bala'
Ember.get(plainObject, 'name'); // returns 'Bala', too
which can be also written with imports as follows
import { get } from '#ember/object';
get(emberObjectInstance, 'name'); // returns 'Bala'
get(plainObject, 'name'); // returns 'Bala', too
Note: not to forget, calling either of get() makes computed property get computed (in the most common cases, I don't want to dive deep now - lazy computation, volatile extensions etc), but for the sake of understanding the difference, we can work with plain values.
From own experience, I am using Ember.get() everywhere I know a plain object might be the object whose property I need to retrieve. A nice example is setupController() hook into which I may pass plain object from my unit tests to test setupController()'s functionality.
// some route:
setupController(controller, model){
this._super(...arguments);
const name = Ember.get(model, 'name'); // ***
controller.set('isNamePresentOnSetup', Ember.isPresent(name));
}
// in my unit tests I can use plain object:
...
const modelMock = { name: 'Bala' }; // plain object is enough because I use Ember.get instead of model.get() (see ***)?
const controllerMock = Ember.Object.create(); // has to be Ember.Object since I use controller.set() within setupController()
subject.setupController(controllerMock, modelMock);
assert.ok(controllerMock.get('isNamePresentOnSetup'), "property 'isNamePresentOnSetup' set up correctly if model name is present");
...
I could also user Ember.set(controller, 'isNamePresentOnSetup', Ember.isPresent(name)) and then pass plain controller mock into setupController(), too.
I think this is a good start since you are new in Ember and I am sure Ember gurus would have much more to add.
Relevant Ember docs:
https://guides.emberjs.com/v2.9.0/object-model/
https://guides.emberjs.com/v2.9.0/object-model/computed-properties/
https://guides.emberjs.com/v2.9.0/object-model/reopening-classes-and-instances/
UPDATE:
Using get() with chained paths works different than working with POJOs.
For example in objectInstance.get('a.b.c') if b is undefined the return value is undefined. Converting this to objectInstance.a.b.c when b is undefined would instead raise an exception.
There is none. foo.get('bar') is equivalent to Ember.get(foo, 'bar'). However because foo.get is defined on Ember.Object you can only call .get() on Ember Objects. Ember.get() will work on all ember objects. On Ember Objects Ember.get(foo, 'bar') is equivalent to foo.get('bar'), on every other object its equivalent to foo['bar'].
Please note that using Ember.get() or this.get() is not needed anymore for most use cases if running Ember >= 3.1, which was released in April 2018. You could now use native ES5 getters. A quick introduction to this change could be found in release notes for Ember 3.1. It's discussed more in detail in RFC 281.
There is a codemode available that helps you transition to ES5 getters: es5-getter-ember-codemod It could be run as part of ember-cli-update.
Please not that using Ember.get() or this.get() is not deprecated. It's still needed for some edge cases, that are listed in release notes linked above:
In fact there are several cases where you must still use get:
If you are calling get with a chained path. For example in this.get('a.b.c') if b is undefined the return value is undefined. Converting this to this.a.b.c when b is undefined would instead raise an exception.
If your object is using unknownProperty you must continue to use get. Using an ES5 getter on an object with unknownProperty will cause an assertion failure in development.
Ember Data returns promise proxy objects when you read an async relationship and from other API. Ember proxy objects, including promise proxies, still require that you call get to read values.
Please note that there is a special case if using ember-changeset. It provides it's own .get() implementation. Therefore Ember.get(this, 'value') and this.get('value') have different results if this is an ember-changeset. You find more information on that case in documentation of ember-changeset.

How do I initialize an Ember service at app boot without type injection?

Perhaps this is a little esoteric but I need some help.
My use case is a clock ticker. I want a service that upon init() will start a timer. (for example an AJAX polling service). I want this to start at app boot but I don't wish to inject it into every object type.
What I've tried:
Writing a service and using Ember.inject.service() in the application controller.
Use an initializer with app.register(…, MyService.create(), {instantiate: false}) without calling app.inject(…).
Don't start the timer in the init() instead Ember.inject.service() it into the application route/controller and in its init() call this.get('myService').startTimer().
Here are some of the stumbling blocks I've run into:
Services are lazy loaded and so the timer never starts because the application controller never performed a this.get('myService'). I could do that in the controller's init() but that felt like a code smell.
Seems that the ember resolver will see the services/my-service.js file and auto register it. Performing an app.register() seems to register two instances and the two get confused.
Like getting the lazy service in the application controller's init() this solution also felt like a code smell. But of all the solutions I've tried this works and is the least smelly of the three.
Is there any other alternatives?
TL;DR Use an instance initializer
An instance initializer will have the needed lookup functions to fetch a service that the system auto registered and perform an action on it.
However, it may be more appropriate to save this initialization for a route or controller's init() because any ajax like thing here will still fall under Embers Loading state and run loop. While being performed in an instance initializer will degrade boot performance with no actual benefits.
If you still feel the initializer is the way to go here is a contrived example that is both Ember 1.13 and 2.0 compatible:
// app/instance-initializers/start-my-service.js
export function initialize(app) {
const { container = app } = app;
const myService = container.lookup('service:my-service');
myService.startPolling();
}
export default {
initialize
};
An example ember-twiddle.

React components that use context are painful to unit test

React doesn't provide an API that lets you pass in context to a created component class, so you have to write a wrapper component that provides the context.
Unfortunately, once you do this, you no longer have direct access to the component you are trying to test - unlike TestUtils.renderIntoDocument, functions like TestUtils.findRenderedComponentWithType don't return the actual rendered component instance, they only return the component constructor. Thus you can't call methods on the component, or set the component state to some known value before executing the test. The only thing you really have access to is the DOM node for your component, which is fine if all you want to do is black box testing, but for some kinds of components that's not sufficient.
I'm curious to know if anyone has come up with a solution for this. I've tried about a dozen different approaches, none of which work. (For example, I tried using 'ref' in my wrapper component, but it has the same problem - doesn't give you access to the real object.)
(Answering my own question)
Turns out the correct answer to all of this is to use enzyme, which replaces the standard React test utils - it offers a ton of features with a jQuery-like API, and best of all it completely supports component contexts. I've switched all of my tests over to it and it's great!
You can build a mock parent component like:
class MockContextContainer extends Component {
static propTypes = {
children: PropTypes.element.isRequired,
};
static childContextTypes = {
onSetTitle: PropTypes.func,
};
getChildContext() {
return {
onSetTitle: () => {},
};
}
render() {
return this.props.children;
}
}
Then use it in your test (in this case its a forgot password form example):
const cxtForgot = TestUtils.renderIntoDocument(
<MockContextContainer><ForgotPasswordForm /></MockContextContainer>
);
Which is what you may already be doing.
You can then do things like:
const input = TestUtils.findRenderedDOMComponentWithClass(
cxtForgot, 'ForgotPasswordForm-input'
);
// enter a valid email
input.value = 'abc#hotmail.com';
TestUtils.Simulate.change(input);
// no error class and button is now enabled
assert(!input.classList.contains('error'));
const button1 = TestUtils.findRenderedDOMComponentWithClass(
cxtForgot, 'primary-button'
);
assert(!button1.disabled);
the Simulate.change above can change the internal state of the component.
As for you question: "set the component state to some known value before executing the test", you can pass in different props to the component and have different tests for each scenario

Testing a component that depends on store from context

I'm having trouble setting up tests for a component that relies on a redux store passed down from context...
My app has a root component that gives its children the redux store as context:
import ApplicationStore from './app-store.js'
class Root extends Component {
getChildContext() {
return { store: ApplicationStore }
}
render(){...}
}
Root.childContextTypes = {
store: PropTypes.object,
};
I have a component that depends on the store (passed down from context):
class List extends Component {
render(){
const items = this.context.store.getState();
return items.map((_, i) => (
<div key={i}>{_.name}</div>
));
}
}
List.contextTypes = {
store: PropTypes.object,
};
So my question is: How to I "inject" the store object into my component's context? Would I have to unmock(./app-store.js)? Additionally, how can I pre-fill the store with a couple fixtures?
Thanks!
I know this does not answer your question, but I would like to point you to the React.js documentation on contexts:
Most applications will never need to use context. Especially if you are just getting started with React, you likely do not want to use context. Using context will make your code harder to understand because it makes the data flow less clear. It is similar to using global variables to pass state through your application.
and
Do not use context to pass your model data through components. Threading your data through the tree explicitly is much easier to understand. Using context makes your components more coupled and less reusable, because they behave differently depending on where they're rendered.
If you use props to pass your store (or even only the required parts of it) to your child components, then your problem is already solved because in your test, you can simply inject your mocked or stubbed store.