My code is here - https://codesandbox.io/s/92xmm6zvmo
The idea is that the user clicks on an Edit link in a table, the row expands and a textarea opens in the expanded row so that the user can edit their JSON in it. This is all working great, except for the onChange event handler of the textarea. When the event fires it seems to be missing a reference to this, and is unable to bind an appropriate event handler. I don't understand why this is the case, I would expect there to be a reference to the ExpandableTable component available as the expandedRowRender() method belongs to that class.
The other form fields (that are not dynamically added) appear to be working fine.
Showing your code make things easier.
I think it's working now.
https://codesandbox.io/s/wkym185vpl
The problem is, you were not binding correctly the "this" context. if you are going to use arrow function, make sure all class functions are:
Class MyClass{
expandedRowRender = () => {}
}
if you are doing expandedRowRender(){} make sure bind it in the constructor.
Class MyClass{
constructor(){
this.expandedRowRender = this.expandedRowRender.bind(this);
}
...
expandedRowRender(){}
}
Related
Try to add a handler to my Node, React button component, to insert a row in DB, but I got an error, though I am just copying code from an example tutorial. What is wrong?
The method handlePost is not defined in class Button. But you are specifying it as this.handlePost. Move the method inside class Button.
I was wondering is it a good practice to bind constant arguments to method in constructor or is there some better alternatives?
f.e.
constructor() {
super();
this.openModalWithFirstTabActive = this.openModal.bind(this, TAB.FIRST);
this.openModalWithSecondTabActive = this.openModal.bind(this, TAB.SECOND);
}
openModal(tabId) {
const {
openModal,
} = this.props;
/*
lots of logic and stuff
*/
openModal({
activeTabId: tabId,
...restProps,
});
}
EDIT
I'll provided more code and will describe my usage case for better understanding.
I've updated with openModal method, in my usecase it's much larger and has more logic in it.
So, i have a modal, in which there's two tabs. I have two buttons, open pressing button 1 modal needs to open and first tab needs to be active, upon pressing button 2 modal needs to open and second tab needs to be active.
I bind two methods in constructor which both call this.openModal only with different activeTabId specified. This avoids code duplication and arrow functions.
So my question would be is there any better alternative to this?
There is no point in binding a constant value to the function since they can directly be accessed inside the method. Also, since the binded arguments are the first to a function it might lead to confusion when debugging or handling the logic in the methods
When you use bind in constructor, it would lead to two instances of the function being created which is alright and won't affect the performance.
In case you have scenarios where you need to pass dynamic value to the methods without using bind in render, you can refer the following post
How to avoid bind or inline arrow functions inside render method
I have an AngularJS custom component that essentially wraps around a Material Design mdSelect, but provides easy configuration of available, default, and current values via its bindings.
But the component functions as a general editing component in an mdDialog that can change its options based upon the thing being edited. Thus is a "Next" button to go to the next "thing" to be edited. When the button is pressed the custom component will have new available, default, and current values—something like this:
<foo-component default-value="dialog.getDefaultFoo()" current-foo="dialog.currentFoo">
</foo-component>
Note that the component, if a list of available values is not given (as in the example above), the component assumes a list of values with only one value, the "default-value" indicated.
So when a user selects "Next", the list of values in the mdSelect will change, because the value returned by dialog.getDefaultFoo(). The new selected value will be dialog.currentFoo.
But if dialog.currentFoo is null, I want the control to automatically select the indicated default value, or if no default is indicated, the first available value. That's easy enough when the component is created using $onInit. But once it is created, how do I know (inside the component) that the user has selected "Next" and the list of available values has changed?
In the code for the "Next" button, I call this.fooForm.$setPristine(). According to the documentation, the when this method is called the form controller will "propagate to all the controls contained in this form." So I considered having my custom control hook detect that $setPristine() is being called, so that it can automatically select a default value from the list if the new value is null. But how now I'm back in the same situation: how does my custom component detect that $setPristine() is being called on the form?
In essence, my custom component needs to detect when one of its bound values changes, and perform some custom update of other values under certain conditions. I know that I can use a getter/settter from outside the custom component, but how does the custom component detect that one of its bound values has changed?
To make matters more complicated, dialog.currentFoo is actually a function, which my component recognizes as a getter/setter function which will return/update the correct value based upon the state of the dialog. So I can't even detect that this value has changed, because the actual function never changes—only the value that it returns will change.
And it's actually even more complicated than that, because the mdSelect is only one piece of the object that gets sent to dialog.currentFoo; otherwise it isn't propagated outside the component.
Trying to summarize, I need to know in a custom component if the binding dialog.currentFoo, which is really a getter/setter method, would now return null so that the custom component could select a default value (also dynamic) based upon the current items (also dynamic) listed in the internal mdSelect. I would accept workarounds, such as detecting that $setPristine() has been called on the enclosing form. I would even accept a hack, such as forcing AngularJS to recreate the custom component when some external state changes.
This is tricky, because AngularJS is tricky, and because it's exceedingly hard to track information on custom AngularJS control/input components.
Above all the first thing that needs to be done is to make the custom component use the normal ngModel framework; trying to "fake" it using a two-way bound currentValue doesn't cut it for this complexity, and besides, ngModel is the "correct" AngularJS way to do it. To pull this off you'll need to use NgModelController. Finding out how to do this with a component is difficult in itself, although one page gave the magic formula. It goes something like this:
require: {
ngModelCtrl: "ngModel"
},
bindings: {
ngModel: "<"
//TODO add defaultValue or whatever other properties here
},
Then you don't access the two-way value directly; you use the NgModelController interface:
//get the current value
const foo = this.ngModelCtrl.$modelValue;
//set the current value
this.ngModelCtrl.$setViewValue(foo);
As best I can tell, if you add ng-model-options="{getterSetter:true}" to the component, the ngModelOptions will automatically get applied to your model, and getters/setters will be called automatically.
Now you are hooked into the whole ngModel framework, and use NgModelController to add/access validators and all sorts of advanced features.
Getting back to my needs, where I need to "reset the component" in certain conditions, I can patch into NgModelController#$setPristine(). The form controller recognizes my component is part of the ngModel framework, and calls $setPristine() when the form is reset to its pristine state.
this.$onInit = () => {
this.ngModelCtrl.$setPristine = () => {
myInternalValue1 = null;
myInternalValue2 = this.defaultValue;
};
this.ngModelCtrl.$setPristine(); //initialize default value
};
This explains the doubt I had about the form controller "propagat[ing $setPristine()] to all the controls contained in this form", which I mentioned in my original question. The secret is that the component must become part of the real ngModel system so that it can interact as AngularJS is expecting, as well as to gain access to important internal methods.
I'm running into a weird case that only seems to happen upon first loading a component on a heavily based component page (loading 30+ components).
#Component{
selector: <parent-component>
template: `<child-component [myObject]=myObject>
}
export class ParentComponent {
private myObject:DTOValue;
constructor(service:MyService){
service.getDTOValue().subscribe((dtoValue:DTOValue) => {
this.myObject = dtoValue;
});
}
}
#Component{
selector: <child-component>
template: `<div></div>
}
export class ChildComponent {
#Input set myObject(value:DTOValue) => {//do something};
constructor(){
}
}
In this code, the Parent is going to get a value to a child as an input. This value comes from a request at a later time, so when the child is first initialized, the input could be undefined. When the value does get returned from the request and is set on the variable myObject, I'd expect that the child component would receive an input event being triggered. However, due to the timing, it seems like this is not always the case, especially when I first load a page that contains a lot of files being loaded.
In the case that the child component doesn't receive the input, if I click else where on my page, it seems to now trigger the change detection and will get the input value.
The 2 possible solutions I can think of that would require some large code changes so I want to make sure I choose the right now before implement them.
Change the input to be an Subject, so that I push the input value which should ensure that a correct event is triggered(this seems like overkill).
Use the dynamic loader to load the component when the request as return with the correct value (also seems like overkill).
UPDATE:
Adding a plnker: http://plnkr.co/edit/1bUelmPFjwPDjUBDC4vb, you can see in here that the title seems to never get its data bindings applied.
Any suggestions would be appreciated.
Thanks!
If you can identify where the problem is and appropriate lifecycle hook where you could solve it, you can let Angular know using ChangeDetectorRef.
constructor(private _ref: ChangeDetectorRef)
method_where_changes_are_overlooked() {
do_something();
// tell angular to force change detection
this._ref.markForCheck();
}
I had a similar issue, only with router - it needed to do redirect when/if API server goes offline. I solved it by marking routerOnActivate() for check...
When you trigger change detection this way a "branch" of a component tree is marked for change detection, from this component to the app root. You can watch this talk by Victor Savkin about this subject...
Apologize, the issue ended up being my interaction with jQuery. When I triggered an event for a component to be loaded, inside of the jQuery code, it wouldn't trigger the life cycle. The fix was after the code was loaded to then call for a change detection.
I initialize CKEditor in a React component like this:
render: () ->
<div ref="editable" dangerouslySetInnerHTML={{__html: #props.html}} />
componentDidMount: () ->
#editor = AlloyEditor.editable(#refs.editable, {extraPlugins: AlloyEditor.Core.ATTRS.extraPlugins.value + 'plugins go here'})
#editor.get('nativeEditor').on('blur', (event) =>
#props.handleChange())
//At this point the #props.html changes and the component is re-rendered
I define widgets following this tutorial. The appropriate html elements are properly recognized as widgets and everything works fine until the 'blur' event is fired. After the component is re-rendered, the widgets' upcast functions don't get invoked again and the html elements don't become widgets any longer.
I thought that I could use methods such as checkWidgets and initOnAll in the componentDidUpdate method, but they only work for widget candidates with .cke_widget_new class. As far as I understand, the upcasting happens at the data (html) processing stage, and it looks like the editor doesn't process html again after DOM was re-rendered. What can I do about this? Thanks!
Update: Eventually oleq's solution worked for me. I added:
componentDidUpdate: () ->
data = #editor.get('nativeEditor').getData()
#editor.get('nativeEditor').setData(data)
The only problem is that this (and probably any other) solution doesn't work with the blur event, which was a bad event choice, because it is also triggered right before the user adds a widget by pressing a button. I replaced this event with another one that seems to work for me at least for now.
You can use editor.widgets.checkWidgets() as documented here:
https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_plugins_widget_repository.html
The general idea is that it checks all the widgets in the document and refreshes ones that have not been made widgets yet.
OR if you only want to init one widget that you have a reference to, do it with initOn() documented on the same page.