Ember: Access ember data 'store' object from utility class - javascript

I have a utility class for validating usernames in my ember application and have it setup as specified in the ember-cli docs. I do client-side username validation in several places in my application (components and controllers) so I wanted to pull the validation logic out into a reusable method.
The file is at /app/utils/username-validator.js and I can successfully include the file in my app by importing it like so: import usernameValidator from 'my-app/utils/username-validator';
This works great so far and I've used the pattern for several utility classes. The problem I'm running into now is that I'd like the username-validator method to include a check to see if the username already exists.
As I am using Ember-Data I'd like to check the Ember-Data store. By default, the store appears to only be accessible in controllers and routes. How can I make it accessible in my utility class? All the injection examples I've seen deal with injecting the store into other first class Ember objects like components.
Is it possible to inject the store into a simple utility class as well?
Thank you!
I am using the following versions:
Ember-cli v0.2.6
ember.debug.js:4888 DEBUG: -------------------------------
ember.debug.js:4888 DEBUG: Ember : 1.12.0
ember.debug.js:4888 DEBUG: Ember Data : 1.0.0-beta.18
ember.debug.js:4888 DEBUG: jQuery : 1.11.3
ember.debug.js:4888 DEBUG: Ember Simple Auth : 0.8.0-beta.2
ember.debug.js:4888 DEBUG: -------------------------------
===== Updated with detailed solution based on answer from torazaburo ======
Creating a service works great. Here is how I did it using ember-cli (v0.2.6) and ember v1.12.0
Create your service inside of /app/services/<service-name>.js
The service blueprint will look like this (note the name of the service is based on the name of the file):
import Ember from "ember";
export default Ember.Service.extend({
myFunction: function(){
}
});
Create an initializer for your service in /app/initializers/<service-name>.js which is used to inject your service into the different top level Ember objects (such as routes, controllers, components etc). Note that the file name of the initializer should match the file name of your service.
The blueprint for the initializer will look like this:
export function initialize (container, app) {
// Your code here
}
export default {
name: '<service-name>',
initialize: initialize
};
To give a concrete example, lets say your service is called validator and contains a bunch of validation routines. You want to inject the validator into all controllers, and you also want to inject the Ember Data store into the validator itself. You can do it like this:
export function initialize (container, app) {
// Inject the Ember Data Store into our validator service
app.inject('service:validator', 'store', 'store:main');
// Inject the validator into all controllers and routes
app.inject('controller', 'validator', 'service:validator');
app.inject('route', 'validator', 'service:validator');
}
export default {
name: 'validator',
initialize: initialize
};

Make your utility into a "service", into which you can inject the store. Actually, it sounds like your utility should be a service anyway, even if it doesn't need the store. By making it a service, for instance, it becomes much easier to stub it out when writing tests. With a service, you need neither import anything nor do any global injections in initializers, you can simply say
export default Ember.Component.extend({
myService: Ember.inject.service(), // inject services/my-service.js
foo: function() {
this.get('myService').api1(...);
}
});

Related

how to access DOM elements in angular 4 service?

I am able to access DOM elements components like below
declare var jQuery: any;
declare var $: any;
//component stuff
$('.my_class').innerHeight();
I am trying to implement the same inside the service class, but dom elements and template is not accessible in the service class.
p.s: this is not duplicate of how to access them in components.
You can access DOM from an Angular service using the plain javascript document object, with some little additions to your service:
// We import not only "Injectable", but "Inject" too, from #angular/core
import { Injectable, Inject } from '#angular/core';
// We import DOCUMENT from #angular/common. Be careful, because the old import from '#angular/platform-browser' is deprecated.
import { DOCUMENT } from '#angular/common';
// Our standard service class in the usual way
#Injectable()
export class LoadingSpinnerService {
// In the constructor we inject a dependency to DOCUMENT, of type HTMLDocument
constructor(#Inject(DOCUMENT) private document: HTMLDocument) {
// We create a new div in the DOM, child of the body tag, <div id="LoadingSpinner"></div>
var NewDomElement = this.document.createElement("div");
NewDomElement.setAttribute("id", "LoadingSpinner");
document.body.appendChild(NewDomElement);
}
}
As you will probably know, you crete the service from the command line with something like:
ng g s loading-spinner
Don't forget to edit the app.module.ts to add the "import" to the service, and the item to the "providers" array in its "#NgModule" decorator:
import { LoadingSpinnerService } from './WHATEVER-DIRECTORY-YOU-CREATE-THE-SERVICE/loading-spinner.service';
(...)
providers: [LoadingSpinnerService,
(...)
About the topic of using angular services only for data, i don't agree. As you can read in the official architecture guide for services:
https://angular.io/guide/architecture-services
Service is a broad category encompassing any value, function, or feature that an app needs.
A component can delegate certain tasks to services, such as fetching data from the server, validating user input, or logging directly to the console.
The provided example just in this documentation is for a log data service.
Hope this helps someone.
You can't in services. You can do it javascript way like document.getElementById.
In components and directives You can use ViewChild from #angular/core
HTML:
<div class="my_class" #myElement></div>
TS:
import { ElementRef, ViewChild } from '#angular/core';
#ViewChild('myElement') myElement:ElementRef;
console.log(this.myElement.nativeElement.offsetHeight); // inside any function.
I guess you cannot directly access with CSS selectors by Angular way. Alternatively, you can just use plain old JavaScript
document.getElementsByClassName("my-class");
Note: You can only do this in components and directives not inside services
Why Services
Components shouldn't fetch or save data directly and they certainly shouldn't knowingly present fake data. They should focus on presenting data and delegate data access to a service.
Source: https://angular.io/tutorial/toh-pt4
In Simpler terms:
component, directive for presenting, manipulating and interacting with DOM
services are for data handling between your component and backend

How to fetch data from Mirage in Ember.js?

I need to return a list of users from Mirage. I need this list for rendering it on the front end. How do I do this?
To install mirage:
ember install ember-cli-mirage
Once this is done, you need to configure Mirage to send data. This can be done by updating the file mirage/config.js.
In the mirage/config.js file, its better you define a namespace so that Ember application's call does not conflict with the routes defined already.
Say you want Mirage to return a sample list of users. You can do something like this in the mirage/config.js file:
export default function() {
this.namespace = '/api';
this.get('/users', function() {
return {
// list of users
};
});
}
Now whenever the Ember application makes a get request to /api/users , a list of users is returned.
Only doing this isn't enough, you need to make your application to default making requests to the namespace /api.
This is done by creating an application adapter.
To create an application adapter:
ember generate adapter application
Then in app/adapters/application.js, add the namespace like this:
export default DS.JSONAPIAdapter.extend({
namespace: 'api'
});
Restarting the Ember server would include Mirage in your build.
Say you were making a request for all users in the model function of a route:
export default Ember.Route.extend({
model() {
return this.get('store').findAll('users');
}
})
Here, when you request for all the users, Ember data will fetch all users from /api/users.

Create base class for controller to extend/inherit in Ember application

I'm trying to create a Base Class for my controllers, so I can avoid duplication of code.
The problem here is that it is throwing me errors whenever I try to use it.
"Assertion Failed: You attempted to define a {{link-to "inventory"}} but did not pass the parameters required for generating its dynamic segments. Could not find module controllers/base-inventory imported from frontend/controllers/inventory"
To create my base controller I am using ember cli and this is what I did:
ember g controller base-inventory
Then
// base-inventory.js
const BaseInventory = Ember.Controller.extend({
//my code...
});
export default BaseInventory;
In the controller where I want to use this base class I did the following
import BaseInventory from 'controllers/base-inventory';
// also tried import { BaseInventory } from 'controllers/base-inventory';
// and export default new BaseInventory({});
export default BaseInventory.extend({
//more code here...
});
Any thoughts of what I am doing wrong?
I didn't plan to use mixins, because it doesn't seem the best option here at first. I am not really sure about the sharing content, which mixins provide. I don't think it would be a problem since I'm trying to inherit within controllers, but as I said I'm not sure about how it really works.
If it's not possible to do the way I'm trying to, I'll write a mixin.
Both files are in the same folder structure so import path should be like ./base-inventory
import BaseInventory from './base-inventory';

How to include external JavaScript libraries in Angular 2?

I am trying to include an external JS library in my Angular 2 app and trying to make all the methods in that JS file as a service in Angular 2 app.
For eg: lets say my JS file contains.
var hello = {
helloworld : function(){
console.log('helloworld');
},
gmorning : function(){
console.log('good morning');
}
}
So I am trying to use this JS file and reuse all the methods in this object and add it to a service, so that my service has public methods, which in turn calls this JS methods. I am trying to reuse the code, without reimplementing all the methods in my typescript based Angular 2 app. I am dependent on an external library, which I cant modify.
Please help, thank you in advance.
With ES6, you could export your variable:
export var hello = {
(...)
};
and import it like this into another module:
import {hello} from './hello-module';
assuming that the first module is located into the hello-module.js file and in the same folder than the second one. It's not necessary to have them in the same folder (you can do something like that: import {hello} from '../folder/hello-module';). What is important is that the folder is correctly handled by SystemJS (for example with the configuration in the packages block).
When using external libs which are loaded into the browser externally (e.g. by the index.html) you just need to say your services/component that it is defined via "declare" and then just use it. For example I recently used socket.io in my angular2 component:
import { Component, Input, Observable, AfterContentInit } from angular2/angular2';
import { Http } from 'angular2/http';
//needed to use socket.io! io is globally known by the browser!
declare var io:any;
#Component({
selector: 'my-weather-cmp',
template: `...`
})
export class WeatherComp implements AfterContentInit{
//the socket.io connection
public weather:any;
//the temperature stream as Observable
public temperature:Observable<number>;
//#Input() isn't set yet
constructor(public http: Http) {
const BASE_URL = 'ws://'+location.hostname+':'+location.port;
this.weather = io(BASE_URL+'/weather');
//log any messages from the message event of socket.io
this.weather.on('message', (data:any) =>{
console.log(data);
});
}
//#Input() is set now!
ngAfterContentInit():void {
//add Observable
this.temperature = Observable.fromEvent(this.weather, this.city);
}
}

dependency injection without singleton in ember-cli

Just converted my app to ember-cli, but I don't know how to use Ember.Application.register any more because register doesn't seem to be available when Application is started with extend rather than create.
import Ember from 'ember';
import App from 'myapp/app';
var AdminMyController = Ember.ObjectController.extend({
});
// THROWS ERROR HERE BECAUSE register isn't, uh...registered?
App.register('controller:adminMyController', AdminMyController, { singleton: false });
export default AdminMyController;
Previously, because App was a global, I could register this right in the same class.
Am I going to have to move all the register calls to an initializer so I can get access to the app instance?
I belive an initializer would do this for you. You'll need to create an initializers folder in your app directory (same level as controllers, templates, etc). This file should go there.
import Ember from 'ember';
var AdminMyController = Ember.ObjectController.extend({
...
});
export default {
name: 'adminMyController',
initialize: function (container, application) {
container.register('controller:adminMyController', AdminMyController, {singleton: false});
}
};

Categories

Resources