React JS - accessing component property in an element attribute - javascript

I have an element defined as class, and in the render() method I'm trying to give an element a custom attribute data-activates with an id. However, in the resulting html I just see the plaintext of the expression, either one of the following:
data-activates="{this.state._id}"
data-activates="${this.state._id}"
data-activates="{id}"
data-activates="${id}"
The id is present in both props and state, and outside of the element both of them work correctly:
<a className="dropdown-button waves-effect" href="#!" data-activates="{id}">
{ id }, {this.state._id} <- works here
</a>
For some reason React doesn't resolve expressions within the attribute and I need that for the dropdown to work. What am I doing wrong?
Bonus point for a better way to implement dropdown in React, if there is no way to make this code work.

You have to go into JavaScript land inside your JSX with the help of {}. That way you can use any expression you like in the JSX.
Example
<a
className="dropdown-button waves-effect"
href="#"
data-activates={this.state._id}
>
Test
</a>

You should not wrap data attributes inside QUOTES
data-activates={this.state._id}
data-activates={id}
This documentation clears everything check the documentation

Make sure you have tried the following:
<a data-activates={this.state._id}>...</a>
<a data-activates={id}>...</a>
Please bear in mind that JSX adheres to JS syntax first. So inside the curly brackets, you still can do interpolation within tildes (`):
<a data-activates={`my-id-${id}`}>...</a>
Cheers,

Don't use a dash. Use lowerCamelCase instead. React doesn't recognize elements with a dash in the name as a prop. So, instead of data-activates={id}, use dataActivates={id}.

Related

React Contenteditable - how can I assign an onClick to a <span> in react-contenteditable?

Is there anyway I can use something like this:
<span onClick={(e) => console.log(e)}>Testing</span>
Inside react-contenteditable to handle the click on Testing keyword?
When I use the html prop, it would only take an html string, something like <span onclick="something()"></span> I suppose, but is there a way to do it in react instead?
And if I use <span onclick="something()"> where should I define this function something()? I suppose at this point I won't have access to the functions defined in react, right? So how should I do this?
onClick is a React property so react-contenteditable wouldn't know what to do with it - since html expects plain html
A hacky way to achieve what you want - or pretty close to it - is:
Create an onClickContentEditable function and is it as onClick for ContentEditable
Add an innerRef to ContentEditable
in onClickContentEditable, then the clicked element is ContentEditable do nothing - since we want to interact only with the children of ContentEditable
Based on DOM attributes of the clicked element (tagName, className ...etc) fun with it! :)
In onClickContentEditable you can check the DOM attributes of the clicked element and take action accordingly.
You could create a class to mark the element you want to click.
You can test this implementation here - the sandbox is forked from the complex react-contenteditable example. I logged the interactions in the console.
Hope it helps!
You cannot assign any React DOM events on children of the react-contenteditable component, because internally it converts all the children to plain HTML string via dangerouslySetInnerHTML React prop:
https://github.com/lovasoa/react-contenteditable/blob/master/src/react-contenteditable.tsx#L53
All React bindings will be lost once the node is rendered

While using Bootstrap-Vue, how do you prevent a b-dropdown from closing when clicking on a nested b-input component?

I'm pretty sure I just don't understand how to implement Vue's Event Modifiers. According to that documentation, all I have to do is add this:
<!-- the click event's propagation will be stopped -->
<a v-on:click.stop="doThis"></a>
Here's how I interpreted the example into my pug code:
b-dropdown(text="Actions")
b-dropdown-item
b-form(inline)
.row
.col
b-input(#click.stop='' placeholder="#123")
b-button(:href='printCheck' variant="primary") Print Check
It looks pretty simple, however it's not working as expected. If you need more supporting info, just ask. And feel free to tweak the title; I wasn't sure if my question is a vue, bootstrap-vue, or javascript question.
Thanks for your time in advance,
Kevin
Since you're clicking on a component you should combine .native with .stop modifiers like so :
b-input(#click.native.stop='' placeholder="#123")
if you're using a simple HTML element like input you could use only .stop modifier:
input(#click.stop='' placeholder="#123")
You can now use the new <b-dropdown-form> sub-component for placing input fields into dropdowns.
You should avoid placing input controls inside <b-dropdown-item> (which renders an <a> as it's root element), or <b-dropdown-item-button> (which renders a <button> as its root element). HTML5 doesn't like interactive elements inside <a> or <button> elements.
<b-dropdown-form> does not auto-close the dropdown when it is clicked.

What does a `#` attribute do in HTML?

I am writing a handler to do something to some element when a user clicks on a different element.
At first I had the following (This is using Angular2 but I suspect the only different is how the onclick event is handled):
<span>
<input type="text" id="bioName">
</span>
<span class="icon-pencil" (click)="editField(bioName);"></span>
...but this didn't work. I then found an example that identified the input field differently, and this did work for me. It was as follows:
<span>
<input type="text" #bioName>
</span>
<span class="icon-pencil" (click)="editField(bioName);"></span>
Unfortunately I can't find anything that explains this. Searching for "hash" and "pound" with HTML and Javascript yield too many results that have nothing to do with this in the subject area.
So what does # do in this case? Can id not be used to obtain a reference to the DOM element when setting up an event handler? What is this called so I can google it and read the appropriate documentation?
This is Angular2 specific, regular HTML doesn't recognize this syntax, i.e. you have to use id="bioName" in order to access the tag with CSS/JavaScript.
Here is more information on how to use # in Angular2.
Hash (#) is syntax for defining template variable in Angular 2 templates. It is used to assign unique identifiers to template elements which you can later use to get a reference to template elements in component. In your case, for example, you can use bioName variable to get a reference to input element in your component and you can do whatever you want with it there - get file name, file size or even file itself. This is done using ViewChild decorator. You can check out answer I wrote few days ago and see what template variables are mostly used for.

What is a good way for a Angular directive to act as an facade to other elements?

This is more a generic question about Web Components, however I'll write the examples in Angular as it offers some more ways to handle this problems (like replace even if it is deprecated) and it is also more familiar to me and probably others.
Update
Because of this comment I think many problems I face are Angular specific, because of the way Angular "compiles" directives. (I can't easily add or remove a directive at runtime.) Therefor I don't search for a generic solution anymore, but for a Angular specific solution. Sorry for this confusion!
Problem
Say I want to create a menu bar which could look like this:
<x-menu>
<x-menu-item>Open</x-menu-item>
<x-menu-item>Edit</x-menu-item>
<x-menu-item>Create</x-menu-item>
</x-menu>
This could translate to this:
<section class="menu">
<ul class="menu-list">
<li class="menu-list-item">
<button type="button" class="menu-button">Open</button>
</li>
<li class="menu-list-item">
<button type="button" class="menu-button">Edit</button>
</li>
<li class="menu-list-item">
<button type="button" class="menu-button">Create</button>
</li>
</ul>
</section>
This is fairly trivial. The problems arise, if I want to configure my <x-menu-item> with (existing) directives/attributes. Sometimes an attribute should refer to the button. E.g. a click on <x-menu-item> should probably be proxied to the <button>, because it is the "real" interactive element inside <x-menu-item>.
<x-menu-item ng-click="foo()">Open</x-menu-item>
<!-- → possible translation -->
<li class="menu-list-item">
<button type="button" class="menu-button" ng-click="foo()">Open</button>
</li>
However other attributes should refer to the <li>. Say I want to hide <x-menu-item> I probably want to hide everything, not just the <button>.
<x-menu-item ng-hide="bar">Open</x-menu-item>
<!-- → possible translation -->
<li class="menu-list-item" ng-hide="bar">
<button type="button" class="menu-button">Open</button>
</li>
And than there are of course attributes which affect the <li> as well as the <button>. Say I want to disable the <x-menu-item> I probably want to style the <li> and I want to disable the <button>.
<x-menu-item ng-disabled="baz">Open</x-menu-item>
<!-- → possible translation -->
<li class="menu-list-item" ng-class="{ 'is-disabled': baz }">
<button type="button" class="menu-button" ng-disabled="baz">Open</button>
</li>
That is basically what I want to achieve. I know some solutions, but all have their downsides.
Solution #1: Generate template dynamically and handle attributes manually
I could replace the <x-menu-item> with a complete dynamic template and handle the attributes manually. It could look like this (not fully functional):
// directive definition
return {
restrict: 'E',
transclude: true,
template: function(tElement, tAttrs) {
var buttonAttrs = [];
var liAttrs = [];
// loop through tAttrs.$attr
// save some attributes like ng-click to buttonAttrs
// save some attributes like ng-hiden to liAttrs
// save some attributes like ng-disabled to buttonAttrs and liAttrs
// optionally alter the attr-name and -value before saving (so ng-disabled is converted to a ng-class for liAttrs)
// unknown attribute? save it to either buttonAttrs or liAttrs as a default
// generate template
var template =
'<li class="menu-list-item" ' + liAttrs.join(' ') + '>' +
'<button class="menu-button" ' + buttonAttrs.join(' ') + ' ng-transclude>' +
'</button>' +
'</li>';
return tElement.replaceWith(text);
}
}
This actually works quite well in some cases. I have a custom <x-checkbox> which uses a <input type="checkbox"> internally. In 95% cases I want all attributes placed on <x-checkbox> to be moved to <input type="checkbox"> and just some on a wrapper around <input type="checkbox">.
I actually handle ng-disabled here, too. In case you wonder how this could look like, here is an example:
angular.forEach(tAttrs.$attr, function(attrHtml, attrJs) {
buttonAttrs.push(attrHtml + '="' + tAttrs[attrJs] + '"');
if (attrHtml === 'ng-disabled') {
liAttrs.push('ng-class="{ \'is-disabled\': ' + tAttrs[attrJs] + ' }"');
}
});
Downsides: I need to decide where to place attributes I don't know beforehand. Should they be placed on the <button> or <li>? I think I want more attributes on the <button> than on the <li>, because my <x-menu-item> is basically a wrapped button and using it feels like you would use button. A developer would expect <x-menu-item> to work like a <button>. However it seems strange to not place unknown attributes on the root element (in this case <li>). One would also expect that attributes on <li> would affect <button>, if necessary (like a CSS class does). I also create my markup in JavaScript, instead of plain HTML.
Replace or don't replace
I know its deprecated, but sometimes I like to use replace my directive with my template. Say someone places an id on my directive, I like to move the id to the canonical element in the template representing the directive (.e.g. on a <x-checkbox> the id would be transferred to the <input checkbox="type">). So if somebody tries to getElementById he will get the canonical element behind it. If I don't replace the whole directive, I would need to decide which attributes (or all) should be removed on the directive, because they were moved to a different element. This can be buggy, if you miss something (and suddenly have the same id twice).
Solution #2: Use prefixed attributes
Similar to #1, but the user decides if an attribute should be used on the directive or on certain elements. It could look like this:
<x-menu-item li-ng-hide="bar" button-ng-click="foo()">Open</x-menu-item>
<!-- → possible translation -->
<li class="menu-list-item" ng-hide="bar">
<button type="button" class="menu-button" ng-click="foo()">Open</button>
</li>
Downsides: This one gets more verbose, but offers more flexibility. E.g. a developer could create a custom id for the directive, the li and the button. But what is with ng-disabled? Should the developer place a button-ng-disabled as well as a li-ng-class? That is cumbersome and error prone. So we probably need to handle those cases manually again...
Solution #3: Use two directives
If we can't decide how to handle our attributes, we could introduce two directives. That way we don't introduce artificially prefixed attributes.
<x-menu-item ng-hide="bar">
<x-menu-button ng-click="foo()">Open</x-menu-button>
</x-menu-item>
<!-- → possible translation -->
<li class="menu-list-item" ng-hide="bar">
<button type="button" class="menu-button" ng-click="foo()">Open</button>
</li>
Downsides: This isn't very dry and therefor error prone. I always have a <x-menu-button> in my <x-menu-item>. There will never be an empty <x-menu-item> nor a <x-menu-item> with a different child element. I also have the same problem with ng-disabled as in solution #2. A developer should be able to easily deactivate my whole <x-menu-item>. He shouldn't care to add a certain ng-class for styling purposes and disable the button on his own.
Solution #4: Use a generic interface
Limit your interface. Instead of trying to stay generic (which is nice, but cumbersome) one should limit its interface. Instead of special handling for ng-disabled, ng-hide and ng-click try to identify your common use cases and offer a more custom interface to use them. That way we only handle explicitly defined attributes in a special way.
<x-menu-item hidden="bar" action="foo()" disabled="baz">Open</x-menu-item>
<!-- → possible translation -->
<li class="menu-list-item" ng-show="bar" ng-class="{ 'is-disabled': baz }">
<button type="button" class="menu-button" ng-click="foo()" ng-disabled="baz">Open</button>
</li>
Downsides: This approach isn't very intuitive. Every Angular developer knows ng-click. No one knows my action attribute.
(Partly) Solution #5: Proxy DOM events
Instead of moving a ng-click from the directive to buttons it can't sometimes be useful, if the directives listens for clicks on itself and automatically trigger a click on the button (or the other way around - it depends on the use case).
Solution #6: Dirty queries.
See the answer from #gulin-serge for details. Short explanation: "Decorating" existing directives like ng-click with custom logic, if it is used on a certain element and prevent using default behavior.
Downsides: Every ng-click will be checked, if it is used on a certain element even if this is not the case. This checking is a small overhead. You must also remove the default behavior of ng-click which can result in unexpected behavior. E.g. Angulars ngTouch module decorates every ng-click so it also called on a touch event. This is something which should happen for <x-menu-item>, too, but you would now need to check, if ngTouch is used manually and if this is true, listen for touch events as well. This is error prone and doesn't scale. This "decoration step" currently happens on the link phase which can have its own downsides: it would be hard to generate a ng-class for the <li> dependent on ng-disabled here. You would need to use $compile which can have unexpected effects on its own. (E.g. I used it on <select> ones and suddenly all <options> were duplicated. That can be hard to debug.) Other directives have a default behavior which is too useful to loose (e.g. ng-class is "animation aware" and sets utility classes like ng-enter - it wouldn't be enough to rebuild some custom element.addClass(cssClass)).
(Partly) Solution #7: Use multiple templates.
Sometimes it is sufficient to use multiple templates which are chosen dependent on some attributes. This can happen inside the templateUrl function.
<x-menu-item>Open</x-menu-item>
<!-- → possible translation using "template-default.html" -->
<li class="menu-list-item">
<button type="button" class="menu-button">Open</button>
</li>
Or:
<x-menu-item disabled="baz">Open</x-menu-item>
<!-- → possible translation using "template-disabled.html" -->
<li class="menu-list-item" ng-class="{ 'is-disabled': baz }">
<button type="button" class="menu-button" ng-disabled="baz">Open</button>
</li>
Downsides: This isn't very DRY. If you want to change the menu-list-item class you need to do this in two templates. But it's nice to finally write templates in HTML again and not as JavaScript strings. But it doesn't scale well, if you have more variation. However this can be your only solution, if not just some attributes change, but the whole markup behind it.
Solution #8: Try to initialize every hidden directive with some default behavior (even if it is some noop).
Maybe every specially handled attribute can be initialized with some default value, even if this value does nothing. The default behavior can be overridden.
<x-menu-item>Open</x-menu-item>
<!-- → possible translation -->
<!-- $scope.isDisbabled = has('ng-disabled') ? use('ng-disabled') : false -->
<!-- $scope.action = has('ng-click') ? use('ng-click') : angular.noop -->
<!-- $scope.isHidden = has('ng-hide') ? use('ng-hide') : false -->
<li class="menu-list-item" ng-hide="isHidden" ng-class="{ 'is-disabled': isDisabled }">
<button type="button" class="menu-button" ng-disabled="isDisabled" ng-click="action()">Open</button>
</li>
Downsides: You initialize directives which sometimes are never used. This can be a performance problem. But all in all this approach is relatively clean. This is currently my favorite solution.
Solution #?: ???
...
Possible #6. Dirty queries.
What if we will use directive definition as a query expression for the case when we should switch off default implementation?
You write something like:
.directive('ngClick', function() {
return {
restrict: 'A',
priority: 100, // higher than default
link: function(scope, element, attr) {
// don't do that magic in other cases
if (element[0].nodeName !== 'X-MENU-ITEM') return;
element.bind('click', function() {
// passthrough attr value to a controller/scope/etc
})
// switch off default implementation based on attr value
delete attr.ngClick;
}
}})
This will switch off default implementation of ng-click at your tags. Same job for ng-hide/ng-show/etc.
Yep, it looks terrible by sense, but result is closer to your idea. Of course it will slow down linking process of compile a bit.
P.S. According to your list I prefer #2 but with custom directive namespace. Something like:
<app-menu-item app-click="..." app-hide="..."/>
And add a convention to docs to use app prefix for all custom things and behaviour. Where app is an abbr of project name usually.
Update: Rewrite based on Comments
This is largely rewritten based on the clarification within the comments. As a pure component-based framework, the idea behind Polymer Elements is give developers a simple way to accomplish two tasks. The first is the ability to make simple Elements with previously un-programmed functionality. The second is the ability to make complex controls with simple custom markup.
This is a considerably different focus than Angular, which consolidates functionality into directives that translate custom markup into native markup and couples the functionality to the translated markup.
Notes on Polymer:
Polymer is primarily a framework polyfill until Web Components, Shadow DOM, Scoped CSS and HTML Imports becomes standardized and implemented. HTML Templates is already implemented pretty well in modern browsers and is worth reading up on. That said, Polymer won't just go away when those are standard, as it provides convenience functions and attributes, as well as a good set of components to start with. The polyfills are considered to be slower than native support, but that is to be expected.
Differences between Polymer and Angular:
Unlike Angular, when you create an element, you are not 'translating' it from one markup-set to another. You are actually defining its functionality and presentation. Even though it may provide additional markup within its Template(s) and Shadow DOM, that additional markup are all functional elements (whether custom or native).
This also means that your elements can have their own CSS Classes and IDs are are handled by the browser allowing for different presentation quite easily. Selector Engines will get the actual custom element and can get the properties and methods associated with them.
Simple Demonstration:
<polymer-element name="x-menu">
<template>
<style>
/* Scoped Style rules */
</style>
<content></content>
</template>
<script>
/* Registers the Element with the browser. */
Polymer('x-menu', {
// Additional Element properties and methods
});
</script>
</polymer-element>
<polymer-element name="x-menuitem">
<template>
<style>
/* Scoped style rules */
</style>
<button type="button" class="{{parentElement.classList}}">
<content></content>
</button>
</template>
<script>
/* Registers the element with the browser */
Polymer('x-menuitem', {
// Additional Element properties and methods
});
</script>
</polymer-element>
Actual Usage
<x-menu class="cool">
<x-menuitem>Open</x-menuitem>
<x-menuitem>Edit</x-menuitem>
<x-menuitem>Create</x-menuitem>
</x-menu>
When you actually run this, you will see that the button actually copies the classList from the parentElement. In other words: {{parentElement.classList}} is actually shorthand for this.parentElement.classList. Understanding this, you can actually build a number of functions amd properties that are based on parent markup. In contrast, the reverse can be done, as well. You can also use document.querySelector('x-menu') and you will get the <x-menu>.
Additionally, since these are attributes that apply only your own custom element, you need not worry about namespacing the attributes. No other element will understand your attributes, and the browser won't try to do anything funny with them.
Update: Applying Polymer to your Needs
First, and foremost, using your example above, if you have two elements, then you will have two custom elements. How coupled they are depends largely on how they are programmed. It needs be understood that there is only need for native elements within your custom elements if you need multiple types of functionality or presentation. I would recommend, at first, not utilizing native elements except where you need for styling. With Polymer, it is generally best to start small...
For your functional example, there is no real need to have anything other than the content unless you need a) multiple styling blocks or b) to integrate the functionality of another element or component. Since you require click functionality and the ability to focus/style, the most one should add is a button. The rest can easily be handled in CSS.
Getting out of the mindset that a component should handle multiple elements is important, because it is only true when you need multiple elements. A simple element is there to extend the HTML Element set for your needs. Only worry about managing other elements (either custom or native) when you need an advanced component.
Update: Facading Elements
Polymer provides several mechanisms for having an element have multiple presentations or function based on properties. The simplest mechanism is template binding which changes the relevant document fragment based on conditions.
<polymer-element name="x-menuitem">
<template>
<template if="{{condition}}">
<style>Uses this set of styles</style>
</template>
<template if="{{condition2 OR !condition}}">
<style>Use this stylesheet instead</style>
</template>
<content></content>
</template>
<script>Polymer Registration</script>
</polymer-element>
There may be any number of conditions, and since <template> is just an element, you can actually put any elements in there, including more <template> bindings.
Update: Two examples with Disabled
In the first example, we will simply make a <x-menu-item> with the ability to disable it. We're only going to use the core element, without an <li> or <button>. To disable the item, you may either set the attribute directly (when you mark it up, or get the element via a selector query and set the disabled property.
<polymer-element name="x-menuitem">
<template>
<style>
:host { color:blue; }
:host([disabled]) { color:red; }
</style>
<content><content>
</template>
<script>
Polymer('x-menu-item', {
publish: {
disabled: {
value: false,
reflect: true
}
},
method: function() {
if (this.disabled) return;
}
});
</script>
</polymer-element>
In our second example, we will use an <x-menu-item> with child <li> and <button>. This will have some conditional template binding so that it renders with a different classes. It can still be disabled the same as the above.
<polymer-element name="x-menuitem">
<template>
<style>
:host #item {
/* styles */
}
:host([disabled]) #item {
/* styles */
}
</style>
<template if="{{disabled}}">
<li id="item" class="disabled_class1">
<button class="disabled_class2">
<content><content>
</button>
</li>
</template>
<template if="{{!disabled}}">
<li id="item" class="enabled_class1">
<button class="enabled_class2">
<content><content>
</button>
</li>
</template>
</template>
<script>
Polymer('x-menu-item', {
publish: {
disabled: {
value: false,
reflect: true
}
},
method: function() {
if (this.disabled) return;
}
});
</script>
</polymer-element>
Final Note Updated:
Keep in mind that the above examples are just two declarative ways to accomplish what you need. There are other declarative techniques to accomplish the same goal. Further, you may always utilize the ES/JS to imperatively define everything as well, giving even more options.
There is a lot to Polymer that is matches default browser behavior for HTML Elements. That does not mean there isn't a lot to know. This answer was merely to demonstrate one specific desired functionality of your question. Since the development process and terminology is so different, I wasn't sure what else should be addressed. Whatever might be missing can certainly be added to the answer in updates; simply let me know which specifically you would like added.

Get reference to anchor's dom element from javascript

If I have a some javascript in an anchor's href attribute:
<a href="javascript:alert('hello!')">
Is there a way I can get a reference to the DOM element that was clicked when the script executes? I mean, I know I could do
<a href="javascript:alert('hello from '+document.getElementById('thisAnchor'))" id="thisAnchor">
But I was hoping for something more like
<a href="javascript:alert('hello from '+target)">
Something like this?
Example: http://jsfiddle.net/TTzDb/
me​​​​​​​​​​​​​​
Using onclick, this will refer to the element that received the event.
Move the JavaScript to the onclick="yourJavaScriptHere" attribute. Then you can use the 'this' keyword to reference your anchor. So
some text
Although, that isn't very meaning. Additionally, it is a better practice to separate your JavaScript from your HTML to build a more maintainable website.
Yes, and the answer is this which refers to current DOM element:
Click me
EDIT:
Of course as bobince mentioned (see comments) that won't work as excepted. The correct form is:
Click me

Categories

Resources