I'd like to use ARIA attributes in Ember core form components, such input and textarea fields.
I noticed that using an aria attribute within the component in my template, it doesn't work at all
{{input aria-label="Your name"}}
{{textarea aria-label="Your address"}}
So I decided to reopen the core components in an initializer to add this attribute to the components
export default {
name: 'reopenTextAreaComponent',
initialize: function () {
Ember.TextArea.reopen({
attributeBindings: ['aria-label']
});
}
};
Since I did that, the performance of my application is pretty bad. The integration tests take much more time than before.
I tried not to use their components and simply a HTML tag:
<textarea {{bind-attr aria-label="Your address"}}>{{value}}</textarea>
But this doesn't compile with handlebars! It returns an error because of the {{value}} within the textarea tag.
What is the solution to avoid reopening? Should I create my own component?
Thanks
From Ember 2.8+, the simplest way of doing this is to install ember-component-attributes, like this:
ember install ember-component-attributes
Then you can add aria-label, other ARIA attributes and whatever other attributes you might require as follows:
{{input (html-attributes aria-label="Your name")}}
{{textarea (html-attributes aria-label="Your address")}}
What is the solution to avoid reopening? Should I create my own component?
Those are your two options.
I think you have the correct approach regarding the reopening of Ember.TextArea, because you want to have aria-label available on all instances.
I use a similar initializer - with one difference being that I reopen Ember.TextSupport so that both Ember.TextField and Ember.TextArea will have the aria-label binding.
// initializers/input.js
import Ember from 'ember';
export function initialize(/* application */) {
Ember.TextSupport.reopen({
attributeBindings: ['aria-label']
});
}
You could try creating your own component(s) to see if there is any difference in performance, but I think reopening is the right approach in this case.
Related
I'd like to use js-data 3.0.1 as store for my Vue.js 2.4.2 SPA. Everything works like a charm, but unfortunately I can't make the reactive data bindings work.
I've already tried vue-js-data, which seems to be broken.
The following example doesn't work. If I change the text fields, the text will not be updated. However if I replace the js-data record with a plain old JS object, it works like expected.
user.vue:
<template>
<div>
<input name="name" v-model="user.name" />
<br />
Name: {{user.name}}
</div>
</template>
<script>
export default {
data() {
return {
user: this.store.createRecord('user')
}
}
}
</script>
I'm thankful for any advice how to make the data binding (via v-model for example) work.
If you add this to the constructor of your User class, it should work for all root level properties. See here for a complete example: https://gist.github.com/calebroseland/2fa37abdb5560739b3b4b901382b0a90
// apply vue reactivity
for (let key in this) {
Vue.util.defineReactive(this, key, this[key])
}
// re-apply js-data schema
this._mapper().schema.apply(this)
The reason Vue and Js-Data reactivity don't work together out-of-the-box is that they both have separate implementations that use slightly different mechanisms. Here's an explanation: https://medium.com/p/525ffe12ad81#c925
I have followed the Vue.js lessons from laracasts when Vue.js 1 came out and I used to do something like this:
import Comments from './components/Comments.vue';
import Upload from './components/Upload.vue';
new Vue({
el: 'body',
components: {
Comments,
Upload,
Preview,
Algolia,
},
etc,
});
This allowed me to kind of 'sprinkle' components all over my application. I can no longer bind to the body though because Vue replaces the content and also throws an error message saying you shouldn't bind to the body or html.
I followed a couple of lessons for Vue.js 2 but how can I replicate this workflow in the Vue.js 2 manner? I loved just binding to the body and having the option to place a component here and there with the custom tags.
We use the same "sprinkling" approach and all we did was change it from 'body' to '#app'.
We also added a wrapping element inside that had this id to basically replicate body. (https://github.com/onespacemedia/project-template/blob/develop/%7B%7Bcookiecutter.repo_name%7D%7D/%7B%7Bcookiecutter.package_name%7D%7D/templates/base.html#L62)
<body>
<div id="app">
...
</div>
</body>
We use Jinja2 for our templating language and have found when a variable that doesn't resolve in Jinja2 it tanks Vue as well as i think Vue tries to use it.
I believe it takes everything inside #app after initial render and converts it to virtual dom. This doesn't effect anything from what i've seen though so you can happily just add the wrapping class inside body and use it the same as Vue 1
Before anyone press eagerly the close button, I already have looked the following question: ReactJS Two components communicating. My problem is exactly the third scenario developped in the current accepted answer.
I am using ReactJS to build something with two components. For HTML reasons (and presentation), i want my two components to be at two different places of the page.
For the moment, I have the following pattern, corresponding to scenario #2:
FooForm = React.createClass({
...
});
FooList = React.createClass({
...
});
FooManager = React.createClass({
...
render: function () {
return (
<div>
<FooForm ref="form" manager={this} />
<FooList ref="list" />
</div>
);
}
});
React.render(
<FooManager someProp={value} />,
document.getElementById('foo')
);
This gives something like:
<div id="foo">
<form>Form generated with the render of FooForm</form>
<ul>List generated with the render of FooList</ul>
</div>
However, i would like to have something like this:
<div id="fooform">
<form>Form generated with the render of FooForm</form>
</div>
<!-- Some HTML + other controls. Whatever I want in fact -->
<div>...</div>
<div id="foolist">
<ul>List generated with the render of FooList</ul>
</div>
The problem here is: how can I keep a reference in each component? Or at least the link Form -> List?
I tried to create the FooList before and pass the reference to the current manager, but I get the following warning/error:
Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.
The documentation says you can attach events to link two components which do not have a parent-child relation. But I don't see how. Can someone give me some pointers?
The Less Simple Communication lesson from react-training has a good example of how you can move actions & state sideways to avoid having to create an explicit link between related components.
You don't need to jump into a full Flux implementation to get the benefit of this approach, but it's a good example to lead you up to Flux, should you eventually need it or something like it.
Note that this requires you to model the relationship between the components based on changing state rather than explicitly passing a reference to a component instance (as you're doing above) or a callback bound to the component managing the state.
This would be the perfect use-case for a Flux type architecture.
What you want is someone FooManager to be able to trigger state changes in both components. Or, in fact, having the different components trigger, through Actions, state changes in each other.
The Flux Todo-App Tutorial illustrates your use-case perfectly!
After this, then you'd have the choices of using Facebooks implementation of Flux or the other gazillion ones.
My personal favorite is Reflux
I'm trying to use the new components system in knockout 3.2.0.
There isn't much documentation at the moment, but this does work.
ko.components.register('price-input', {
template: '<span>price-input</span>'
})
However the template binding allows you to specify a template name that already exists in the DOM, such as:
<script type="text/html" id="price_input">
<span>price-input</span>
</script>
Then you could do this:
<div data-bind="template: {name: 'price_input'}"></div>
So I tried this:
ko.components.register('price-input', {
template: {name: 'price_input'}
})
but it doesnt work. Is there a way to use named templates with the new components, or must they be inline or loaded with AMD.
Thanks
Edit: After RP Niemeyer's answer, for clarification here is the template I tried his answer with:
<script type="text/html" id="ifx_price_input">
<h4>PRICE INPUT <span data-bind="text: value"></span></h4>
</script>
Here is the component code:
ko.components.register('price-input', {
template: {element: 'ifx_price_input'}
})
It does load the template, but treats it as an escaped string.
Ideas?
You can pass an element property that is either an element itself or a string that is the id of the element like:
template: { element: 'myTmpl' }
In v3.2.0 beta, this case wasn't handled well, hence the hackery needed by InternalFX.
This will be fixed in v3.2.0 final. It will work as you expect - simply reference a script, template, or textarea element, and its logical contents will be intepreted as template nodes.
In case you're interested, the commit that fixes and tests this is here: https://github.com/knockout/knockout/pull/1454
Finally solved this with some hackery...I hope this gets answered better by the knockout devs.
This works. Basically I just load the template text manually and pass it to the register function. So it works as if it was inline.
ko.components.register('price-input', {
template: $('#ifx_price_input').html()
})
I'm just getting my feet wet with Ember.js, and I've hit something that I'm sure I'm not understanding.
I've got a selected object controller. It has content, which is an Ember.Object, which is the currently selected model. The model has a property (isDirty), and basically I'd like my save button on my form to be enabled only when the object is dirty and needs to be saved.
I've managed to bind up the form just fine, but the isEnabledBinding property on the save button is either not doing anything or I'm not hooking up the binding properly.
I've prepared a jsfiddle demonstrating my basic set up.
http://jsfiddle.net/blargity/fqc73/1/
How do I get the button to be enabled only when isDirty is true? The bindings should also work if the content property on the selected object controller changes.
I found a way to do this without using the now-deprecated Ember.Button.
In the handlebars template:
<button {{action "save" target="controller"}} {{bindAttr disabled="view.isNotDirty"}}>Save</button>
In the view:
isNotDirty: function(){
return !this.get('controller.content.isDirty')
}.property('controller.content.isDirty').cacheable()
(With the version of Ember I have, Ember.Binding.not does not exist. Maybe I need to update, but the docs don't show it either so perhaps it was actually removed.)
The problem is that there is no isEnabled property on Ember.Button. You need to bind to the disabled property.
One possibility is to create a custom Ember.Button which handles this for you, see http://jsfiddle.net/LabpW/.
Handlebars:
{{#view App.SaveModelButton modelBinding="model"}}Save{{/view}}
JavaScript:
App.SaveModelButton = Ember.Button.extend({
disabledBinding: Ember.Binding.not('model.isDirty')
});
The used Ember.Binding.not is just a shortcut for writing your own computed property, which would look like this:
App.SaveModelButton = Ember.Button.extend({
disabled: function() {
return !Ember.getPath(this, 'model.isDirty');
}.property('model.isDirty').cacheable()
});
I also refactored your code a bit:
You mixed create and extend: use create for instances and extend for classes. There is a good blog post about this
It's kind of a convention to use lowerCase for instances and UpperCase for classes, so it should be App.controller instead of App.Controller