I am attempting to follow Alisdar's new Itegration tests instead of doing unit testing on my ember component.
I really like the new approach, but I have a problem when testing components which calls another component in its' view.
alpha component:
App.TestAlphaComponent = Ember.Component.extend({
listWords: []
});
alpha component view uses beta component:
{{#each listNumbers as num}}
{{test-beta word=num}}
{{/each}}
beta component:
App.TestBetaComponent = Ember.Component.extend({
word: 'hello world'
});
beta component view:
<h1>{{ word }} </h2>
Mocha Chai Integration test for TestAlphaComponent
import Ember from 'ember';
import { expect } from 'chai';
import {
describeComponent,
it
} from 'ember-mocha';
import hbs from 'htmlbars-inline-precompile';
import tHelper from "ember-i18n/helper";
import testBeta from "app/components/test-beta";
var foo;
describeComponent(
'test-alpha',
'Integration: testAlphaComponent',
{
integration: true
},
function() {
beforeEach(function () {
this.container.lookup('service:i18n').set('locale', 'en');
this.registry.register('helper:t', tHelper);
this.registry.register(
'component:test-beta',
testBeta
);
Ember.run(this, 'set','foo', ['foo','bar','baz']);
this.render(hbs`{{test-alpha listWords=foo}}`);
});
it('prints foo bar baz in h1s', function() {
expect($('h1').to.have.length(3);
});
});
);
my test fails. testBeta is never called, nor does it complain about missing components. What is the correct way to inject testBetaComponent into testAlpha's integration test environment?
Components need to have a dash in their names. Once you add a dash, subcomponents will be added automatically. See the note:
http://guides.emberjs.com/v1.13.0/components/defining-a-component/
Related
I currently writing a financial application using Vue.js and Vuetify. I have a few component files and javascript files like
Dashboard.vue
Cashflow.vue
NetWorth.vue
stores.js <- Vue Vuex
I have some functions which I need to use across all the Vue.js and javascript files. Would it be possible for me to perhaps write a function library which can be used across all
the component and js files.
function moneyFormat(num)
function IRRCalc(Cashflow)
function TimeValueMoneyCalc(I,N,PV,FV,PMT)
function PerpetualAnnuityCalc(I,PV)
function CarLoanInstallment(V,N)
function HouseLoanInstallment(V,N)
I know in C it is very simple just #include<financial.h> was wondering is there something similar in javascript.
Thanks.
There are 3 ways to do this:
1/You can create a helper.js file and import it to .vue files
// helper.js
export default {
function moneyFormat(num) { // some logic}
}
// Dashboard.vue
<script>
import helper from "helper.js" //the path may change depends on where you put the js file
methods: {
useHelper(value) {
helper.moneyFormat(value)
}
}
</script>
2/Another way is bind the function to Vue prototype
in main.js
Vue.prototype.$moneyFormat= function moneyFormat(num) {}
then in Dashboard.vue just call this.$moneyFormat(num). No need to import anything
3/ Use mixins. You can search online on how to use this https://v2.vuejs.org/v2/guide/mixins.html
You can create a single JS file that holds all the helper/util methods, and then export them individually:
export function moneyFormat(num) { ... }
export function IRRCalc(Cashflow) { ... }
export function TimeValueMoneyCalc(I,N,PV,FV,PMT) { ... }
export function PerpetualAnnuityCalc(I,PV) { ... }
export function CarLoanInstallment(V,N) { ... }
export function HouseLoanInstallment(V,N) { ... }
Then, you can simply import individual methods as of when needed, i.e.:
import { CarLoanInstallment, HouseLoanInstallment } from '/path/to/helper/file';
This can be quite usefuly for tree-shaking when you're bundling with webpack, for example, so that you don't bundle unnecessary functions that are never used in your project.
You can use Mixin
In your main.js, add Vue.mixin:
import Vue from "vue";
import App from "./App.vue";
Vue.mixin({
methods: {
helloWorld() {
alert("Hello world");
}
}
});
new Vue({
render: h => h(App)
}).$mount("#app");
and then you can call helloWorld() method from your component script with this.helloWorld() or just helloWorld() from the template.
You also can use filters if the method is to apply common text formatting
In your main.js, add Vue.filter:
import Vue from "vue";
import App from "./App.vue";
Vue.filter("capitalize", function(value) {
if (!value) return "";
value = value.toString();
return value.charAt(0).toUpperCase() + value.slice(1);
});
new Vue({
render: h => h(App)
}).$mount("#app");
and then you can do {{ "some text" | capitalize }} to apply capitalize filter on "some text"
Example here: https://codesandbox.io/s/heuristic-dirac-esb45?file=/src/main.js:0-226
I am new to Ember and I am currently working on 3.8 version of Ember. I just want an array to be initialize in component file and then to use the array in template file. Thanks in advance.
#mistahenry suggesting initialize array in the init function like
init() {
this._super(...arguments);
this.set('foo', [{
id: 0,
name: "baz"
}, {
id: 1,
name: "bazzz"
}]);
}
otherwise eslint throwing error with latest Ember 3.8.
on js side you can initialize like
import { computed } from '#ember/object';
export default Component.extend({
foo: computed(function() {
return [{id:0,name:"baz"},{id:1,name:"bazzz"}]
}),
...
});
on template side just call your variable
{{#each foo as |item|}}
{{item.name}}
{{/each}}
The best way to initialize an array on an Ember Object (which a component is a subclass of) is to use the Ember A helper. This is a function that returns a new Mutable Array (which provides a lot of helpful methods on top of a regular [].
New Syntax:
import Component from '#ember/component';
import { A } from '#ember/array';
export default class MyComponent extends Component {
myArray = A()
}
Older Syntax
export default Component.extend({
myArray: A()
})
Why is this even an issue? Because when you define a component, you're defining a factory for that component. When it comes time to use it, ember returns a new instance of that component class. Arrays in javascript are mutable and are stored by reference, so when you update that array, then create a new component instance, the new value of the array is also updated.
See this article for more information: https://dockyard.com/blog/2015/09/18/ember-best-practices-avoid-leaking-state-into-factories
You can create a new Ember.NativeArray by using an init hook in you component/controller.
import Component from '#ember/component';
import { A } from '#ember/array';
export default Component.extend({
tagName: 'ul',
classNames: ['pagination'],
init() {
this._super(...arguments);
if (!this.get('content')) {
this.set('content', A());
this.set('otherContent', A([1,2,3]));
}
}
});
I'm trying to create simple ToDo app using Ractive.js and Redux, but I ran into a problem with rendering more than one component on my page.
Javascript code:
import Ractive from 'ractive';
import template from './Home.html';
import './Home.less';
import { AddToDoForm, ToDoList } from '../';
export default Ractive.extend({
template,
data: {
message: 'This is the home page.',
},
components: {
AddToDoForm,
ToDoList
}
});
HTML of the component:
<AddToDoForm store={{store}} />
<ToDoList store={{store}} />
But only the first component is rendered. The store parameter I'm passing is the Redux store, but it doesn't work even without it.
I would add to verify defaults as a
...
components:{
AddToDoForm: AddToDoForm,
ToDoList: ToDoList
}
...
syntax examples (answer/31096635)
How can I be able to call filter of parent using single file component. Below are my code.
app.js
import computed from '../vue/mixins/computed.js';
import filters from '../vue/mixins/filters.js';
import methods from '../vue/mixins/methods.js';
const app = new Vue({
el: '#app',
mixins:[
computed,
filters,
methods
],
mounted: function() {
}
});
home.vue
<template>
<div class="home-content">
<h3>{{home | uppercase}}</h3>
</div>
</template>
<script type="text/javascript">
export default {
data: function() {
return {
home: 'home'
}
},
mounted: function() {
this.$parent.$options.methods.makeConsole();
}
}
</script>
It's giving me this warning in console "Failed to resolve filter: uppercase"
You should just make the filter global available before starting the root instance with
Vue.filter('uppercase', uppercase);
Where uppercase can be a simple function like
function uppercase(str)
return str.uppercase();
}
That would be the most simple and reliable way to use the filter on all vue components;
If you import your filters to your parent via mixins why don't you use that mixin in the child?
Please do not use the this.$parent-method as it makes your child component statical dependend of that parent.
To use the $parent approach you may need to declare the filter function from the parent as a filter in the child:
filters:{
uppercase: this.$parent.$options.filters.uppercase
}
There is no point. Just include your mixin in the child as well. A component should ideally be autonomous, and not aware of where it is in the hierarchy of components (at least not the ones above or on the same level.
I'm trying to test a link-to href property in an ember component. with ember 2.0
but when I render the component with renter hbs it renders this:
<div id=\"23\" class=\"ember-view\">
<p><!----></p>
<div class=\"participantes\">
<a id=\"ember282\" class=\"ember-view\">
<span>rnt-ayl-bld-js-jvr-frd-edw</span>
</a>
</div>
and the href property is not rendered,
I read that is something related to router but I'm not sure how to include the router in the test I tried something like:
moduleForComponent('conversation-item', 'Integration | Component | conversation item', {
integration: true,
setup(){
const router = this.lookup('router:main');
router.startRouting(true);
}
});
but the lookup function is not present
TypeError: 'undefined' is not a function (evaluating
'container.lookup('router:main')')
You might want to take a look at this question Ember Component Integration Tests: `link-to` href empty
Although it is 1.13 it might help you. Unfortunately I'm using ember-mocha and am still having problems.
Essentially, you're really close, you just need to use the container to look up the router.
// tests/helpers/setup-router.js
export default function({ container }) {
const router = container.lookup('router:main');
router.startRouting(true);
}
// tests/integration/components/my-component-test.js
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import setupRouter from '../../helpers/setup-router';
moduleForComponent('my-component', 'Integration | Component | my component', {
integration: true,
setup() {
setupRouter(this);
}
});