Ember DRY pattern for reusing "Ember.computed.alias" - javascript

I have a form that transitions through several views. Currently each controller.js file has a long list of these Ember.computed.alias. How can I break that out into one file and import it into each controller?
Currently in each controller.js
entityEmail: Ember.computed.alias('controllers.checkout.entityEmail'),
entityDOB: Ember.computed.alias('controllers.checkout.entityDOB'),
entityPhone: Ember.computed.alias('controllers.checkout.entityPhone'),
entityAddress1: Ember.computed.alias('controllers.checkout.entityAddress1'),
entityAddress2: Ember.computed.alias('controllers.checkout.entityAddress2'),
entityCity: Ember.computed.alias('controllers.checkout.entityCity'),
I would like to pull all that out into a file so I can simply import some 1 liner in each controller.js

This is a classic use-case for Ember.Mixin.
You can extract all these computed props into a single mixin and extend every controller (that needs to have these props) with it.
Add the following mixin to your app
// app/mixins/entity-form.js
import Ember from 'ember';
const { Mixin, inject, computed: { alias } } = Ember;
export default Mixin.create({
checkout: inject.controller(),
entityEmail: alias('checkout.entityEmail'),
entityDOB: alias('checkout.entityDOB'),
entityPhone: alias('checkout.entityPhone'),
entityAddress1: alias('checkout.entityAddress1'),
entityAddress2: alias('checkout.entityAddress2'),
entityCity: alias('checkout.entityCity')
});
And then use it in a controller
// app/controllers/example.js
import EntityFormMixin from 'yourAppName/mixins/entity-form';
const { Controller } = Ember;
export default Controller.extend(EntityFormMixin, {
// rest of controller's props and functions
});
Note: Ember.inject API is available since Ember 1.10.0. In case you are using an older version you need to replace the inject line with: needs: ['checkout'] and prefix the aliases with "controllers." like you did in your example.

Related

How are the imports implemented in different React libraries and how can I implement the same pattern in my own library?

For example, the recommended way of importing in React Bootstrap is to go this way:
import Button from 'react-bootstrap/Button' instead of import { Button } from 'react-bootstrap';
The reason is "Doing so pulls in only the specific components that you use, which can significantly reduce the amount of code you end up sending to the client."
source: https://react-bootstrap.github.io/getting-started/introduction/
Same for React MUI components:
import Button from '#mui/material/Button';
source: https://mui.com/material-ui/getting-started/usage/
I want to implement something similar in my React components library, to limit the usage of code in the bundle, but I don't know how they implement this specific pattern. I have looked at their code base, but I don't quite understand.
Basically it is all about modules and module files and their organization. You can have a lot of.. lets call them folders, "compoments/*" for example. "components/button", "components/alert", "component/badge", and other things. All of them will have some index.js or .ts file that will export or declare and export all the functionality that needed in order to make this component work, 'react-bootstrap/Button' for example. Ideally all those subfolders or submodules are independend from each other, no references between them but probably each one will have 1 reference to 1 common/shared submodule like "components/common" which will contain some constants, for example, and no references to other files. At the top level of them you will have another index.js or .ts file that is referencing all of those components, so "components/index.js" will import and reexport all the nested components index files. So in order to import a Button, for example, you can either import "components/index.js" file with all the other imports this file is using, either only 1 single "components/button/index.js" file which is obviously much more easy to fetch. Just imagine a tree data structure, you import root of the tree (root index.js) - you get all the tree nodes. You import one specific Node (components/button/index.js) of the tree - just load all the childs (imports) of that node.
Sorry for a long read but asuming you mentioned webpack - there is a technique called tree-shaking which will cut off all the unused things.
Info about modules: https://www.w3schools.com/js/js_modules.asp
Info about Tree-Shaking: https://webpack.js.org/guides/tree-shaking/
It might not be as complicated as you think. Let's say you write the following library:
// your-library.js
const A = 22
const B = 33
export function getA () { return A }
export function getB () { return B }
export function APlusB () { return A + B }
// a lot of other stuff here
If some consumer of your library wants to make use of the APlusB function, they must do the following:
// their-website.js
import { APlusB } from 'your-library'
const C = APlusB()
However, depending on how the code is bundled, they may or may not wind up with the entire your-library file in their web bundle. Modern bundling tools like Webpack may provide tree shaking to eliminate dead code, but this should be considered an additional optimization that the API consumer can opt into rather than a core behavior of the import spec.
To make your library more flexible, you can split up independent functions or chunks of functionality into their own files while still providing a full bundle for users who prefer that option. For example:
// your-library/constants.js
export const A = 22
export const B = 33
// your-library/aplusb.js
import { A, B } from 'constants'
export default function APlusB () { return A + B }
// your-library/index.js
// instead of declaring everything in one file, export it from each module
export * from 'constants'
export { default as APlusB } from 'aplusb'
// more exports here
For distribution purposes you can package your library like so:
your-library
|__aplusb.js
|__constants.js
|__index.js
You mentioned react-bootstrap and you can see this exact pattern in their file structure:
https://github.com/react-bootstrap/react-bootstrap/tree/master/src
and you can see they aggregate and re-export modules in their index file here:
https://github.com/react-bootstrap/react-bootstrap/blob/master/src/index.tsx
Essentially, what you are asking is:
"How to export react components"
OR
"How are react components exported to be able to use it in a different react project ?"
Now coming to your actual question:
import Button from 'react-bootstrap/Button' instead of import { Button } from 'react-bootstrap';
The reason is 'Button' component is the default export of that file react-bootstrap/Button.tsx. So there is no need for destructuring a specific component.
If you export multiple components/ functions out of a file, only 1 of them can be a default export.
If you have only 1 export in a file you can make it the default export.
Consider the file project/elements.js
export default function Button(){
// Implementation of custom button component
}
export function Link(){
// Implementation of custom Link component
}
function Image(){
// Implementation of custom Image component
}
Notice that the Button component has 'default' as a keyword and the Link component doesn't.
The Image component can't even be imported and can only be used by other functions/components in the same file.
Now in project/index.js
import 'Button', {Link} from './elements.js'
As Button component is the default export its possible to import without destructuring and as Link component is a regular export, I have to destructure it for importing.

Using a Model Instance in Ember Component Integration Tests

I am trying to do an integration test for an Ember component that requires a model as input. I have mock data, but I cannot pass it into the component directly because the model includes computed properties and other customizations that EmberData normally handles under the hood. However, I cannot figure out how to inject the store into the test to convert my mock data into an Ember model. I've tried a few different approaches, none of which work:
Ember.inject.service('store');
this.get('store')
var store = this.container.lookup('service:store');
Ember.getOwner(this).lookup("service:store");
None of these actually work. I'm using Ember v2.9.1. The code for my integration test is below.
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('host-pod', 'Integration | Component | host-pod', {
integration: true
});
test('it renders', function(assert) {
... code to get store ...
this.set('mockModel', store.normalize('model', mockModel));
this.render(hbs`{{component model=mockModel show='all' mode='grouped'}}`);
assert.equal(this.$().length, 1);
});
var mockModel = { ... }

Invoke JQuery method from Angular 2 component

I'm working on creating an Angular 2 front end on an existing project that previously just used JQuery. Is it possible to invoke a JQuery method inside of an Angular 2 component, when that JQuery function exists in a separate file? I'm writing my components in TypeScript, in case that is important to know.
For example, I have a JavaScript file called EditCheckBoxes.js with a method called editCheckBoxes(). In order for editCheckBoxes() to work, I need it to be invoked after a certain component is initiated. My attempted solution was this:
ngOnInit(): void {
editCheckBoxes();
}
This code gives me the following error:
Cannot find name 'editCheckBoxes'.
Is there any way I can get this to work?
Also, I added declare var $: any; to my component file, so I am able to use JQuery within that component, but I'd rather not copy entire files into my components in order to use them.
EDIT:
The folder structure looks like this:
Plan
app
selling
My Angular 2 component
Scripts
EditCheckBoxes.js
In my component, the import statment looks like this: import { editCheckboxes } from '../../Scripts/EditCheckboxes';
You need to import the editCheckboxes method in your typescript file for it to be available.
To do that you first need to export the editCheckboxes function from EditCheckBoxes.js
export function editCheckboxes() { ... }
Next you should just import that function inside your component
import { editCheckBoxes } from './EditCheckBoxes';
Now it can be called in your component: editCheckBoxes();
In order to import function from js file you should at first export it like so:
// EditCheckBoxes.js
module.exports = function editCheckBoxes () { ... };
then add a d.ts file in same directory as your js-file with definition of your module that would be used by Typescript
// EditCheckBoxes.d.ts
declare function editCheckBoxes (): void;
export = editCheckBoxes;
then in your Typescript file you would specify your definition file, import your function and use it like so:
/// <reference path="../../Scripts/EditCheckBoxes.d.ts" />
import editCheckBoxes = require('../../Scripts/EditCheckBoxes');
ngOnInit (): void {
editCheckBoxes();
}

Using nested classes to simulate namespaces in ES6

I have a file, Services.js which I am trying to load all of my individual services in. These are exposed as singletons.
Services.js
var Services = { };
export default Services;
I then want Sample Service nested under Services, so I could invoke for example Services.Sample.Operation()
`SampleService.js'
import Services from './Services';
Services.Sample = {
Operation: function() {
alert('operation!')
}
};
export default Services.Sample;
Then, I try and import:
import Services from './services/Services';
import SampleService from './services/SampleService';
alert(Services); // yields '[object object]'
alert(SampleService); // yields '[object object]'
alert(Services.Sample); // yields 'undefined' <--- This is the one I actually want to use
How can I get it so I can refer to Services.Sample rather tan SampleService. How can I make SampleService become nested under Services?
Your way doesn't work because you're importing Services.js in SampleService.js, but the Services variable is not the 'original' Services variable from Services.js.
What I'd do is something like this:
SampleService.js:
SampleService = {
Operation: function() {
alert('operation!')
}
};
export default SampleService;
Services.js:
import SampleService from './SampleService';
var Services = { };
Services.Sample = SampleService;
export default Services;
and then:
import Services from './services/Services';
alert(Services);
alert(Services.Sample);
export default Services;
This does also seem to make more sense to me regarding basic (in)dependencies and consistency of your modules (define what Services can do in Services.js not SampleService.js, SampleService could be independent of Services, the module loading Services.js should not depend on SampleService.js as well as that might change later, ...).

How do I access the Ember global 'App' variable in an Ember CLI application?

I am creating an Ember application using the Ember CLI. I have a view which invokes a component that I created. I am trying to access the global App variable to create my component and insert it into my layout.
The error: Uncaught ReferenceError: App is not defined
How do I fix this?
app.js
import Ember from 'ember';
import Resolver from 'ember/resolver';
import loadInitializers from 'ember/load-initializers';
Ember.MODEL_FACTORY_INJECTIONS = true;
var App = Ember.Application.extend({
modulePrefix: 'client-web', // TODO: loaded via config
Resolver: Resolver
});
loadInitializers(App, 'client-web');
export default App;
item-table.js (This is a view)
import Ember from 'ember';
export default Ember.View.extend({
templateName: 'item-table',
didInsertElement: function() {
// All my other code here
App.FreestyleChartComponent.create().appendTo($('#wp-chart td')); // This throws an error.
}
});
app/components/freestyle-chart.js
import Ember from 'ember';
export default Ember.Component.extend({
templateName: 'components/freestyle-chart',
didInsertElement: function() {
console.log('Inserted the component.');
}
});
I can think of two ways. The first is to put the App in the global scope manually.
var App = window.App = Ember.Application.extend();
The second is to import the App into your view:
import App from './path/to/app/file';
The latter is only possible if Ember CLI supports circular references. The official ES6 spec supports them but many transpilers don't (mine doesn't).
However, I don't think this is your root concern. For the most part, you shouldn't be accessing global variables in Ember CLI. Instead of placing the FreestyleChartComponent in the App namespace, why not just put it in a module and import it like any other module? Global variables are unavoidable (I experienced that today), but you should try to minimize them.
I do agree that you should not be accessing your app via the global namespace, however ember-cli actually does actually make you app a global with the name of your app being the name of the variable.
If you open /app/index.html in your ember-cli project you should see a script tag towards the bottom that looks like...
<script>
window.YourAppName = require('your-app-name/app')['default'].create(YourAppNameENV.APP);
</script>
in the above example YourAppName would be your app available as a global
Import the component that you want:
import FreestyleChartComponent from 'app_name/app/components/freestyle-chart';
There is no straight forward way to do this since Ember CLI wants you to use the public API and its given components rather than accessing the App instance directly.
Most solutions consist of making the App instance global, whereby this one does not. I did not test this, but I think this should work:
appname/utils/application.js
export default {
instance: null
};
appname/instance-initializers/application.js
import application from 'appname/utils/application';
export default {
name: 'app-initializer',
initialize: function (application) {
application.instance = application;
}
};
Now
import application from 'appname/utils/application';
and use application.instance in any file where you need the application instance.
Justed tested with Ember 3.7.
import App from 'app-name/app';
app-name has to be replace with the name of your app (I guess the one in package.json).

Categories