Programmatically bind CSS classes to a kendo-dropdownlist? - kendo-ui-angular2

I want to bind the kendo-dropdownlist to a specific class. Essentially I need to change the appearance of the control based on certain state of the form (e.g. error, required, etc). The logic in the model determines which classes are applied to the control.
If the model is 'error-state' then add CSS to put a box around it, if required, change border to a different state, and other business rules.
How do I programmatically bind CSS classes to a kendo-dropdownlist?
I have tried
[ngClass]="class_list_in_model"
-- and --
class="class_list_in_model"
For my text box input controls I am using [ngClass]="someproperty_in_model" and that works.

You can use an [ngClass] binding for the <kendo-dropdownlist> component:
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `
<kendo-dropdownlist [ngClass]="ddl_state" [data]="listItems">
</kendo-dropdownlist>
`,
styles: [".error-state { box-shadow: 0 0 3px 3px red; }"]
})
export class AppComponent {
public ddl_state: string = "error-state";
public listItems: Array<string> = ["Item 1", "Item 2", "Item 3"];
}
Here is a plunkr demo that shows this in action.

Related

Why is theme undefined in styled component props?

Here I access theme by passing a callback function to the styled tag. I guess styled calls this callback function with the props as first argument. This works well.
export default function SectionHeading(props: SectionHeadingProps) {
const Heading = styled.h2`
${props => props.theme.green && `
color: green;
`}
`;
return (
<Heading>{propss.children}</Heading>
);
}
In this example I pass an expression that contains the props the component has received. Here, theme is undefined.
export default function SectionHeading(props: SectionHeadingProps) {
const Heading = styled.h2`
${props.theme.green && `
color: green;
`}
`;
return (
<Heading>{props.children}</Heading>
);
}
Why is theme undefined in the second example?
The reason is that these are different "props" and they are evaluated in different times, in the first example, the props are the props passed to the styled component, augmented with theme (provided you used <ThemeProvider .../>. In the second example, it is the props passed to your component.
The injection of the theme is done by styled-component library and only to styled components. Your component doesn't get it (because it is not a styled component).
Btw, your code has redundant nesting and creates a styled component each time it is invoked.
The way to do it is to simply define:
const SectionHeading = styled.div`
${props => (props.theme && props.theme.green && {color: 'green'})};
`;
and then:
export default SectionHeading;
Note that your sample code has a typo in the first part, you wrote {propss.children} (an extra 's').

Styling react-select with styled-components

I'm trying to change the color of the select-up-arrow and the color of the control when it's in focus, but without success. Have anyone done this using styled-components?
This applies to react-select#v2.*
The same ideas as #bamse answer can be applied to v2 of react-select. The problem is that in v2 they removed pre-determined class names unless you specify to add them in with the prop classNamePrefix. They also changed what the class names in general look like.
General solution is to make sure to add in the class names with the prop classNamePrefix, then use styled around ReactSelect and target classes within it.
import React from 'react';
import ReactSelect from 'react-select';
import styled from 'styled-components';
const ReactSelectElement = styled(ReactSelect)`
.react-select__indicator react-select__dropdown-indicator {
border-color: transparent transparent red;
}
`;
export (props) => <ReactSelectElement classNamePrefix="react-select" {...props} />
This applies to react-select#v3.*
I had the same problem and solved it like this:
CustomSelect.js file:
import ReactSelect from 'react-select';
import styled from 'styled-components';
export const CustomSelect = styled(ReactSelect)`
& .Select__indicator Select__dropdown-indicator {
border-color: transparent transparent red;
}
`;
TheComponent.js file:
import React from 'react';
import { CustomSelect } from './CustomSelect';
export function TheComponent () {
return <div>
<CustomSelect
classNamePrefix={'Select'}
{* props... *}
/>
Something awesome here...
</div>
}
`;
Note the classNamePrefix={'Select'} in TheComponent.js - that's important.
This applies to react-select#v1.*
Here you can find an example of styling react-select with styled-components.
To change to caret's colour when the select is opened you can use this
&.Select.is-open > .Select-control .Select-arrow {
border-color: transparent transparent red;
}
The component would look like
import React from 'react';
import ReactSelect from 'react-select';
import styled from 'styled-components';
const RedCaretWhenOpened = styled(ReactSelect)`
&.Select.is-open > .Select-control .Select-arrow {
border-color: transparent transparent red;
}
`;
export (props) => <RedCaretWhenOpened {...props} />

How to focus a styled component?

I have a styled component:
const StyledComponent = styled.div`
...
`;
and I want to focus it when the component that uses it is mounted:
class someComponent extends React.Component {
componentDidMount() {
this.sc.focus();
}
render() {
return (
<StyledComponent innerRef={(elem) => { this.sc = elem; }}>
...
</StyledComponent>
);
}
}
this technique does not work - is there a solution for this?
You can't focus non-interactive elements without the use of tabIndex ("tabindex" in normal HTML). The "div" element is considered non-interactive (unlike anchor links "a" and button controls "button".)
If you want to be able to focus this element, you'll need to add a tabIndex prop. You can use .attrs if desired to have it be added automatically so you don't need to write the prop every time.
This link
has an example with both React 16 React.createRef() and the callback method.
Have you tried setting the autoFocus attribute, or is that not fit for your use case?
Try passing the autoFocus prop to your StyledComponent like <StyledComponent autoFocus />.

Angular2 MDL disabling mdl-menu-item not working

I am trying to disable mdl-menu-item based on condition set by module.
my app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'ca-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
test() {
return true;
}
}
my app.component.html
<button mdl-button #btn1="mdlButton" (click)="m1.toggle($event, btn1)" mdl-button-type="raised" mdl-ripple>Options</button>
<mdl-menu #m1="mdlMenu" mdl-menu-position="top-left">
<mdl-menu-item mdl-ripple [disabled]="test()">Draw Object</mdl-menu-item>
<mdl-menu-item mdl-ripple mdl-menu-item-full-bleed-divider>Another Action</mdl-menu-item>
<mdl-menu-item mdl-ripple disabled>Disabled Action</mdl-menu-item>
<mdl-menu-item>Yet Another Action</mdl-menu-item>
</mdl-menu>
At this stage the menu item is never disabled, not sure what i am doing wrong here.
The disabled attribute is a ui only feature in material design lite. e.g. there are only some css rules that change the ui if the disabled attribute is present on an mdl-menu-item. So in your case you can do the following:
<mdl-menu-item [attr.disabled]="test() ? '' : null">Draw Object</mdl-menu-item>
The null value removes the attribute. Also you should note that the click event is fired in any case.
This could be improved but I think I would break existing behavior. I have filed an issue for the next major release to make it more angular like (https://github.com/mseemann/angular2-mdl/issues/797).

How to create an SVG component dynamically in Angular2?

I am creating a web application which uses SVG.
I have created components consist of SVG element, and they are put into a root svg element.
They have attribute selector, because SVG/XML document tree is strict so I cannot use element selector.
And they have a template starts with svg:g tag:
#Component({
selector:'[foo]',
template: '<svg:g>...</svg:g>',
})
In the application, I want to create a component when a user press a button,
and simultaneously start dragging it.
I thought it can be achieved by creating a component dynamically using ComponentResolver:
#ViewChild('dynamicContentPlaceHolder', {read: ViewContainerRef})
protected dynamicComponentTarget: ViewContainerRef
private componentResolver: ComponentResolver
onMouseDown() {
this.componentResolver
.resolveComponent(FooComponent)
.then((factory) => {
const dynamicComponent = this.dynamicComponentTarget.createComponent(factory, 0)
const component: FooComponent = dynamicComponent.instance
const element = dynamicComponent.location.nativeElement
// add event listener to start dragging `element`.
})
}
Component is created when onMouseDown() called, but its DOM element is div, so it is illegal element in svg document and cannot be displayed.
I have tried with selector='svg:g[foo]', then g element is created, but its namespace is not for SVG (http://www.w3.org/2000/svg), but normal HTML namespace (http://www.w3.org/1999/xhtml) and its class is HTMLUnknownElement > g.
I also tried with selector='svg:svg[foo]', then svg:svg element is created and it is displayed. But svg:svg cannot move with transform attribute so this doesn't work well for my application.
How can I dynamically create svg:g element for attribute selector component?
I am using Angular2: 2.0.0-rc4.
You're right about the namespacing issues keeping the g element from rendering as svg. Unfortunately, attaching the node as an svg element is the only way to feasibly get the component to namespace properly.
However, this doesn't mean this won't work. If you add the drag functionality as a directive on the g element in the template, it will be compiled with your component, and you can offset your logic into that directive. The top level svg will be namespaced correctly, and the template will inherit this accordingly.
import {Component, Input} from '#angular/core';
#Component({
selector: 'svg:svg[customName]', // prevent this from hijacking other svg
template: '<svg:g dragDirective>...</svg:g>', // note the directive added here
style: []
})
export class GComponent {
constructor() { }
}
This may not be ideal, but until https://github.com/angular/angular/issues/10404 is resolved, there's not much of an alternative.
I am not sure that my solution is right but it works for me (even with ivy):
#Component({
...
})
class ParentComponent {
constructor(
private injector: Injector,
private appRef: ApplicationRef,
private componentFactoryResolver: ComponentFactoryResolver,
) {}
createDynamicComponent() {
let node = document.createElementNS("http://www.w3.org/2000/svg", "svg");
let factory = this.componentFactoryResolver.resolveComponentFactory(DynamicComponent);
let componentRef = factory.create(this.injector, [], node);
this.appRef.attachView(componentRef.hostView);
}
}
After that you must manually append node into DOM.
Instead of trying to create your component to the view with the Component Resolver I will do this instead.
Create a object with properties which match the attributes you want to pass to your SVG Component.
Append this object to an array (ex.svgItems).
Add *ngFor="svgItem in svgItems" to the SVG component you want to create dynamically.
Hope it's clear and solve your problem.

Resources