I have an angular component
<my-component foo="" bar=""></my-component>
And its corresponding class MyComponent
I use this component in my html
<my-component foo="bar" bar="foo"></my-component>
<my-component foo="baz" bar="qux"></my-component>
<my-component foo="bar" bar="baz"></my-component>
Now i want to querySelect my custom elements and access their attributes directly. I think about something like this:
List<MyComponent> mys = querySelector('my-component');
mys.forEach((my){
print(my.foo);
my.bar = '1234';
});
There are a view problems with that code:
querySelector always returns Element not MyComponent. can i cast Element to MyComponent?
Is MyComponent to <my-component> like DivElement to <div>?
querySelector cannot select custom elements. i could ad a class to every my-component and select it with that class. or is there another way?
I only can access the attributes with my.getAttribute() not with my.foo. I know, this is because my is still a Element not a MyComponent.
This is not officially supported. There was something like ng-element that allowed this as far as I remember but was intended to be only used for unit tests. There were some changes in the last versions but I don't know the current state.
You should pass references using dependency injection, Angular events (explained here How to communicate between Angular DART controllers) or the scope to access other elements.
Related
I have an AngularJS (1.7) app which is being migrated to Angular 7, using ngUpgrade. So the AngularJS and Angular frameworks are running at the same time, new components are written in Angular, and sometimes these modern Angular components are used inside of legacy AngularJS components.
In my case, the parent component needs to communicate with the child component in certain circumstances.
The ngUpgrade docs clearly show how to pass data and propagate events from the child Angular component to the parent AngularJS component:
<legacy-angularjs-component>
<div>{{ $legacyCtrl.whatever }}</div>
<modern-angular-child-component
[data]="$ctrl.initialDataForChildComponent"
(changed)="$ctrl.onChildComponentChanged($event)"
>
</modern-angular-child-component>
</legacy-angularjs-component>
To make that work, you just need to add a couple properties to the Angular child component: #Input() data; for the initial data, and #Output() changed = new EventEmitter<WhateverThing>(); that can then be used to propagate events to the parent component by doing this.changed.emit(this.whateverThing).
That all works, but what about propagating events from the parent to the child? I know how to do this in Angular, e.g. with #ViewChild or using observables, but those mechanisms are not available in my app's AngularJS environment. So the parent component cannot use them.
Two approaches I have tried that do work are:
Creating a separate service, which both components share.
Pass a reference to the parent controller into the child controller like this:
<legacy-angularjs-component>
<div>{{ $legacyCtrl.whatever }}</div>
<modern-angular-child-component
[observe]="$ctrl.registerObserver",
>
</modern-angular-child-component>
</legacy-angularjs-component>
...and then having the child component invoke this observe function:
ngOnInit() {
if (this.observe) {
// Pass reference to child component to the
// parent, so parent can directly send it messages.
this.observe(this);
}
}
This way, the parent has a direct reference to the child once the components are set up. The child implements some TypeScript interface that defines all the methods of the child that the parent can invoke to inform the child of events.
Both of those do work, but they both strike me as fairly kludgey and a lot of rigamarole to have to do for something as simple as sending an event to a child component.
Since this is easy to do in both AngularJS and Angular, I wondered if I might be missing an easier/simpler way to do the same thing in the context of ngUpgrade, where the parent is AngularJS and the child is Angular 7.
Or is my second approach a reasonable way to do it?
Well, as Eazy-E once said, "Ask, and ye shall immediately think of a simpler answer, right after you post the question in public."
A better solution than either of my first two mentioned above is to simply use regular Angular one-way bindings and use the OnChanges mechanism provided by Angular.
<legacy-angularjs-component>
<div>{{ $legacyCtrl.whatever }}</div>
<modern-angular-child-component
[observed]="$ctrl.valueThatChildIsObserving"
>
</modern-angular-child-component>
</legacy-angularjs-component>
Then, in the child component:
import { Component, Input, OnChanges, SimpleChanges } from '#angular/core';
export class MyChildCompoennt implements OnChanges {
#Input() observed: SomeWhateverObject;
ngOnChanges(changes: SimpleChanges) {
console.log(changes);
// Inspect changes (it contains old and new values
// for `observed` object. That object could be a string
// or something more complicated with multiple properties.\
}
}
This way, when the parent component wants to tell the child to do something, it can just do this.valueThatChildIsObserving = someWhatever;.
Angular will handle invoking the child componentngOnChanges(), passing it a structure containing the old and new value of the property.
You have to implement the child component so that it can inspect the changed value and execute the correct action, but that is pretty simple and avoids having to have the parent/child share references to each other.
I think this is a reasonably simple way to implement "propagate some kind of event from parent component to child component" in a hybrid AngularJS/Angular app using ngUpgrade.
I defined component of polymer.js 2.0.
and inside member function of this component, I would like to get current component content as dom object after dom ready. is this possbile?
How can I get dom object of current component after it attached to document?
Since any Custom Element is an HTML element, this is a reference to the DOM object. Custom Elements created with the Polymer library are no different in this regard.
All the methods and properties of the HTMLElement prototype are available inside member functions of a Polymer Element:
_onClick() {
const span = document.createElement('span');
span.innerText = 'hi there!';
this.appendChild(span);
}
Here is a simple JSFiddle for this example: https://jsfiddle.net/vlukashov/oyr5bpyk/
We are using a component that contains a form. We don't want to bind the fields to any specific model at initiation because we want to be able to use it against multiple interfaces.
We are using it as a multiple instance component in a parent componen, like the following example:
<component>
<foo1></foo1>
<foo2></foot2>
<component>
Assuming that the foo subcomponents contain a form with the field Name
From the parent component, we would like to bind (for example) the Name field in foo1 to object.foo1.form.name and object.foo2.form.name
But this has to happen in the ngAfterViewInit event.
The API says that there is a model property on the formcontrol directive. But can you access that property after post ngInit? If so, how?
I have a section of my view (html) that is generated programmatically by a viewmodel/class. This uses the Aurelia DOM (Aurelia Docs - pal :: Dom) functionality to generate and add the raw HTML elements to the view.
However, I am unable to get events within the generated html to call back to the viewmodel. An example:
let deleteButton = this.dom.createElement("button");
deleteButton.setAttribute("onclick", "cancelCreditNote(`${ row.creditNoteId }`)");
A click on the generated button won't call back to the viewmodel, which does have a cancelCreditNote function. Various other things like deleteButton.setAttribute("click.delegate", "cancelCreditNote('${ row.creditNoteId }')"); do not work either.
Does anyone know how to access a viewmodel class from essentiall 'raw' html in aurelia?
Unfortunately in this instance I cannot use the standard aurelia templating to generate the HTML.
The DOM property on PAL is just an abstraction for the browser's DOM object, create element is likely just calling document.createElement which doesn't afford any Aurelia binding to the created element.
You could try using aurelia.enhance(context, element) which takes an existing DOM element and runs it through the templating engine.
With this method you can also pass a binding context to apply to the element.
In my HTML I use this:
<div id="collapsesidebar" click.delegate="toggleSidebar()">
In my view-model I have this method:
toggleSidebar(){
alert('hi');
}
You could also do this from your view-model with JQuery like this:
attached() {
$('main').on('click', ()=> alert('hi'));
}
The last option is ONLY available áfter the attached() method is triggered: before that the binding needs to do its job and only after that the elements are located inside of the dom.
In other words: this will not work:
activate(){
$('main').on('click', ()=> alert('hi'));
}
because the constructor and the activate method both get fired before the attached method.
React Developer Tools give a lot of power to inspect the React component tree, and look at props, event handlers, etc. However, what I'd really like to do is to be able to inspect those data structures in the browser console.
In chrome I can play with the currently selected DOM element in the console using $0. Is there a way to extract React component info from $0, or is it possible to do something similar with the React Dev Tools?
Using React Developer Tools you can use $r to get a reference to the selected React Component.
The following screenshot shows you that I use React Developer Tools to select a component (Explorer) which has a state-object callednodeList. In the console I can now simply write $r.state.nodeList to reference this object in the state. Same works with the props (eg.: $r.props.path)
An answer to your question can be found here in a similar question I asked:
React - getting a component from a DOM element for debugging
I'm providing an answer here because I don't have the necessary reputation points in order to mark as duplicate or to comment above.
Basically, this is possible if you are using the development build of react because you can leverage the TestUtils to accomplish your goal.
You need to do only two things:
Statically store the root level component you got from React.render().
Create a global debug helper function that you can use in the console with $0 that accesses your static component.
So the code in the console might look something like:
> getComponent($0).props
The implementation of getComponent can use React.addons.TestUtils.findAllInRenderedTree to search for match by calling getDOMNode on all the found components and matching against the passed in element.
Open console (Firefox,Chrome) and locate any reactjs rendered DOM element or alternatively execute js script to locate it:
document.getElementById('ROOT')
Then check for element properties in object property viewer for attributes with name beginning like '__reactInternalInstace$....' expand _DebugOwner and see stateNode.
The found stateNode will contain (if it has) 'state' and 'props' attributes which is used heavily in reactjs app.
Though the accepted answer works, and is a great method, in 2020 you can now do a lot of inspection without using the $r method. The Components tab of React DevTools will show you props and detailed state when you select the relevant component (make sure you're on the right level), as well as let you do other things like suspend it or inspect the matching DOM element (little icons in the top right).
Assign the state or prop object to the window object:
window.title = this.state.title
And then from the dev tools console you can try different methods on the exposed object such as:
window.title.length
8
You can attach a reference to the window object like
import { useSelector } from "react-redux";
function App() {
// Development only
window.store = useSelector((state) => state);
return (
<div className="App">
</div>
);
}
export default App;
Then access it from the console
store
{states: {…}}
states:
someProperty: false
[[Prototype]]: Object
[[Prototype]]: Object
[Console][1]
[1]: https://i.stack.imgur.com/A4agJ.png