I'm currently using Angular2 (Dart) to create a reusable search box component, which includes a suggestion box.
The trouble I'm running into is that I'm not sure how to make the suggestion box extensible by supporting richer suggestion types, such as suggestions with icons, suggestions with multiple lines of text, varying styles on the text, animations, etc.
Currently the suggestion box HTML (stripped down) looks a bit like this:
<div class="suggestion-box-container">
<div class="suggestion" *ng-for="#suggestion of model.suggestions">
{{ suggestion.text }}
</div>
</div>
And the Dart code looks sort of like this:
#Component(
selector: 'suggestion-box'
)
#View(
templateUrl: 'suggestion_box.html',
directives: const [NgFor]
)
class SuggestionBox {
final SuggestionModel model;
// ...
}
class SuggestionModel {
List<Suggestion> suggestions = [];
// ...
}
class Suggestion {
String text;
// ...
}
I'm wondering how I can modify this design so that a user can extend Suggestion and write their own corresponding suggestion component.
I considered something like
<basic-suggestion *ng-if="suggestion.type == 'basic'"></basic-suggestion>
<icon-suggestion *ng-if="suggestion.type == 'icon'"></icon-suggestion>
...
But of course that would get out of hand quickly as more suggestion types are supported, and would also require duplicating the (^hover) and (^click) handlers to each type of suggestion. Worse yet, it would require modifying the original library rather than just using it in a modular way.
Is there an elegant solution to this sort of thing? Or would users simply be better off writing their own suggestion box entirely if they wanted richer suggestions? I'm hoping to avoid that since I want to bake in a bunch of accessibility functionality into the search box/suggestion box that the user shouldn't need to rewrite.
EDIT: based on Gunter's answer I've managed to improve this by a little bit:
<suggestion-box>
<div *ng-for="#suggestion in suggestions">
<suggestion suggestion-instance="suggestion">
<!-- Custom suggestion code goes here.
The following is an example -->
<img src="{{suggestion.iconSrc}}"></img> {{suggestion.text}}
</suggestion>
</div>
</suggestion>
I have a new suggestion component that looks a bit like this:
#Component(
selector: 'suggestion',
properties: const ['suggestion: suggestion-instance']
)
#View(
template: '''
<div (^click)="handleClick(suggestion)"
(hover)="handleHover(suggestion)">
<content></content>
</div>
'''
)
class SuggestionComponent {
final SuggestionModel model;
final Suggestion suggestion;
// ...
}
This is a bit nicer now because the user now gets accessibility/keyboard control/mouse control out of the box. But this still isn't ideal. I'd somehow like the template to automatically determine the appropriate component to use based on the actual subclass of the suggestion. I suppose I could define a method in each component like getTemplate() but that probably wouldn't be a very Angular-esque way of doing it.
I haven't really used Angular2 but I think there are at least two ways to do it
- You should be able to extend a component (just create a derived class and specialize the behavior
- Use composition with the <content> tag of the shadow DOM. I found some docs here https://www.airpair.com/angularjs/posts/creating-components-p3-angular2-directives
- Or a combination of the two
update
Support for extending components is coming with version 3.2.
Annotations like #HostBinding(), #HostListener(), #ViewChild(), #ContentChild() will be recognised on subclasses, so will be interfaces for OnInit and other lifecycle callbacks
There are different ways beside inheritanc to make a component customizable.
You can pass component types to be instantiated by your search box using DynamicComponentLoader,
pass children to be displayed at <ng-content> locations,
or pass <template> content to be added dynamically by your search component, and probably others.
Related
I need a ComboBox without this clear button. It confuses the users.
I believe in Vaadin 8 it could be removed with setEmptySelectionAllowed(true);.
How can it be removed in vaadin 10? setAllowCustomValue(false) does not help.
Java 8
Vaadin 10.0.2
I guess the easiest way to achieve that would be with CSS, at least that's how I would do it.
What you want to do is extend the default theme module for VaadinComboBox web component (see https://github.com/vaadin/vaadin-themable-mixin/wiki/2.-Adding-Styles-to-Local-Scope), so you can use the following approach:
First, choose a CSS class name, like my-combobox
Next, create an HTML file that will contain the extension of the default theme module for VaadinComboBox web component. Give it a name like my-combobox-theme.html and put it into src/main/resources/META-INF/resources (yes, it's resources twice)
Put the following into that HMTL file:
<dom-module id="my-combobox-theme" theme-for="vaadin-combo-box">
<template>
<style>
:host(.my-combobox) [part="clear-button"] {
display:none !important
}
</style>
</template>
</dom-module>
In the first line you declare that the following CSS is supposed to supplement whatever styles are defined for VaadinComboBox web component.
Then, the only CSS rule that is there defines that whenever there is a VaadinComboBox that has CSS class my-combobox the clear-button part of the web component should not be displayed.
Import the custom module to a view with #HtmlImport("frontend://my-combobox-theme.html"). NB: you need to add this annotation in all views that you want to use the modified ComboBox in. See point 6 for an alternative
Now you're pretty much all set. Whenever you want to have a ComboBox without delete button, just add a class name with comboBox.addClassName("my-combobox")
You probably want to use your ComboBox in more than one place, so a good idea is to create your own class. This gives you a reusable component and takes care of always having the right HTML import for custom style in place:
#HtmlImport("frontend://my-combobox-theme.html")
public class MyCombobox extends ComboBox {
public MyCombobox() {
addClassName("my-combobox");
// Adding the following code registers a listener which
// resets the old value in case the user clears the
// combo box editor manually, e.g. by entering "".
//
// addValueChangeListener(listener -> {
// if(listener.getValue() == null) {
// setValue(listener.getOldValue());
// }
// });
}
}
Since Vaadin 14 you can easily hide/show the clear button with
comboBox.setClearButtonVisible(false);
API documentation
I know you asked for Vaadin 10, but for completeness I wanted to add this here.
This is not possible at the moment but discussed as feature. See the Github issue No way to disallow clearing selected value. You can leave a thumbs up on that issue to emphasize its importance. IMO this is a must-have feature that should be implemented from the beginning.
The roadmap says something about a "Dropdown menu" upcoming in Vaadin 11 in Q3. This could be interesting.
I am using shadow-dom traversal of the clear button component inside the vaadin-combo-box (in this case, id:my-combo), and set the display property. (javascript)
var clear_button = this.$.my_combo.shadowRoot.querySelector("#clearButton");
clear_button.style.display = "none";
I am using angular ui grid. At first I implemented this in a controller and it works fine after all my customization. As I am using multiple grids I cannot write a long controller each time. So, I converted it to factory. I kept the common function in factory and column definition and data in controller. Now when I use this factory in more than 1 controllers, the last controller is overriding all others.
1) Is it correct to make this grid in a factory?
2) If yes how do I overcome this problem?
3) By using factory for this grid, my gridObj.gridApi.pagination.on is throwing error(gridObj is the singleton object that I am returning).
Any suggestion is welcome. Thanks a lot in advance.
You should use a Directive instead. Factories create a single Instant (see Angular Provider Documentation and wont create a private scope, which you need to not override your data.
Note: All services in Angular are singletons.
But Directives provide a private scope and create new instances every time they are called in HTML if you want them to.
//directive
scope: { // this option creates isolated scopes
something : '=',
},
I created a Plunkr showcasing a possible setup. For some more written details please see my answer from few days ago.
Your HTML might look like this afterwards
<my-grid options="all.firstOptions" class="grid"></my-grid>
Where my-grid is your directive and options="" are your special settings (and whatever else you wish to use in the directive). In your directive you declare the default settings and merge them with the special ones.
scope.gridOptions = {
data: scope.options.data, //private scoped from options : '=',
columnDefs: scope.options.colDef || defaultColDef, // optional setting or default setting
// ... some more default data
};
If you have any specific questions, let me know.
I am newbie to Xamarin and Xamarin.Form, basically I want to define style for the controls.
Like when I place Label control then it should follow the same style throughout the page. I've read somewhere on article that it can be done by defining styles in tags but don't know how..
How to define for 1 page
How to define globally which will be applied to all the pages
How to define device specific
Can anyone provide some example code / link for same?
Thanks in advance!
If you are talking about doing it through XAML then:
- if it is per page then go with resources.
- for device specific, use OnPlatform class
- there are no global resources currently
If you are creating your stuff in code then apply whatever global value you wish when you create them. If there are platform specific values then use Device class.
HTH
For per page resources, you can create a Common folder with files such as "ColorResources" which might have entries such as
public static readonly Color ActivityIndicator = Color.Blue;
You can then use that in your XAML like this
<ActivityIndicator IsRunning="{Binding IsLoading}"
Color="{x:Static common:ColorResources.ActivityIndicator}" />
(Remember to declare your common namespace)
For cross page, I'd recommend a custom renderer, built from the original control but tailored to look like what you want. You can find out more hereCustom Renderers
I have a component called
<checkbox_component ng-repeat="ccc in cmp.teachers1"
label="{{ccc.name}}" objectValue="{{ccc}}"></checkbox_component>
Should I declare objectValue as a NgAttr? but that doesn't work I think it only works with strings.
Here I want to complain, I find it relatively difficult to communication between components. Lets say I have a component which is a page and then it has some components of mine on that page, its slightly hard to communication from the child components to the parent components and back again, either due to my lack of knowledge or some limitation.
Either use the #NgTwoWay annotation or the NgModel Decorator:
<checkbox_component ng-repeat="ccc in cmp.teachers1" ng-model="ccc"></checkbox_component>
#NgComponent(
selector: 'checkbox_component[ng-model]',
...
)
class CheckboxComponent {
NgModel _ngModel;
CheckboxComponent(this._ngModel);
...
}
Take a look at how NgModel is used at class InputCheckbox here.
You are right. #NgAttr can only work with literal values but you can use #NgTwoWay
In angular-dart it is possible to create your own components as can be seen here. If you use custom tags in the html like this:
<rating></rating>
angular will create a component by calling the constructor of the class associated with rating, in this case new RatingComponent() (if i'm not mistaken).
I know you can add attributes for having some control over it, but i was wondering if it is possible to supply your own instances, instead of angular calling the constructor. What if i have a list of buttons in the main controller, how to achieve something like this:
<div ng-repeat='b in ctrl.buttonList'>
<fancy-button instance='b'></fancy-button>
</div>
I have the feeling i'm missing something obvious, but i did search around and couldn't find the answer.
edit (for extra clarification): I think it boils down to if you can or can not influence/bypass the call of the constructor by angular. If it was just about generating the html, its easy to not use the component and just generate the html using the main-controller (like below), but if possible i would like to use a component since it also has shadow-dom for sandboxing the css.
<div ng-repeat='b in ctrl.buttonList'>
<input type='button' class='fancy' value='{{b.label}}'></input>
</div>
You want something like
<div ng-bind-html='ctrl.html'></div>
This way you can provide the html by the controller.
You would have to create your own implementation of NgBindHtmlDirective (just a few lines) though, because the used NodeValidator of ng-bind-html doesn't allow custom tags (yet).
Maybe there are more sophisticated ways but I don't know Angular very well yet.
=== Edit ===
I think what you try to do is not Angularish.
You can ng-repeat over a model that provides values for the attributes of the fancy-button or input.
In the components class you have access to these attributes and can make the behavior and appearance depending on that attributes.
Using ng-bind-html you can create the markup using code if you don't know at development time what kind of tag you want to use.