Sorry for the title, I don't know how to explain it.
I have an item like this :
<div class="faq__item">
<div class="faq__item-header"></div>
</div>
When I click on faq__item, I have an active class active
So in my scss, I want to style the header without selecting the whole child selector and keep the ampersand
.faq__item {
&.active{
.faq__item-header{ => I would like to avoid this
}
}
}
I don't know if I'm clear, I'm pretty sure I can optimise the code, but I don't know what to search
Store the parent selector in a variable and use it like so:
.faq__item {
$parent: &;
&.active {
#{$parent}-header {
/* rules here */
}
}
}
Related
i'm new to nightwatch and was wondering if there's any good way to select the inner element of a current element and then get the text? Assuming i have the following..and i'm trying to retrieve the text inside (a) tags of each (li).
so i would like to get 'text to retrieve' and 'text to retrieve 2'.
...
<div class="mywrapperhere">
<ul>
<li>
<a>.....
<div>text to retrieve</div>
</a>
</li>
<li>
<a>.....
<div>text to retrieve 2</div>
</a>
</li>
<li>...
...
</div>
I'm thinking along these lines..
module.exports = {
'Demo test 1' : function (browser) {
....
//some sort of selector then gets from the anchor list
...'.mywrapperhere li a') : {
..
//for each element of the anchor..
{
//is there anyway to get it through something like
element.('div').innerHTML eg..
//or am i forced to use browser.execute( ...getElementsByTag method
//to achieve this?
}
}
browser.end();
}
};
Looking at the nightwatch api, i couldn't find anything allows me to do that. I'm particularly looking at the 'Element State' examples that doesn't seem to have a way for me to select the current element state's child element :
http://nightwatchjs.org/api/elementIdAttribute.html
The reason why i had to loop through the anchor tag level is because i'll need to retrieve a few more data besides the one from div tag, thanks!
You can use elementIdElement and elementIdText to get text from a child element. First you can get all the li elements by using .elements(). Then you use elementIdElement to get a child element. Then you can use elementIdText to get the text of this child element. Here is an example that will allow you to get the text of both list items in your snippet and log the values to the console.
browser.elements('css selector', 'li', function(listItems) {
listItems.value.forEach(function(listItem) {
browser.elementIdElement(listItem.ELEMENT, 'css selector', 'a', function(anchor) {
browser.elementIdText(anchor.ELEMENT, function(text) {
console.log(text.value);
});
});
}, browser); //have to pass in browser for scoping
});
I have some data that is accessible via:
{{ content['term_goes_here'] }}
... and this evaluated to either true or false. I'd like to add a class depending on the truthiness of the expression like so:
<i class="fa" v-bind:class="[{{content['cravings']}} ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline']"></i>
where true gives me the class fa-checkbox-marked and false would give me fa-checkbox-blank-outline. The way I wrote it above gives me an error:
- invalid expression: v-bind:class="[{{content['cravings']}} ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline']"
How should I write it to be able to conditionally determine the class?
Use the object syntax.
v-bind:class="{'fa-checkbox-marked': content['cravings'], 'fa-checkbox-blank-outline': !content['cravings']}"
When the object gets more complicated, extract it into a method.
v-bind:class="getClass()"
methods:{
getClass(){
return {
'fa-checkbox-marked': this.content['cravings'],
'fa-checkbox-blank-outline': !this.content['cravings']}
}
}
Finally, you could make this work for any content property like this.
v-bind:class="getClass('cravings')"
methods:{
getClass(property){
return {
'fa-checkbox-marked': this.content[property],
'fa-checkbox-blank-outline': !this.content[property]
}
}
}
<i class="fa" v-bind:class="cravings"></i>
and add in computed :
computed: {
cravings: function() {
return this.content['cravings'] ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline';
}
}
Why not pass an object to v-bind:class to dynamically toggle the class:
<div v-bind:class="{ disabled: order.cancelled_at }"></div>
This is what is recommended by the Vue docs.
the problem is blade, try this
<i class="fa" v-bind:class="['{{content['cravings']}}' ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline']"></i>
You could use string template like with backticks `` :
:class="`${content['cravings'] ? 'fa-checkbox-marked' : 'fa-checkbox-blank-outline'}`"
if you want to apply separate css classes for same element with conditions in Vue.js
you can use the below given method.it worked in my scenario.
html
<div class="Main" v-bind:class="{ Sub: page}" >
in here, Main and Sub are two different class names for same div element.
v-bind:class directive is used to bind the sub class in here.
page is the property we use to update the classes when it's value changed.
js
data:{
page : true;
}
here we can apply a condition if we needed.
so, if the page property becomes true element will go with Main and Sub claases css styles. but if false only Main class css styles will be applied.
The HTML DOM structure I have is, sort of, repetitive. Is there any good practice to refactor my sass rules?
HTML:
<div name="OpportunityDetailView" class="actor-wrapper"><div name="OpportunityDetailView" class="detail-view expansion-bottom-normal">...</div></div>
<div name="ApplicationDetailView" class="actor-wrapper"><div name="ApplicationDetailView" class="detail-view expansion-bottom-normal">...</div></div>
<div name="ProfileDetailView" class="actor-wrapper"><div name="ProfileDetailView" class="detail-view expansion-bottom-normal">...</div></div>
SCSS case 1:
div[name="OpportunityDetailView"] > div[name="OpportunityDetailView"],
div[name="ApplicationDetailView"] > div[name="ApplicationDetailView"],
div[name="ProfileDetailView"] > div[name="ProfileDetailView"],
{
css rules...
}
SCSS case 2:
.section {
div[name="Business_Image_Url__c"],
div[name="Name"],
div[name="Account_Name__c"],
div[name="Business_Type__c"],
div[name="Region_Province__c"] {
.label{
display: none !important;
}
}
If I'm not missing something, it seems like you can use the classes assigned to the elements to achieve what you want:
For case 1:
.actor-wrapper {
.detail-view {
//css code goes here
}
}
and please provide some markup for the case 2.
If all the div[name] elements share the same class, then you can group them by that class, as done with the above example.
Is it possible to render static HTML fragments without having an associated view-model in Aurelia? For instance, I have a typical header, body, footer layout. In body, I have the router view. There are a set of links in the footer such as FAQs upon clicking which I want to render a view in the body area.
When I try to define a route config for the faq route, the config is expecting one of You must specify a "moduleId:", "redirect:", "navigationStrategy:", or "viewPorts:".
The temporary work around that I have is to create a passthrough view model that doesn't do anything. This is resulting in a bunch of passthrough view model classes. I am sure I am doing something wrong.
I couldn't find any help online with this use-case. Any references will be highly appreciated.
It seems like you're looking for an inlineView("<your html here/>") type of functionality for routes so that navigating to the target route will directly render the HTML in the router-view element.
This is not directly possible with aurelia-router because without a ViewModel, no ActivationStrategy can be invoked. Aurelia-router wants to call canActivate, activate, canDeactivate, deactivate on something.
However, if you simply want to define markup programmatically, and you don't want to declare a ViewModel for each individual piece of markup, then that can be solved quite neatly with the compose element in combination with inlineViewStrategy.
With this approach, you only need one View/ViewModel pair which is responsible for retrieving the correct HTML based on the current route, and then render that HTML.
There are also other ways to do this, but AFAIK this approach involves the least amount of plumbing.
Of course you also need an object to store the HTML/route pairs in, and a service to store/retrieve those objects.
You can see a live working version here (including a few comments to clarify things):
https://gist.run/?id=8c7e02ce1ee0e25d966fea33b826fe10
app.js
import { inject } from "aurelia-framework";
import { Router } from "aurelia-router";
import { FaqService } from "./faq-service";
#inject(Router, FaqService)
export class App {
constructor(router, faqService) {
router.configure(config => {
config.map({ route: "", moduleId: "./empty" });
config.map({ route: "faq/:route", moduleId: "./faq-detail" });
});
this.router = router;
this.faqService = faqService;
}
openFaq(item) {
this.router.navigate(`faq/${item.route}`);
}
}
app.html
<template>
<router-view></router-view>
<ul>
<li repeat.for="item of faqService.faqItems" click.delegate="openFaq(item)">
${item.title}
</li>
</ul>
</template>
empty.js (just a convenience for default empty route):
import { inlineView } from "aurelia-framework";
#inlineView("<template>no content</template>")
export class Empty {}
faq-service.js
import { singleton } from "aurelia-framework";
class FaqItem {
constructor(route, title, markup) {
this.route = route;
this.title = title;
this.markup = markup;
}
}
#singleton(false)
export class FaqService {
constructor() {
this.faqItems = [
new FaqItem("question-1", "Question 1", "<h4>Question 1</h4><p>Answer 1</p>"),
new FaqItem("question-2", "Question 2", "<h4>Question 2</h4><p>Answer 2</p>"),
new FaqItem("question-3", "Question 3", "<h4>Question 3</h4><p>Answer 3</p>")
];
}
getByRoute(route) {
return this.faqItems.find(i => i.route === route);
}
}
faq-detail.js
import { inject, InlineViewStrategy } from "aurelia-framework";
import { FaqService } from "./faq-service";
#inject(FaqService)
export class FaqDetail {
constructor(service) {
this.service = service;
}
activate(param) {
let item = this.service.getByRoute(param.route);
this.viewStrategy = new InlineViewStrategy(`<template>${item.markup}</template>`)
}
}
faq-detail.html
<template>
<compose view.bind="viewStrategy"></compose>
</template>
In Angular 1 I have written a custom directive ("repeater-ready") to use with ng-repeat to invoke a callback method when the iteration has been completed:
if ($scope.$last === true)
{
$timeout(() =>
{
$scope.$parent.$parent.$eval(someCallbackMethod);
});
}
Usage in markup:
<li ng-repeat="item in vm.Items track by item.Identifier"
repeater-ready="vm.CallThisWhenNgRepeatHasFinished()">
How can I achieve a similar functionality with ngFor in Angular 2?
You can use #ViewChildren for that purpose
#Component({
selector: 'my-app',
template: `
<ul *ngIf="!isHidden">
<li #allTheseThings *ngFor="let i of items; let last = last">{{i}}</li>
</ul>
<br>
<button (click)="items.push('another')">Add Another</button>
<button (click)="isHidden = !isHidden">{{isHidden ? 'Show' : 'Hide'}}</button>
`,
})
export class App {
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
#ViewChildren('allTheseThings') things: QueryList<any>;
ngAfterViewInit() {
this.things.changes.subscribe(t => {
this.ngForRendred();
})
}
ngForRendred() {
console.log('NgFor is Rendered');
}
}
origional Answer is here
https://stackoverflow.com/a/37088348/5700401
You can use something like this (ngFor local variables):
<li *ngFor="#item in Items; #last = last" [ready]="last ? false : true">
Then you can Intercept input property changes with a setter
#Input()
set ready(isReady: boolean) {
if (isReady) someCallbackMethod();
}
For me works in Angular2 using Typescript.
<li *ngFor="let item in Items; let last = last">
...
<span *ngIf="last">{{ngForCallback()}}</span>
</li>
Then you can handle using this function
public ngForCallback() {
...
}
The solution is quite trivial. If you need to know when ngFor completes printing all the DOM elements to the browser window, do the following:
1. Add a placeholder
Add a placeholder for the content being printed:
<div *ngIf="!contentPrinted">Rendering content...</div>
2. Add a container
Create a container with display: none for the content. When all items are printed, do display: block. contentPrinted is a component flag property, which defaults to false:
<ul [class.visible]="contentPrinted">
...items
</ul>
3. Create a callback method
Add onContentPrinted() to the component, which disables itself after ngFor completes:
onContentPrinted() {
this.contentPrinted = true;
this.changeDetector.detectChanges();
}
And don't forget to use ChangeDetectorRef to avoid ExpressionChangedAfterItHasBeenCheckedError.
4. Use ngFor last value
Declare last variable on ngFor. Use it inside li to run a method when this item is the last one:
<li *ngFor="let item of items; let last = last">
...
<ng-container *ngIf="last && !contentPrinted">
{{ onContentPrinted() }}
</ng-container>
<li>
Use contentPrinted component flag property to run onContentPrinted() only once.
Use ng-container to make no impact on the layout.
Instead of [ready], use [attr.ready] like below
<li *ngFor="#item in Items; #last = last" [attr.ready]="last ? false : true">
I found in RC3 the accepted answer doesn't work. However, I have found a way to deal with this. For me, I need to know when ngFor has finished to run the MDL componentHandler to upgrade the components.
First you will need a directive.
upgradeComponents.directive.ts
import { Directive, ElementRef, Input } from '#angular/core';
declare var componentHandler : any;
#Directive({ selector: '[upgrade-components]' })
export class UpgradeComponentsDirective{
#Input('upgrade-components')
set upgradeComponents(upgrade : boolean){
if(upgrade) componentHandler.upgradeAllRegistered();
}
}
Next import this into your component and add it to the directives
import {UpgradeComponentsDirective} from './upgradeComponents.directive';
#Component({
templateUrl: 'templates/mytemplate.html',
directives: [UpgradeComponentsDirective]
})
Now in the HTML set the "upgrade-components" attribute to true.
<div *ngFor='let item of items;let last=last' [upgrade-components]="last ? true : false">
When this attribute is set to true, it will run the method under the #Input() declaration. In my case it runs componentHandler.upgradeAllRegistered(). However, it could be used for anything of your choosing. By binding to the 'last' property of the ngFor statement, this will run when it is finished.
You will not need to use [attr.upgrade-components] even though this is not a native attribute due to it now being a bonafide directive.
I write a demo for this issue. The theory is based on the accepted answer but this answer is not complete because the li should be a custom component which can accept a ready input.
I write a complete demo for this issue.
Define a new component:
import {Component, Input, OnInit} from '#angular/core';
#Component({
selector: 'app-li-ready',
templateUrl: './li-ready.component.html',
styleUrls: ['./li-ready.component.css']
})
export class LiReadyComponent implements OnInit {
items: string[] = [];
#Input() item;
constructor() { }
ngOnInit(): void {
console.log('LiReadyComponent');
}
#Input()
set ready(isReady: boolean) {
if (isReady) {
console.log('===isReady!');
}
}
}
template
{{item}}
usage in the app component
<app-li-ready *ngFor="let item of items; let last1 = last;" [ready]="last1" [item]="item"></app-li-ready>
You will see the log in the console will print all the item string and then print the isReady.
I haven't yet looked in depth of how ngFor renders elements under the hood. But from observation, I've noticed it often tends to evaluate expressions more than once per each item it's iterating.
This causes any typescript method call made when checking ngFor 'last' variable to get, sometimes, triggered more than once.
To guarantee a one call to your typescript method by ngFor when it properly finishes iterating through items, you need to add a small protection against the multiple expression re-evaluation that ngFor does under the hood.
Here is one way to do it (via a directive), hope it helps:
The directive code
import { Directive, OnDestroy, Input, AfterViewInit } from '#angular/core';
#Directive({
selector: '[callback]'
})
export class CallbackDirective implements AfterViewInit, OnDestroy {
is_init:boolean = false;
called:boolean = false;
#Input('callback') callback:()=>any;
constructor() { }
ngAfterViewInit():void{
this.is_init = true;
}
ngOnDestroy():void {
this.is_init = false;
this.called = false;
}
#Input('callback-condition')
set condition(value: any) {
if (value==false || this.called) return;
// in case callback-condition is set prior ngAfterViewInit is called
if (!this.is_init) {
setTimeout(()=>this.condition = value, 50);
return;
}
if (this.callback) {
this.callback();
this.called = true;
}
else console.error("callback is null");
}
}
After declaring the above directive in your module (assuming you know how to do so, if not, ask and I'll hopefully update this with a code snippet), here is how to use the directive with ngFor:
<li *ngFor="let item of some_list;let last = last;" [callback]="doSomething" [callback-condition]="last">{{item}}</li>
'doSomething' is the method name in your TypeScript file that you want to call when ngFor finishes iterating through items.
Note: 'doSomething' doesn't have brackets '()' here as we're just passing a reference to the typescript method and not actually calling it here.
And finally here is how 'doSomething' method looks like in your typescript file:
public doSomething=()=> {
console.log("triggered from the directive's parent component when ngFor finishes iterating");
}