In my dusk test I want to firstly add and then delete news. Each news has .delete-news class but on the screen I have multiple elements. Each .delete-news class has in it's path data attribute with it's id data-newsid="id". Now the browser does not know which delete-news class it should to click. How should I manage that?
Probably I should take delete-news class with the biggest data-newsid attribute. But I don't know how I should check it.
Currently I'm deleting it like this:
public function testRemoveNews() {
$this->browse(function ($browser) {
$browser->visit('/')
->press('.delete_news')
->press('Yes')
->waitForText('News has been deleted!')
->press('OK')
->assertDontSee('Title of the news');
});
}
If you sorting your news by 'id' desc, maybe you should try
->press('.delete-news:first') // or :nth-child(1)
or add a dusk attribute to first news element, like dusk="last-news", and call it with:
->press('#last-news')
Related
I have an Angular component that generates mat-checkbox dynamically at runtime and I need to change the individual background of each checkbox differently with different color and I don't (won't) have the information before hand, only available at runtime.
I have the following ng-template for the checkboxes:
<ng-template #renderCheckbox let-id="id" let-attr="attr">
<mat-checkbox
[checked]="attr.show"
[color]="'custom-' + id"
(change)="onChange($event.checked, attr)">
{{attr.name}}
</mat-checkbox>
</ng-template>
where, attr in the template has the following interface type, these infomation are pulled from Highcharts' series and I didn't want to hardcode the color.
interface LinkedSeriesAttributes {
id: string;
name: string;
index: number;
color: string;
checked: boolean;
}
Since there is no way to create css classes before hand and there is no way to directly apply color to the mat-checkbox, I could only generate the <style>...</style> right at the beginning of my template.
In my component, I have code that will generate the style which would give me something like this:
.mat-checkbox.mat-custom-hello.mat-checkbox-checked .mat-checkbox-background::before {
color: #6E8BC3 !important;
}
.mat-checkbox.mat-custom-world.mat-checkbox-checked .mat-checkbox-background::before {
color: #9ED6F2 !important;
}
...
However, I tried various ways to dump it inside <style> without success. I tried:
<style>{{ dynamicCSSStyles }}</style>
Which, my IDE shows that's an error with the curly braces, although it compiled fine and ran without errors, I got nothing, can't even see the <style> tag.
I also tried to include <style> inside my dynamicCSSStyles variable, and angular just dumped the whole thing out as text...
What's the correct way to generate a <style> in Angular.
I've found a REALLY dirty way of "making this work" but it causes Angular to keep adding the <style> back into the DOM.
First, set encapsulation to ViewEncapsulation.None.
Second, create a function to generate the <style> tag the old fashion way with an id:
updateDynsmicStyleNode() {
const id = 'dynamic-css-styles';
const nativeElm = this.elmRef.nativeElement;
const existing = nativeElm.querySelector(`style#${id}`);
if (!existing) {
const styleTag = document.createElement('style');
styleTag.setAttribute('id', id);
styleTag.innerHTML = this.dynamicCSSStyles;
nativeElm.prepend(styleTag);
} else {
existing.innerHTML = this.dynamicCSSStyles;
}
}
Third, call our function in ngAfterViewChecked:
ngAfterViewChecked() {
this.updateDynsmicStyleNode();
}
I mean while this worked, it is really bad, since moving the mouse around the screen would cause Angular to just continuously reinsert the <style> tag.
Does anyone know some other way more legit to archive this? LOL
You can use ngClass or [class] attribute. Since you can have the styles ready from the component.ts file.
You can do something like this:
Way 1: If you already know what the dynamic ids might be, (like if it always will be 'hello' and 'world')
let dynamicClasses = {};
// Once you get some classes from your logic, you can add them to the object above
dynamicClasses['hello'] = 'custom-hello';
dynamicClasses['world'] = 'custom-world';
// Then in HTML
<mat-checkbox [ngClass]="dynamicClasses"></mat-checkbox>
Way 2: If you dont know what the classes also might be, like if its not always be hello or world, then create a method and call it where required, you might need to do something similar to #codenamezero said.
I have 20+ elements, which all should use the same class (animate.css)
It is super annoying to change all classes if I want to edit the animation, so I saved the animation class in my service in a variable:
animClassSecond = "animate__animated animate__bounceInUp";
But I cant figure out how to add it to [ngClass], this does not work:
[ngClass]="{'select_elem':true, 'btn_2':true, 'dataService.animClassSecond':true}"
[ngClass]="{'select_elem':true, 'btn_2':true, 'this.dataService.animClassSecond':true}"
[ngClass]="{'select_elem':true, 'btn_2':true, this.dataService.animClassSecond:true}"
[ngClass]="{'select_elem':true, 'btn_2':true, dataService.animClassSecond:true}"
Its either a template error or it does not resolve to the variable. Any ideas?
P.S.: Adding a second [ngClass] attribute also does not work, because the first one is ignored.
is:
[ngClass]="dataService.animClassSecond"
But remember that you need declare the service public in the constructor
constructor(public dataService:DataService){}
NOTE you can use class and [ngClass] in the same tag:
class="select_elem btn_2" [ngClass]="dataService.animClassSecond"
This is probably not achievable in the template since the Angular template language is quite limited.
Just move the logic of ngClass object into your component.ts. There you can use all TypeScript's power
ngOnInit() {
this.ngClassObj = { [dataService.animClassSecond]: true };
}
or if you need it to be dynamic (use this one carefully because it might become a performance issue)
get ngClassObj() {
return { [dataService.animClassSecond]: true };
}
and then
[ngClass]="ngClassObj"
There is a very useful pattern in react called the renderProps pattern (https://reactjs.org/docs/render-props.html) but I'm not sure if this is possible with lit-elements, due to the way the shadow dom isolates the css (meaning any css defined on the renderProp won't be carried into the shadow dom of the component with the renderProp).
Has anyone found a way around this, or a different pattern that enables the same use case as the renderProps pattern ?
Thanks !
EDIT: Here is an example that might make it clearer. Let's imagine a hover-menu component whose job is to display a menu on hover. This menu might need to know the position of the element hovered. And we obviously want to be able to render whatever we want inside it.
So we would like to be able to do something like that (renderMenuContent is a renderProp).
<hover-menu
.renderMenuContent="${(boundingClientRect) =>
html`<div>my menu content which could be positioned using ${JSON.stringify(boundingClientRect)}</div>`
}"
></hover-menu>
Turns out there is indeed no such easy solution as in React, again due to the isolation of the shadow dom.
The best solution is to create a component and use it in the renderProp (this way it can manage its own css classes).
In our example:
<hover-menu
.renderMenuContent="${(boundingClientRect) =>
html`<my-menu-content .boundingClientRect="${boundingClientRect}"></my-menu-content>`
}"
></hover-menu>
class MyMenuContent extends LitElement {
static get properties() {
return { boundingClientRect: { type: Object } };
}
static get styles() {
return css`.my-container { color: red }`;
}
render() {
return html`<div class="my-container">
can be positioned using ${JSON.stringify(this.boundingClientRect)}
</div>`;
}
}
what's the advantage of using:
this.refs.id.value
versus using:
document.getElementById('newID').value
is it just shorter syntax or is something else going on?
The benefit is using ref has good reusability and scope. Because ref only stay in this component, when you manipulate this ref, it won't interfere other component.
If you use id, it has duplicate problem. Think about this component.
class Demo extends React.Component {
componentDidMount() {
document.getElementById('demo').style.color = this.props.color;
}
render() {
return (
<div id="demo">
content
</div>
);
}
}
class Main exntends React.Component {
render() {
return (
<div>
<Demo color="red"/>
<Demo color="green"/>
<Demo color="blue"/>
</div>
)
}
}
If use id to change the style property, all the <Demo/> text color will become blue(the last declaration).
But if you use ref, you can promise the manipulation keeps in this component.
I also find there is a similar question:
What is the difference with using ref and document.findElementById, ect when using ReactJS?
Refs can work like HTML IDs however you do not suffer the re-usability problem. In HTML once you set ID of an element. You cannot re use your component again for the element you are trying to get by ID is totally unique. Refs allow you to specify a reference instead of an ID, this reference allows you to well, reference the current element being referred to. Using IDs the browser will only ever get the first in the series of qualified nodes. Take the following example:
class YourComponent extends React.Component {
onBlur() {
this.refs.referenceValue.setAttribute('class', '');
}
render() {
return (
<div>
<input ref="referenceValue" onBlur={this.onBlur.bind(this)}/>
</div>
);
}
}
This way, you can have multiple components with the same reference If you were to attempt to achieve this using IDs you will come to obstacles quite soon!
can any master of jsoup tell me some suggestions to filter html to text/string? I've tried calling text() of Document. But all tags/elements will be filtered. My aim is to filter some specified tags.
i.e: I've html text like:
<div>hello<p>world</div>,<table><tr><td>xxx</td></tr>
to get result:
<div>hello<p>world</div>,xxx
which has filtered tags.
I can't test this right now but I think you want to write a recursive function that steps through the tree and prints each node based on a condition. The following is an example of what it might look like but I expect that you will have to modify it to suit your needs more precisely.
Document doc = JSoup.parse(page_text);
recursive_print(doc.head());
recursive_print(doc.body());
...
private static Set<String> ignore = new HashSet<String>(){{
add("table");
...
}};
public static void recursive_print(Element el){
if(!ignore.contains(el.className()))
System.out.println(el.html());
for(Element child : el.children())
recursive_print(child);
}
You can use Whitelist to achieve this goal. For example:
Whitelist whiteList = new Whitelist();
whiteList.addTags("div", "p", "td");
It means that all other tags will be removed.